gloeglm pointed out that plain old Java equality is actually good enough if 1) you don't keep objects around across sessions (which we don't) and 2) you aren't concerned about proxies. (Actually, he wasn't thinking about proxies, but we are in this thread.)
Somehow in this thread I got so wrapped up in things that I forgot all about using plain old object equality!
So I have added a flag to hbm2java to turn *off* its default equals() and hashCode() method generation. I plan to just use this flag and then not worry about this issue further.
Unfortunately for you, Paul, the
Hibernate manual makes clear that you can't get what you want without some substantial Hibernate surgery:
Code:
Certain operations do not require proxy initialization
* equals(), if the persistent class does not override equals()
* hashCode(), if the persistent class does not override hashCode()
* The identifier getter method
It looks to me like Hibernate is kind of trying to have it both ways here. It will ensure that two proxies to the same persistent object are == to each other (and hence hash to the same ID), but it will NOT ensure that an object is equal to its own proxy. (And come to think of it, how did you get into that situation anyway, unless you saved objects from session to session? -- which we already know is fundamentally evil, even in your land of beautiful horror?)
So I think that to make your hybrid-identity proposal work fully, you would need to:
[list=]Implement equals() and hashCode() as you have above
Add some kind of flag to the mapping to tell Hibernate to NOT try to load a proxy when calling equals() or hashCode() on it, but instead to just use the persistent identity[/list]
And you would still have trouble if you:
[list=]made a new object
added it to a set
saved it
closed the session
opened a new session
lazily loaded the object (getting a proxy)
checked to see whether the proxy is in the set[/list]
You want the final check to say "yes, this object is in the set." But even with your horrible beauty, that won't happen.
Although, let's continue down this primrose path of perfect obsession. I was explaining all this to a coworker and he said it made him nervous -- he wants a way to tell when someone does the wrong thing. So I was thinking, your objects that save their pre-persistent identity as their equalityObject could put themselves in some kind of weak cache when they do so. They are now the "Pre-Persistents" and they are forever suspect. My original thought ended there -- when you make a new session, do System.gc() and make sure the Pre-Persistent cache is empty afterwards (otherwise you are hanging onto objects when you shouldn't and you may corrupt your collections if you reload Pre-Persistents in the new session).
Now, *if* you could somehow intercept Hibernate's proxy creation, *then* you could check to see whether the persistent ID you are about to make a proxy for exists in this weak cache. If it *does* exist in that weak cache, it means that some collection somewhere is holding onto the original pre-persistent object. In that case, you should potentially just use that very object, rather than making a proxy at all!!!
What this basically amounts to then is a trans-session weak cache of ALL objects that obey this equality contract, and a tight integration of that trans-session with Hibernate.
I doubt whether Hibernate has the right hooks to enable such an integration now, but I'm sure it's possible.
However, having thought this through to the point where everyone except you and I are ready to shriek uncontrollably, and given that I don't actually need proxies let alone trans-session proxy-to-pre-persistent identity, I am going to stop thinking about this issue :-) I do hope you post how it goes for you though!
Cheers!
Rob