-->
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 ... 6, 7, 8, 9, 10  Next
Author Message
 Post subject:
PostPosted: Fri Dec 09, 2005 3:59 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
As have been mentioned many times and is also done in the equals / hashcode article on hibernate:

This is *not* a hibernate specific issue; it is a *java Collection API* issue which is there for obvious reasons.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 09, 2005 5:38 pm 
Newbie

Joined: Fri Dec 09, 2005 11:02 am
Posts: 7
max wrote:
As have been mentioned many times and is also done in the equals / hashcode article on hibernate:

This is *not* a hibernate specific issue; it is a *java Collection API* issue which is there for obvious reasons.


Well, the issue I am trying to address is outlined in 109.html:
Quote:
However, once you close the Hibernate session, all bets are off. If you keep holding onto an object that you either created or loaded in a Hibernate session that you have now closed, Hibernate has no way to know about those objects. So if you open another session and query for "the same" object, Hibernate will return you a new instance. Hence, if you keep collections of objects around between sessions, you will start to experience odd behavior (duplicate objects in collections, mainly).


But if we do want to point to the Java Collections, the advice given here, 109.html and the Hibernate documentation is sending people to a Collections problem, specifically the use of natural keys/mutable objects, cause problems in the Java Collections framework, as outlined here:
Quote:
Some mutable objects will change their hashCode() value depending on their state (like the StringHolder example class in Listing 2). If you use such a mutable object as a HashSet key, and then the object changes its state, the HashSet implementation will become confused -- the object will still be present if you enumerate the set, but it may not appear to be present if you query the set with contains(). - Java theory and practice: To mutate or not to mutate?: DeveloperWorks, February 2003


But not only does document 109 send people headlong into the problem with no warning, it also tells people to just override equals and hashCode with natural keys and do not read / look for better solutions. This is very dangerous advice, given the use of those objects in Collections is now potentially buggy.

Quote:
Note that this is all that you have to know about equals()/hashCode() in most cases. If you read on, you might find solutions that don't work perfectly or suggestions that don't help you much. Use any of the following at your own risk.


Again, I ask the members of the community with the expertise to thoroughly cover this issue for the benefit of all. This issue is a dangerous "leaky abstraction" that is hurting Hibernate and it’s users.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 09, 2005 5:46 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Blahblah. This is not dangerous, nor leaky, nor anything else.

It's simply a consequence of an object-oriented programming language and equality-based collections. If in-memory identity is no longer sufficient to distinguish your objects, you obviously need to implement a new identification routine yourself. Nothing to do with Hibernate, nothing "leaks" or is difficult about this.

The only problem is that Java developers are traditionally completely unaware of this problem because no Java beginner book touches the subject. Stop blaming it on Hibernate. Just because you have seen the problem for the first time in your life when you've used Hibernate doesn't mean that its a Hibernate problem.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 09, 2005 5:46 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
I do not understand your reasoning! Why is it is a dangerous thing to expliciltly tell you that to have determinstic behavior in java collections you need to implement equals and hashcode ?

And how is it dangerous to explain that a generated id is not good enough for this since it can/will change when in the collection ?

And how is it dangerous to suggest that you use a natural key to implement it instead since a natural key has the exactly needed capabilities ?

If you have troubles in finding a natural key in your datamodel then change your datamodel or dont use the objects in collections (which is quite doable too!)

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 09, 2005 6:01 pm 
Newbie

Joined: Fri Dec 09, 2005 11:02 am
Posts: 7
christian wrote:
It's simply a consequence of an object-oriented programming language and equality-based collections. If in-memory identity is no longer sufficient to distinguish your objects, you obviously need to implement a new identification routine yourself.

Did and posted to this forum. 20000+ people have come to this forum for answers for well over a year, and to whit, there is no definitive answer for them. I cannot speak to their level of expertise, but I was hoping for someone with a lever higher than mine to clarify better than "it's not an issue" or "don't use these object in Collections" or "don't close your session".


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 09, 2005 6:08 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
Why can't you accept that there is no other solution than the one proposed ?

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 09, 2005 6:28 pm 
Newbie

Joined: Fri Dec 09, 2005 11:02 am
Posts: 7
max wrote:
Why can't you accept that there is no other solution than the one proposed ?

Would love to! Which one is the proposed one?

If we assume for a second that my business layer needs Collections, and that because I am using the DAO pattern, my session is not generally available, and could be closed, I am left with Natural keys right? Okay, so now, in each of my objects, I need to find a Natural key that won't change for the duration of the life of the object in any of my Collections. Hmm ... which could it be? A date, say creation date of some sort. Well, it is possible to have two objects with the same date, so what else can I add to the equals to make it unique? And after I find suitable keys for each of my objects, they and their mappings might need to be modified, or at best override the equals / hashCode to provide this unique, immutable identity key. Which brings me to the posted solution, where one base class ensures a unique, immutable identity key for the duration of the existence of the object. Done. No muss, no fuss. What am I missing? Did I break something else? Or did I miss something else with the other recommended approaches?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 09, 2005 6:56 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
It's intrusive. A natural business key is not intrusive and the correct way to approach a custom identity routine. The business key might even include all scalar properties of a class. Or, in other words, the business key is what your application users would consider when "identifying an object". You should use this key to identify objects in the application logic.

(This is the last time I'm explaining this here. Read Hibernate in Action.)


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 09, 2005 7:27 pm 
Newbie

Joined: Fri Dec 09, 2005 11:02 am
Posts: 7
christian wrote:
It's intrusive. A natural business key is not intrusive and the correct way to approach a custom identity routine. The business key might even include all scalar properties of a class. Or, in other words, the business key is what your application users would consider when "identifying an object". You should use this key to identify objects in the application logic.

Thank you Christian. Much appreciated.

My recommendation for those reading this thread: Before using natural business keys, it is important to understand the implications, in particular on Collection, Sets, and the Set Interface (Comparable in Java < 1.5) There are also lot of great articles to help understand the complexity and implications of overridding equals and hashCode.

christian wrote:
(This is the last time I'm explaining this here. Read Hibernate in Action.)

I will!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 27, 2005 3:49 am 
Newbie

Joined: Tue Dec 27, 2005 3:30 am
Posts: 11
The recommendation to possibly use "all scalar properties" needs to reiterate that these properties have to be immutable (for as long as the instance is in a persistent set - which would be forever in the use cases I am working with).

The Hibernate guys are getting pretty testy on this point, but the natural key recommendation will not work for the framework my project is building since the hibernate layer is transparent and the framework has no knowledge of which fields in the domain objects might be candidate keys.

Maybe the suggestion to use Bags instead of Sets is adequate for our purposes.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 27, 2005 4:49 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
if you domain objects don't know whick keys are candidate keys then you would have these issues in any java application you write (with or without hibernate)

But if your domain objects does not know how to identify/seperate its children without a persistent id what do you then use ? If it's some part of a child collection then that is all possible, but you will be hit by the need for objects to go to the database to decide its identity - which is something you just will have to live with.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 27, 2005 1:43 pm 
Newbie

Joined: Tue Dec 27, 2005 3:30 am
Posts: 11
[quote="gerrypower"]
if (vmid != null || vmid == null && id == null) {
[/quote]

In the earlier post, is there some reason the above is different from this -

if (vmid != null || id == null) {

Or are we just being extra careful? :-)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 11, 2006 12:12 am 
Newbie

Joined: Tue Jan 10, 2006 9:42 pm
Posts: 5
christian wrote:
It's intrusive. A natural business key is not intrusive and the correct way to approach a custom identity routine. The business key might even include all scalar properties of a class. Or, in other words, the business key is what your application users would consider when "identifying an object". You should use this key to identify objects in the application logic.



OK, I'm a entirely new to Hibernate, but I'm trying to get as much of an understanding of the fundamental issues before diving in. It seems to me that identity is a pretty fundamental issue. As I understand it, the problem is:
1) The Hibernate ID (synthetic key) isn't assigned to a new object until it is saved. But, ...
2)Java collections require that an identity be static, or the collection may not function correctly. so....
3) the recommended approach is to define equals and hashCode in terms of something that is already defined. That is, a natural key. But, ...
4) the trouble is, a natural key is not always so easy to come by. In all the 9 pages of discussion on this, I don't recall an concrete example. So, let's look at the ones provided in the Hibernate 3.01 reference documentation: Work (section 23.2, Author/Work) lacks such a code (as title might easily be duplicated by different works. I assume that could be corrected with an ISBN number, but that assumes that you actually need and ISBN number for your entity. Adding one if it isn't required by the domain is as intrusive. On Person, "Jane Doe" might very well occur twice in a collection (Mother and Daugher authors). Or, more likely, business rules might require that it be possible to leave the name blank for some period of time.
5) One solution would be to alter the data model and create some sort of false candidate key(e.g. a UUID) assigned by the application. However, if the sole purpose of this is to avoid the problem for the short period of time that an object doesn't have a persistent object ID, then that seems intrusive, as well.
6) So a number of tricks have been proposed, each with their own shortcomings.

It seems to me that the basic problem is that the ID of an object should be assigned no later than the first time someone asks for it. That means that we use Hibernate's "assigned" ID generation facility. If I read correctly, it also means that we need to use an Interceptor, because otherwise Hibernate is forced to go to the database to determine if the object is transient and must be saved(reference doc, sec 5.1.4. btw, the doc refers to Interceptor.isUnsaved, which apparently has been changed to isTransient, or something).

Is there some reason that this approach will not work? The only downside I can think of is that you may end up burning a number of IDs in whatever mechanism that you use to generate them externally from hibernate, should you use the objects in question for something other than persistence.


Top
 Profile  
 
 Post subject: Object Generation Approach
PostPosted: Tue Jan 31, 2006 9:41 pm 
Newbie

Joined: Thu Oct 06, 2005 7:23 pm
Posts: 7
mconner wrote:
Quote:
It seems to me that the basic problem is that the ID of an object should be assigned no later than the first time someone asks for it.


I recently wrote an article that implements an approach very similar to what you mention. It's designed to use an object's surrogate key (what you're calling the persistent object ID), retrieving it in a factory immediately after an object is created - before it's returned to the caller.

You can find the article on DevX:

Object Generation: A Better Approach to Hibernate Integration
http://www.devx.com/Java/Article/30396

_________________
Please remember to rate replies if they're helpful


Top
 Profile  
 
 Post subject: Small addendum to Gerry Power's proposed code
PostPosted: Sat Jun 09, 2007 4:17 pm 
Newbie

Joined: Wed May 30, 2007 7:00 pm
Posts: 5
Location: .NL
Hi,

After thinking about this for quite a while, and then staring at my demands list I've decided to use Gerry Power's technique of using two identifiers, the id once available and an object until then.

I've created a small addendum to his work in where the equals() function is better guarded against basic comparison errors (this is intentionally made to fail, a few lines of code can also compare id against id if set, but that would even further endanger users with hash() errors). Furthermore I've violated some don't-return-in-the-middle-of-a-method-good-coding-practices for (personal) readability.

(Note you can do away with the object and just use the local object to hash, but kept it for using an UUID generator later on.)

My ideal solution would be a hibernate singleton that provides database keys, but that's too expensive when just using entities and not persisting them. So just use self-generated UUID, which leads to DB-hell with multiple access methods (next to the current app). Choices, choices...

Hope this helps somebody as a simple copy/paste class for persistance buildingblocks with annotations.

Code:
@MappedSuperclass
@AccessType("property")
public abstract class GenericEntity
   implements
      Serializable
{
   protected Logger logger
      = Logger.getLogger( this.getClass().getName() );      
   
   // Object for storing uniqueness while not persisted
   private volatile Object object;
   
   // Id
   private long id = Long.MIN_VALUE;
   
   // Optimistic locking
   private int optimisticLockVersion;
   

   
   /**
    * Determines if this object has been stored in the database yet
    * 
    * @return true means not stored, false means stored
    */
   @Transient
   public boolean isTransient()
   {
      return( this.id == Long.MIN_VALUE );
   }
   
   /**
    * Returns the unique persisted identifier for this entity
    * (generated by the database after persistance).
    */
   @Id
   @Index(name="id")
   @GeneratedValue(strategy=GenerationType.SEQUENCE)
   public long getId()
   {
      return id;
   }
   public void setId(long id)
   {
      this.id = id;
   }
   
   /**
    * Get set the optimistic locking
    *
    * @see http://www.hibernate.org/hib_docs/annotations/reference/en/html/entity.html
    */
   @Version
   public int getOptimisticLockVersion()
   {
      return optimisticLockVersion;
   }
   public void setOptimisticLockVersion(int optimisticLock)
   {
      this.optimisticLockVersion = optimisticLock;
   }      

   /**
    * Equals implementation
    *
    * Based upon a technique by Gerry Power.
    *
    * It does mean that a pre-persisted object and [b]new instance[b] / retrieved object
    * will not be equal, but two new instances of the same retrieved object will always be
    * equal regardless of session or JVM. So you can freely mix and match disconnected objects
    * (even across JVMs), and you can mix and match pre-persisted and persisted objects
    * (with the same JVM), but you can't mix and match newly retrieved and disconnected objects
    * with pre-persisted objects.
    *
    * @see http://www.avaje.org/equals.html
    * @see http://forum.hibernate.org/viewtopic.php?t=928172&postdays=0&postorder=asc&start=105&sid=669abb6a5d57af5747220e08118d78b1
    */
   @Override
   public boolean equals(Object that)
      throws
         IllegalStateException
   {
      // Is entity?
      if (that instanceof GenericEntity) {
         
         // Cast and get id
         GenericEntity thatGenericEntity = (GenericEntity)that;
         long thatId = thatGenericEntity.getId();
         
         // Check if we have id and other side not, or we not and other side has, will fail
         if( ( ( this.id == Long.MIN_VALUE ) &&
              ( thatId != Long.MIN_VALUE ) ) ||
            ( ( this.id != Long.MIN_VALUE ) &&
              ( thatId == Long.MIN_VALUE ) ) ) {
         
            throw new IllegalStateException (
                  "This is using id and that is not using id (or in reverse)" );
         }
         
         // Compare objects
         return getObject().equals( thatGenericEntity.getObject() );
      }

      return false;
   }

   /**
    * HashCode implementation
    *
    * Based upon a technique by Gerry Power
    *
    * It does mean that a pre-persisted object and [b]new instance[b] / retrieved object
    * will not be equal, but two new instances of the same retrieved object will always be
    * equal regardless of session or JVM. So you can freely mix and match disconnected objects
    * (even across JVMs), and you can mix and match pre-persisted and persisted objects
    * (with the same JVM), but you can't mix and match newly retrieved and disconnected objects
    * with pre-persisted objects.
    *
    * @see http://www.avaje.org/equals.html
    * @see http://forum.hibernate.org/viewtopic.php?t=928172&postdays=0&postorder=asc&start=105&sid=669abb6a5d57af5747220e08118d78b1
    */
   @Override
   public int hashCode()
   {
      // Return the hashcode of our object
      return getObject().hashCode();
   }

   /**
    * Get the object to use when hashing
    *
    * Based upon a technique by Gerry Power
    */
   @Transient
   private Object getObject()
   {
      // Use id
      if( this.id != Long.MIN_VALUE ) {
         return new Long(this.id);
      }
      
      // Create object
      if( object == null ) {

         // Sync
         synchronized(this) {
            
            // Re-check, and create
            if( object == null ) {
               object = new Object();
            }
         }
      }

      // Use object
      return object;
   }    
}


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 ... 6, 7, 8, 9, 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.