-->
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.  [ 147 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6 ... 10  Next
Author Message
 Post subject:
PostPosted: Wed Feb 25, 2004 7:11 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
I should clarify that in my example, Campaign is a class generated by hbm2java.....

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 2:19 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
So Mel, how's it going? I hope it is working, because it's looking more and more like the pattern I personally want to build into hbm2java....


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 2:33 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
christian wrote:
We are talking about two different scopes:

The scope when two objects have the same database identity and are in the same Set, outside of a Session, non-persistent (detached). We recommend to implement equals()/hashCode() for this to work, using a candidate key attribute.

The scope of two objects that have the same database identity and are in the same Table. We need a primary key for that and we use the best candidate key as a primary key attribute or better, add a surrogate key. The mapping of this database identifier to an identifier property is actually optional!

Don't mix those two, the first is only relevant for a few milliseconds, the other for a hundred years.


The problem is that those two *do* mix, when you're dealing with disconnected object graphs, and especially when the disconnected object graphs contain objects that have not yet been persisted. If the disconnected object graph contains collections, and you persist the whole graph, many of those collections may now be broken.

There are evidently only two actual answers:

1) Define your object identity to be based on some candidate key fields, which are NOT the database identity. The more I look at this, the more it looks like using application-assigned IDs... though I suppose the right way to think of it is that Hibernate IDs are an implementation detail solely to improve performance, not anything that your code should ever access. (In fact, if this is the recommended strategy, then the best practice should be to always make your Hibernate IDs private *and* prevent them from ever being used in equals() or hashCode()!)

2) Use Mel's technique (which I sure hope works!) for creating a "session identity" for an object, under which objects obey conventional Java uniqueness rules and support the usual equals() and hashCode() contracts, PROVIDED YOU NEVER LET OBJECTS ESCAPE THE SCOPE OF A SESSION.

The first answer tries to move all Hibernate systems away from ordinary Java instance rules and towards a model of relational equality (two objects are distinct only if their primary keys -- which are NOT the Hibernate-assigned identifier -- are distinct).

The second answer tries to unify Java object identity and Hibernate-assigned identity within the scope of a single session (which is arguably in line with Hibernate recommended practice anyhow).

If the first answer is really the way to go, then some serious changes are needed to hbm2java, as there are a lot of people using broken hbm2java-generated code without realizing it :-( In fact, the same is true if the second answer is also the way to go, as then the second answer's hashCode technique should be the standard in hbm2java.

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 2:41 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
1) is the recommended approach. Database identifier have to be considered a Hibernate implementation detail, accessor methods should be private in most cases. There are exceptions to that rule, but they are somewhat rare.

As always: Hibernate tries to be as transparent as possible. Implement equals()/hashCode() just like you would do without Hibernate.

_________________
JAVA PERSISTENCE WITH HIBERNATE
http://jpwh.org
Get the book, training, and consulting for your Hibernate team.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 2:42 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Also note that hbm2java DOES NOT use the recommended approach. It has to be updated, and if there isn't any Jira issue, please add one.

_________________
JAVA PERSISTENCE WITH HIBERNATE
http://jpwh.org
Get the book, training, and consulting for your Hibernate team.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 3:02 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
christian wrote:
As always: Hibernate tries to be as transparent as possible. Implement equals()/hashCode() just like you would do without Hibernate.


The thing is that without Hibernate, I can easily do:

Code:
public class Cat {
    private String name;
    public Cat (String name) { this.name = name; }
    public String getName() { return name; }
    public String setName(Name name) { this.name = name; }
}


Set set = new HashSet();
Cat cat1 = new Cat("Mittens");
set.add(cat1);
Cat cat2 = new Cat("Mittens");
set.add(cat2);
assert(set.contains(cat1) && set.contains(cat2));
cat2.setName("Boots");
assert(set.contains(cat1) && set.contains(cat2));

It's very easy to create cats that are distinct cats, despite having the same name. This is exactly what Hibernate-assigned identifiers do. The ideally transparent thing for Hibernate to do would be to allow the above code to work, even with any combination of "session.save(cat1)" or "session.save(cat2)" or "session.flush()" added in.

Note that Mel's implementation technique from earlier in this thread arguably achieves exactly this! Do you have any response to that technique itself? Haven't seen you mention any yet....

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 3:04 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
This code works with Hibernate without changes. Whats the problem?

_________________
JAVA PERSISTENCE WITH HIBERNATE
http://jpwh.org
Get the book, training, and consulting for your Hibernate team.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 3:08 pm 
Beginner
Beginner

Joined: Sun Oct 26, 2003 11:21 pm
Posts: 27
Rob, I completely disagree with the implementation that you're talking about, which Mel posted on 2/25. Think through Mel's code and you'll see other logical inconsistencies. For every inconsistency that you fix, you'll open another one.

Let me start by saying that it can be hard to design a system that does processing ina client layer and sends it back to the Hibernate-enabled business layer for persistence. You generally can't create a new entity and add it to a collection in one step. You have to create it, refresh it, then add it. Remember that this is pretty much the same deal without Hibernate, when using plain JDBC. Hibernate doesn't make your life any easier in this particular case.

Now, depending on your implementation, you must choose one of these with respect to how equals and hadhCode figure out equality, you can't implement more than one of these without getting into trouble.:

An object's identity depends on it's Hibernate key, in which case you should not be comparing objects before saving them. Note that adding an object to a Set will compare it to other members of the set and so, shouldn't be done.

Or

Its identity depends on the other, non-ID fields, in which case you can compare non-persisted objects but you can't expect Hibernate to know if a new object should be saved or updated without an explicit hint. They recommend this approach.

Or

Its identity depends on both the Hibernate key and the other, non-id fields, in which case you should expect (in the code) the identity to change when you first save the object.

Or

Its identity is based on it's JVM object ID (memory location). This, IMHO, is a bad idea for domain objects. See chapter 3 of Effective Java: "A programmer who compares references to value objects using the equals method expect to find out whether they are logically equivalent, not whether they refer to the same object.".


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 3:11 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
christian wrote:
Also note that hbm2java DOES NOT use the recommended approach. It has to be updated, and if there isn't any Jira issue, please add one.


I couldn't find one, so:

http://opensource.atlassian.com/project ... key=HB-754

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 3:14 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
christian wrote:
This code works with Hibernate without changes. Whats the problem?


OK, you're right. Since I didn't override equals or hashCode, yes, this code will work. What wouldn't work is the hbm2java version (which would use the persistent id), but you have already agreed that that is a serious problem.

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 3:23 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
hgilde wrote:
Rob, I completely disagree with the implementation that you're talking about, which Mel posted on 2/25. Think through Mel's code and you'll see other logical inconsistencies. For every inconsistency that you fix, you'll open another one.


The problem is that *all* these options have logical inconsistencies. It is all a space of tradeoffs. Java object identity, relational equality, and Hibernate-assigned persistent identity all have different semantics. Which semantics you want for your objects is a design choice that you make up front, and any possible choice will mean that some "natural" code will break.

Quote:
Now, depending on your implementation, you must choose one of these with respect to how equals and hadhCode figure out equality, you can't implement more than one of these without getting into trouble:

You can get into trouble in each of the individual scenarios you describe.

Quote:
An object's identity depends on it's Hibernate key, in which case you should not be comparing objects before saving them. Note that adding an object to a Set will compare it to other members of the set and so, shouldn't be done.

In other words, you get into trouble if you do it. This is the kind of model that breaks my multi-Cat example above.

Quote:
Its identity depends on the other, non-ID fields, in which case you can compare non-persisted objects but you can't expect Hibernate to know if a new object should be saved or updated without an explicit hint. They recommend this approach.

Actually, can't you have a private property for the Hibernate ID and let Hibernate use that to figure it out? (i.e. it's an implicit, Hibernate-managed hint?) The trouble here is that you may not *have* any such non-ID fields, as in my Cat example -- cat1 and cat2 are different cats whether they have the same name or not.

Quote:
Its identity depends on both the Hibernate key and the other, non-id fields, in which case you should expect (in the code) the identity to change when you first save the object.

The trouble here (obviously) is that you will be breaking collections with no way to fix them.

Quote:
Its identity is based on it's JVM object ID (memory location). This, IMHO, is a bad idea for domain objects. See chapter 3 of Effective Java: "A programmer who compares references to value objects using the equals method expect to find out whether they are logically equivalent, not whether they refer to the same object.".

The quote you cite refers to *value* objects. *Domain* objects have individual identity potentially regardless of any of their field properties. (OK, this is arguable, but certainly not in principle inconceivable.)

Overall, actually, I agree with you that it is likely better to make just one choice for your semantics of object identity. But you can't justify that choice simply on the basis of avoiding trouble -- there are different kinds of trouble with any choice you can make. Mel's technique has its own kinds of trouble, but that (to me) doesn't disqualify it as just one more possibility.

Cheers,
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 3:33 pm 
Beginner
Beginner

Joined: Sun Oct 26, 2003 11:21 pm
Posts: 27
yeah, I agree that it's definitely an implementation of some rules that might work in a particular scenario.

But I wouldn't want those to be the default rules that hbm2java generates because they're a little complicated thus are less likely to work for the majority of cases.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 3:38 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
I agree with everything you just said.

Phew! :-)

Now the real questions I have for Christian are:

1) What equality rules do Hibernate-generated proxies use?

2) Will proxies have Java object uniqueness within a session? (i.e. if two collections reference the same Hibernate instance of a lazy class, will each collection wind up containing the *same Proxy object*?)

Mel asked these questions way back in the mists of yesterday, but Christian (and/or some other Hibernate-clueful person) never answered. Personally I'm not yet using proxy objects (just lazy collections), but I'm still curious :-)

Cheers!
Rob


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 3:57 pm 
Beginner
Beginner

Joined: Sun Oct 26, 2003 11:21 pm
Posts: 27
I'm not a Hibernate team member but I believe that the answers are:

1) proxies pass equals and hashCode to the underlying object

2) they are the same object and can be compared with ==


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 26, 2004 5:03 pm 
Expert
Expert

Joined: Thu Jan 08, 2004 6:17 pm
Posts: 278
hgilde wrote:
1) proxies pass equals and hashCode to the underlying object

*What* underlying object? For CGLIB proxies there *is* no underlying object. Are you saying that equals() and hashCode() actually do a full database load and then construct a real object just to call equals() or hashCode() on it? That would be the same as saying that adding a proxy to a collection forces a full load of the proxy!

Quote:
2) they are the same object and can be compared with ==

I certainly hope this is true but pardon me if I don't take your word on it ;-)

Cheers,
Rob


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 147 posts ]  Go to page Previous  1, 2, 3, 4, 5, 6 ... 10  Next

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.