-->
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.  [ 14 posts ] 
Author Message
 Post subject: Confused about compsite-id vs (with?) natural-id
PostPosted: Tue Nov 01, 2005 10:52 pm 
Beginner
Beginner

Joined: Sun Sep 25, 2005 11:57 am
Posts: 29
I obviously don't have it right ... about how to get what I want from mapping.

I have an existing (pre-hibernate) database. The previous database facility allowed me to use two fields as a primary key for these records. I want to do that with hibernate. I am not getting the results I desire from the way I have mapped this class. My mapping file follows:

<?xml version='1.0'?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

<class name="suncertify.client.ServiceProvider" table="ServiceProvider">

<!-- We use two fields as a composite key. -->
<composite-id>
<key-property name="name" length="32"/>
<key-property name="location" length="64"/>
</composite-id>
<!-- The name and location are, collectively, the unique business key
for this table, so we declare them a natural key. -->
<natural-id>
<property name="name" length="32"/>
<property name="location" length="64"/>
</natural-id>
<property name="specialities" length="64"/>
<property name="size" length="6"/>
<property name="rate" length="8"/>
<property name="owner" length="8"/>

</class>

</hibernate-mapping>

The mapping process, at startup, barfs all over me ... so I clearly have something wrong.

I want to enforce that no two records may have the same values for name/location ... requires the database to cause an exception if I try to insert a record with the same key values (the key values in the database must be unique). These fields in the objects cannot be null, will have some values in them at save() time (enforced by the code). Once established, these values may not be changed (not permitted by the code); but I do need to update changes to other fields in the record representing this object. These objects have hashcode() and equals() methods and are serializeable.

Help ... please.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 01, 2005 11:51 pm 
Expert
Expert

Joined: Mon Jul 04, 2005 5:19 pm
Posts: 720
I don't think natural-id is necessary. If you are trying to make two properties of ServiceProvider make up the key, you want to move both to a seperate class (like a MyCompKey), and make MyCompKey a property of ServiceProvider . then specify MyCompKey in the class attribute of the composite-id element. When you say "objects have hashcode() and equals() methods ", do you mean ServiceProvider, or MyCompKey ? MyCompKey has to do this. also, make sure MyCompKey is serializable.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 02, 2005 9:29 am 
Beginner
Beginner

Joined: Sun Sep 25, 2005 11:57 am
Posts: 29
Dennis,

You are always so good about responding ... and so capable. I surely do appreciate it.

However, in this case you seem a little tentative in your suggestion. I once used only composit-id without natural-id, but I wasn't getting what I wanted then either. I wasn't assured of having a unique key enforcement by the database, in that case. And, I don't want to muck up the primary object by creating a separate class just to house the key elements. Surely there is a way I can get done what I want to achieve.

I am going to research this a little more today.

I will come back to you later.

Thanks, again, for sharing your good skills in support of us struggling newbies-to-hibernate!


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 02, 2005 4:35 pm 
Beginner
Beginner

Joined: Sun Sep 25, 2005 11:57 am
Posts: 29
Well, after studying this again, I can think of four possible approaches to solving this problem, none of which seem to be very good solutions:

1. Use composite-id without any associated component id class.
If I use this approach, will this establish the key as unique within the table, and enforced by the database? And I don't fully understand the caveat in the manual in section 6.1.5, "Unfortunately, this approach to composite identifiers means that a persistent object is its own identifier. There is no convenient 'handle' other than the object itself. You must instantiate an instance of the persistent class itself and populate its identifier properties before you can load() the persistent state associated with a composite key." What does that mean to me in terms of how I must code to use this approach?

2. Use composite-id with a NameLocKey component class which gets constructed upon construction of every ServiceProvider object and becomes an integral part of that ServiceProvider object. This seems messy and I can't figure out what this messy approach buys me.

3. Use natural-id. I can't sort out how to do this properly, but it seems to me - logically - to be a good alternative, if I only fully understood how to implement this solution and its consequences. Also, I don't understand the caveat in the manual at 6.1.12, "This mapping is not intended for use with entities with natural primary keys." My goodness, if not under those circumstance, then for what use is it? And, if not for that, then what _is_ the right solution for that circumstance?

4. Use a single id which is set as the concatenation of Name+Location. This, I think, would work and be a simple solution. However, this approach seems inefficient to duplicate the Name and Location fileds to establish a single key.

Sooo, I don't yet have a solution I feel I can implement and am still studying. I am purchasing the book "Hibernate in Action", hoping that may help me get a better understanding ... and, one of my troops will be attending a Hibernate 2-day class in Dallas next week. Maybe he will be able to get me some answers, if I don't get this solved by then.

Thanks for any further comments.


Top
 Profile  
 
 Post subject: Any conclusions?
PostPosted: Wed Nov 02, 2005 5:58 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Did your research or the seminar help in any way? I'm starting to look into a very similar problem, so I'm interested to know if you got any further.

I presume that you discounted option 4 above. It can never work reliably, as there is always the possibility that you have something like obj1.name = "Name", obj1.location = "Location", and obj2.name = "Nam", obj2.location = "eLocation". Unless you used a separator character that is explicitly excluded from the text fields both in your code and (by trigger) from your database. Plus, it's definitely a horrible solution, even if you could get it to work somehow.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 02, 2005 6:31 pm 
Beginner
Beginner

Joined: Sun Sep 25, 2005 11:57 am
Posts: 29
My research into this only allowed me to identify and articulate these four approaches and the problems and/or confusion I have about them. The seminar has not yet occurred; that will be 10th and 11th next week.

Thanks for identifying the possibility of possible errors due to concatenation of the name and location Strings. I think I can solve that by puting a vertical bar as a separator. But it would be an ugly solution.

You say you are approaching a similar problem. I have to believe this is a common problem with a simple solution, just sitting out there somewhere waiting for us to find it. Maybe someone will come along to point us the so simple solution (Silly us, huh?)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 02, 2005 6:55 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Well you map the same columns twice in the mapping file. You don't go into specifics, so I can only guess here, but I assume you get errors to that effect.

The <natural-id/> here is completely useless. <natural-id/> is really only useful in two scenarios:
1) When using Hibernate's schema generation capabilities to ensure appropriate constraints. But you state this is a pre-existing schema...
2) when using select-style generators. Hibernate can them use those <natural-id/> columns to uniquely identifiy rows to see the value generated for the PK column(s).

Basically, <natural-id/> is an optional mapping element. Either <id/> or <composite-id/> is required.

Again, you are being sort-of vague so it is difficult to help you. You say that you tried mapping this without the <natural-id/> stuff but ran into problems with that. Well what were those problems? That is the correct way to do it.

Quote:
And I don't fully understand the caveat in the manual in section 6.1.5, "Unfortunately, this approach to composite identifiers means that a persistent object is its own identifier. There is no convenient 'handle' other than the object itself. You must instantiate an instance of the persistent class itself and populate its identifier properties before you can load() the persistent state associated with a composite key." What does that mean to me in terms of how I must code to use this approach?

Well, without a seperate "PK class", the individual properties making up the PK are mapped to the entity class itself. In your case, that means ServiceProvider would have the properties for name and location. Thus, think about what it take to load a ServiceProvider instance in that case. Basically, the code would look like:
Code:
ServiceProvider sp = new ServiceProvider();
sp.setName( "someName" );
sp.setLocation( "someLocation" );
sp = ( ServiceProvider ) session.load( ServiceProvider.class, sp );

Ugly, right? Much nicer mapping this using a seperate class encapsulating the PK fields. Something like:
Code:
ServiceProviderPK pk = new ServiceProviderPK( "someName", "someLocation" );
ServiceProvider sp = ( ServiceProvider ) session.load( ServiceProvider.class, pk );


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 02, 2005 11:26 pm 
Beginner
Beginner

Joined: Sun Sep 25, 2005 11:57 am
Posts: 29
Steve. Thank you! Excellent! Now we're getting somewhere, I think.

If I understand, using composite-id is the right way to map two attributes of a class as a composite (combined) key. The complications of requiring every instance of ServiceProvider to already have values for name and location is not a problem for me; they all will have. No ServiceProvider object gets constructed without them.

Now, when I map the class that way (without an component key class) will the database enforce uniqueness on this collective key, such that if I should try to insert a ServiceProvider with the same name and location values as are already persistent, the database will throw a "DuplicateKey" exception that I can catch and manage?

Are there any performance implications (good or bad) from this mapping approach?

Thank you very much!


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 02, 2005 11:36 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
Quote:
Now, when I map the class that way (without an component key class) will the database enforce uniqueness on this collective key, such that if I should try to insert a ServiceProvider with the same name and location values as are already persistent, the database will throw a "DuplicateKey" exception that I can catch and manage?

I dunno. Correct me if I am wrong, but I thought you said this was a pre-existing schema. If so, depends how you defined this stuff in your schema.

If using the schema export stuff, then Hibernate creates a composite primary key in the database (which by definition enforces uniqueness).

No performance implications; just that the code tends to be uglier (in my opinion).


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 02, 2005 11:41 pm 
Expert
Expert

Joined: Mon Jul 04, 2005 5:19 pm
Posts: 720
This does not affect performance, unless you are creating millions of these things.

The integrity of the data model (uniqueness constraint) is a responsibility of the database server (Oracle), as opposed to the ORM (hibernate).

However...
if you put two ServiceProviders,
with matching composite ids (remember equals() ),
into persistent state,
for the same Session,
... Hibernate will promptly punch you in the face and the database will have nothing to do with it.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 03, 2005 9:34 am 
Beginner
Beginner

Joined: Sun Sep 25, 2005 11:57 am
Posts: 29
Thank you, gentlemen ... both! I now have enough solid information to get something that works, I think. Then, I can jack around with it to test the limits of what it does. Thank you.

Steve, actually what I have is not a pre-existing schema in the same database facility (Oracle to Oracle). What I have is pre-esisting data (actually in a flat-file database) that I have to port to MySQL (the new database being used with Hibernate) so I am using SchemaExport (neat tool!) then I have to effect a read from the old DB to the new, to load the data. I have done that 3 times now (as I have struggled to get the mapping right). This time will be the fourth. Frustrating!

I do have another question. Not a problem for me at this time but this discussion raises the question: How, then, do I map this same dataset to allow non-unique keys ... so I coould have multiple keys of the same value? Nobody needs to answer unless you are just interested in the answer and have the solution handy. I am just curious about how one would achieve this.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 03, 2005 5:39 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Keys are unique, there's no way around that. Indices don't have to be unique; if you have a table with no guaranteed unique identifier, the best you can do is set up a non-unique index on the most-unique key-like column(s).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 03, 2005 8:28 pm 
Beginner
Beginner

Joined: Sun Sep 25, 2005 11:57 am
Posts: 29
Thank you.

Signing off on this topic!


Top
 Profile  
 
 Post subject:
PostPosted: Tue May 09, 2006 12:05 pm 
Newbie

Joined: Thu Apr 06, 2006 1:02 pm
Posts: 4
Guys,
May be late... but I am studing <natural-id> right now, and what why:
Hibernate recommends to use surrogate natural keys, always! (at least, for new database schema)
Having <natural-id> simplifies smth, we should have hashCode() and equals() accordingly, and smth like findByUniqueKey...
Am I right, should we use surrogate <id> and <natural-id> as the best practice (for new databases)?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 14 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.