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.