-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 
Author Message
 Post subject: How to map entities with composite keys
PostPosted: Thu Mar 17, 2005 2:08 pm 
Newbie

Joined: Thu Mar 17, 2005 1:55 pm
Posts: 15
... where part of the primary key is also a foreign key. Typical "hierarchical structure" from a legacy database. Take, for example, these two tables:

Code:
CREATE TABLE tb_domain (
    cd_domain varchar(10),
    ds_domain varchar(255),
    constraint pk_domain primary key (cd_domain)
);

CREATE TABLE tb_application (
    cd_domain varchar(10),
    cd_application varchar(10),
    ds_application varchar(255),
    constraint pk_application primary key (cd_domain, cd_application),
    constraint fk_application_domain foreign key (cd_domain) references tb_domain (cd_domain)
);


And the following classes (method implementations omitted):

Code:
public class Domain {
    public String getCode();
    public void setCode(String code);
    public String getDescription();
    public void setDescription(String description);
    public Collection getApplications();
    public void setApplications(Collection applications);
}

public class Application {
    public String getCode();
    public void setCode(String code);
    public String getDescription();
    public void setDescription(String description);
    public Domain getDomain();
    public void setDomain(Domain domain);
}


Unfortunately I don't have any control over the table structures NOR the classes. Is there any way to map these classes? Domain is straightforward, but Application is giving me headaches. Any suggestions/pointers?

Again, remember I can't change the tables or classes. This is a legacy application where the data access backend is being ported from plain JDBC to Hibernate.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 17, 2005 2:16 pm 
Newbie

Joined: Thu Mar 17, 2005 1:55 pm
Posts: 15
Oh, the map needs to be done for Hibernate 2.x, by the way. We're using Spring to abstract all data access and we can't move to Hibernate 3 before Spring 1.2 is out (and adopted by our team, and that will take a while).

That being said, if this can't be done in HB8 2.x, but can be done on 3.x, I would love to know how :-)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 17, 2005 3:32 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
This looks to me like a straightforward parent-child mapping, straight out of http://www.hibernate.org/hib_docs/reference/en/html/example-parentchild.html. Application has a many-to-one association to Domain, and Domain has a set association to Application. Domain is probably the inverse end.

What about this example isn't like that?

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 17, 2005 3:43 pm 
Newbie

Joined: Thu Mar 17, 2005 1:55 pm
Posts: 15
RobJellinghaus wrote:
What about this example isn't like that?


It's about the composite keys. How do I map the key for the Application class? Notice that the key is composed from the "code" property *and* the code from the containing Domain:

Code:
<class name="Application">
  <composite-id>
    <key-property name="code"/>
    <key-property name="???domain.code???"/>
  </composite-id>
  ...
</class>


And oh, the example scheme above don't stop there. There's an "instance" entity below "application", where the key for "instance" is composed of the *three* keys:

Code:
CREATE TABLE tb_instance (
    cd_domain varchar(10),
    cd_application varchar(10),
    cd_instance varchar(10),
    ds_instance varchar(255)
    constraint pk_instance primary key (cd_domain, cd_application, cd_instance),
    constraint fk_instance_app foreign key (cd_domain, cd_application) references tb_application (cd_domain, cd_application)
);

public class Instance {
    public String getCode();
    public void setCode(String code);
    public String getDescription();
    public void setDescription(String description);
    public Application getApplication();
    public void setApplication(Application application);
}


... and it doesn't stop there either. There's a fourth entity below instance, but I'll stop the madness for now.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 17, 2005 3:52 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
Ah, well, why didn't you say so? ;-)

One huge confusion you seem to have is that you *can* set the code and description of (say) an Instance. You can't. (At least, I don't *think* you can.) The primary key of an object is its persistent identity. If you change that, you are working with a different object.

I think that you do indeed need to map these as composite keys, but rather than having key-properties, they have many-to-one references to the parent table. What I don't know is the exact implication of having nested composite keys... that's something that maybe someone else will have to help with.

But I do think it's pretty clear that your API for instances will not have "setApplication", but will instead have something like "makeNewInstance(Application, instanceCode, instanceDescription)". setApplication makes no more sense than setId for a database-generated identifier. (I honestly have no idea what Hibernate does if you try to reset the Java identifier for an object -- I've *never* done it in our system. What would you want it to do in this case? Delete the current Instance row and insert a new one?)

Cheers!
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 17, 2005 3:59 pm 
Newbie

Joined: Thu Mar 17, 2005 1:55 pm
Posts: 15
RobJellinghaus wrote:
But I do think it's pretty clear that your API for instances will not have "setApplication", but will instead have something like "makeNewInstance(Application, instanceCode, instanceDescription)". setApplication makes no more sense than setId for a database-generated identifier.


Yes, perfectly. The setApplication() method on Instance (and the setDomain() method on Application) aren't supposed to be called after the object is fully instantiated. The same reasoning applies to the setCode() method on all three classes. These methods are only called from the database access code during the construction of the object.

Anyways... I was browsing the hibernate mapping DTD and came across <key-many-to-one>, which seems *exactly* what I need. Now if there was more examples on how to use this... (googling didn't came up with anything besides code from the Hibernate test cases).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 17, 2005 4:08 pm 
Newbie

Joined: Thu Mar 17, 2005 1:55 pm
Posts: 15
RobJellinghaus wrote:
(I honestly have no idea what Hibernate does if you try to reset the Java identifier for an object -- I've *never* done it in our system. What would you want it to do in this case? Delete the current Instance row and insert a new one?)


(Duh, forgot to quote and reply to this part). Well, what you asked above is the whole problem about assigned identifiers. What you should do if the user changes the identifier? The current web interface for the classes described above behaves like this:

1) Believes what the user said, so if he's asking to create a new domain with the code MYDOMAIN, issue an INSERT database. If we get a primary key violation, there's already a domain with such code. Inform this to the user and ask him to pick another code.

2) If the user is asking to update the domain MYDOMAIN, issue an UPDATE for it. If no rows are affected, the domain don't actually exists. Inform this to the user, and asks if he wants to create a new domain.

3) If the user is asking to delete the domain MYDOMAIN, issue a DELETE for it. If no rows are affected, inform this to the user and say you're sorry.

Please note that on (2) and (3) the user don't actually type the domain code -- it's usually selected from a list. So, if the user asked to update or delete a non-existent domain it's because another user removed it while the current user was looking at the list (and that's exactly what the current interface tells to the user when this happens).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 17, 2005 4:11 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
pazu wrote:
RobJellinghaus wrote:
But I do think it's pretty clear that your API for instances will not have "setApplication", but will instead have something like "makeNewInstance(Application, instanceCode, instanceDescription)". setApplication makes no more sense than setId for a database-generated identifier.


Yes, perfectly. The setApplication() method on Instance (and the setDomain() method on Application) aren't supposed to be called after the object is fully instantiated. The same reasoning applies to the setCode() method on all three classes. These methods are only called from the database access code during the construction of the object.


Well, then why aren't they constructor parameters rather than public methods? Or at least, why aren't they package scope? Or private, accessed through reflection in the instantiation code?

If you want them to only be called during construction, making them public is about the least safe thing you could do! (We try to practice defensive programming here, since making an open API will definitely lead to people doing the wrong thing because they don't know better....)

http://forum.hibernate.org/viewtopic.php?t=933956 confirms that I was right: keys aren't changeable in persisted objects.

Cheers!
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 17, 2005 4:13 pm 
Newbie

Joined: Thu Mar 17, 2005 1:55 pm
Posts: 15
RobJellinghaus wrote:
Well, then why aren't they constructor parameters rather than public methods? Or at least, why aren't they package scope? Or private, accessed through reflection in the instantiation code?


Legacy code! I would never expose these methods if these classes were written by myself. But they weren't... and someone found easier to make them public.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 17, 2005 4:53 pm 
Newbie

Joined: Thu Mar 17, 2005 1:55 pm
Posts: 15
And oh, about how I expect Hibernate to behave on the above described situations for the Domain class: First, assuming that I find a way to map this into hibernate, I would expect Hibernate to throw exceptions in the following situations:

1) Trying to save an object with a duplicate (existing) assigned key.
2) Trying to update a non-existent object .
3) Trying to delete a non-existent object.

Hopefully, the exception will be some meaningful subclass of HibernateException. Not sure which one, however. Browsing the hierarchy for HibernateException, I couldn't find a subclass suited for the above situations (I was expecting something like DuplicateKeyException and ObjectNotFoundException).


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 10 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.