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.  [ 5 posts ] 
Author Message
 Post subject: Where's the problem with database identifier equality?
PostPosted: Thu Sep 08, 2005 1:17 pm 
Newbie

Joined: Fri Jan 07, 2005 11:56 am
Posts: 6
Location: Manchester, England
In the book "Hibernate In Action" on page 123 it "explains" why you should not use database identifier equality.

It reads:

"Unfortunately, this solution has one huge problem: Hibernate doesn't assign identifier values until an entity is saved. So, if the object is added to a Set before being saved, its hash code changes while it's contained by the Set, contrary to the contract of java.util.Set. In particular, this problem makes cascade save useless for sets. We strongly discourage this solution (database identifier equality)."

Now here is what confuses me:

I use database identifier equality on all my POJOs and many of them have sets that make use of cascade save (actually the sets are configured as all-delete-orphan which includes cascade save). Everything works fine. I can add new objects to the sets and the cascade save works. Sure, the hashcodes of the child POJOs change in the cascade save process but whats the issue with that? It doesnt appear to break anything. Can someone please explain where the problem is?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 08, 2005 3:15 pm 
Senior
Senior

Joined: Wed Jul 13, 2005 4:31 pm
Posts: 142
Location: Seattle, WA
Here's a more detailed explanation from the wiki
[url]
http://hibernate.org/109.html[/url]

hope it helps


Top
 Profile  
 
 Post subject:
PostPosted: Thu Sep 08, 2005 4:37 pm 
Pro
Pro

Joined: Fri Sep 02, 2005 4:21 am
Posts: 206
Location: Vienna
Hi,
anar wrote:
Here's a more detailed explanation from the wiki
[url]
http://hibernate.org/109.html[/url]

Your link was broken - here a corrected version: http://hibernate.org/109.html

Erik


Top
 Profile  
 
 Post subject: Non-hibernate reasons to not use database identifier as key
PostPosted: Fri Sep 09, 2005 12:00 am 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
That article only describes the downsides from hibernate's point of view. There are "normal" java reasons that you should use only business key equality. For example, consider using a standard webapp to modify a user-editable object.

You load the object, disconnect it, put it in scope, and send it off to the user. When it comes back you apply the user's changes, load the version from the DB (using the key in the object retrieve from the request scope), and compare the DB version against the user-editted version. If you're using database id equals, the objects would compare as equal. Therefore you decide not to save it, because the user obviously made no changes. To fix this, you either have to write a special "are all values equal" method, or use the business key equals.

Alternatively, consider using a HashMap to store newly-created objects, in preparation for a bulk insert. Initially all objects will have the same key (null), so they all use the same hash value. Your map is now as effective as a list. Then you save all your objects. Your map is now useless because all the objects now have sensible keys but are stored in the map under the null key. Despite the fact that you didn't change the map at all. If would have been even worse if you'd used a Set.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 12, 2005 9:56 am 
Newbie

Joined: Fri Jan 07, 2005 11:56 am
Posts: 6
Location: Manchester, England
Thanks for your replies guys.

Looking at the suggested url http://hibernate.org/109.html it gives the following 2 examples of problems with database identifiers

Example 1
---------
Quote:
// Suppose UserManager and User are Beans mapped with Hibernate

UserManager u = session.load(UserManager.class, id);

u.getUserSet().add(new User("newUsername1")); // adds a new Entity with id = null or id = 0
u.getUserSet().add(new User("newUsername2")); // has id=null, too, so overwrites last added object.

// u.getUserSet() now contains only the second User


I DO NOT AGREE WITH THE ABOVE EXAMPLE! If id of the User class is a Long (i.e. not long) then it will equal null for newly created instances. If User contains an equals method like that discussed in "Hibernate in Action" as shown below (hashCode() method also shown for completeness)

public boolean equals(Object other) {
if (this==other) return true;
if (id==null) return false;
if ( !(other instanceof User) ) return false;
final User that = (User)other;
return this.id.equals( that.getId() );
}

pubic int hashCode() {
return (id==null)?System.identityHashCode(this):id.hashCode();
}

then the second User object will NOT overwrite the first object because the second line of the equals method will return false due to id=null. Hence the 2 objects will be treated differently by the set. I have used this approach to implement parent/child relationships with database identifier equality and found it to work OK.

Example 2
---------
Quote:
// on client, let's assume the UserManager is empty:
UserManager u = userManagerSessionBean.load(UserManager.class, id);
User newUser = new User("newUsername1");
u.getUserSet().add(newUser); // have to add it to set now since client cannot save it
userManagerSessionBean.updateUserManager(u);

// on server:
UserManagerSessionBean updateUserManager (UserManager u) {
// get the first user (this example assumes there's only one)
User newUser = (User)u.getUserSet().iterator().next();
session.saveOrUpdate(u);
if (!u.getUserSet().contains(newUser)) System.err.println("User set corrupted.");
}


I've managed to reproduce the issue highlighted in example 2 (although I did need to flush the session after the saveOrUpdate method due to the transactional write behind feature of hibernate).

This is a really strange issue because I found that after committing the transaction, newUser was allocated an id(as expected) and if I iterated through the getUserSet() collection and inspected each object, one existed with the same id as newUser, the same hashCode() and was equal in terms of both java identity(==) and applying the equals() method, yet still the call u.getUserSet().contains(newUser) returned false.

I've also discovered another quirk related to the above example which occurs if, after the update, you apply the following

Iterator it = u.getUserSet().iterator();
while (it.next()) {
it.remove();
}
if (!u.getUserSet().size()>0) System.err.println("User set not empty.");

My conclusions are that it IS possible to implement parent/child relationships using database identifier equality although one needs to be aware that certain method calls on the updated child set may not work if the update resulted in a change of id (e.g. null to not-null) of any child element. This may or may not be an issue depending on the structure of your code.


tenwit wrote:

Quote:
You load the object, disconnect it, put it in scope, and send it off to the user. When it comes back you apply the user's changes, load the version from the DB (using the key in the object retrieve from the request scope), and compare the DB version against the user-editted version. If you're using database id equals, the objects would compare as equal. Therefore you decide not to save it, because the user obviously made no changes. To fix this, you either have to write a special "are all values equal" method, or use the business key equals.



Actually there is no need to do it like this.
Instead, once you have loaded the version from the DB, update it with the new values using org.apache.commons.beanutils.BeanUtils. Do not call session.update. When hibernate commits the transaction, "automatic dirty checking" will determine whether the object has changed or not. If no change has occurred, hibernate will make no attempt to update the database.

BTW the suggested URL http://hibernate.org/109.html contains links to some in-depth discussions on this whole subject.


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