ColdFusion 9 ORM – Explaining Hibernate Object State

In the previous two articles, we started the discussion of Hibernate Sessions and how they related to the ColdFusion 9 ORM.  We noted the fact that a Hibernate Session
keeps track of what objects it loads, and can therefore update it when
it realises that the object has changed.  The question then posses
itself – if a Hibernate Session has closed, which happens at the end of every ColdFusion request, what happens to the objects that it was managing?

Going forward, I'm going to reuse the Musician model I used in the Introducing ORM in Adobe ColdFusion 9 beta Adobe DevNet article, for which the updated code samples can be found here.

The very short version for those who don't wish to look at the code, is
a model in which we have a Musician, who has a name, and an age, and
he/she also has a many-to-many relationship to an array of Instruments,
which they can play.

As far as Hibernate is concerned, there are three different states for any Object that it interacts with.

Transient

An Object/Entity (you could use either word) is Transient, if it has just been created with a createObject() or new operator, but has yet to be saved with the EntitySave() method.

Obviously, if transient objects are not persisted via EntitySave(),
then they never get stored in the database, and will eventually be
garbage collected.

For example, our musician john below is a transient object:

 


<cfscript>
    import com.*;
   
    //this is transient
    john = new Musician("Transient John");
</cfscript>

Since john is never saved, it will always be transient.

Persistent

A Persistent object is one that has either come from, or has been saved in the database, within the scope of the current Hibernate Session (which is an important point to note).

For example, we can make our musician john below persistent, by:


<cfscript>
    import com.*;
   
    //join is transient
    john = new Musician("Transient John");
    
    john.setAge(47);

    //john is now persistent   
    EntitySave(john);
</cfscript>


Therefore, since john was inserted into the database, he is persistet.

By the same token, if we retrieve an object through the ORM, it is also persistent:


<cfscript>
    //larry's ID is 2

    //larry is also persistent
    larry = EntityLoad("Musician", 2, true);
    
    //As larry is persistent, we can change his age, which will be reflected in the database
    larry.setAge(19);
</cfscript>

Since larry was retrieved from the database, he is also persistent.

Detached

Detached objects are ones that used to be persistent, but the Hibernate Session has
closed. Detached objects were one of the biggest things for me to wrap
my head around when starting with Hibernate, and can cause a few really
tricky errors to occur.

To explain detached a little better, we can go through a
scenario that will causes them, as I think this is something that a lot
of ColdFusion developers are going to end up running in to.

Let's have a new musician, called Tim, and time is going to have 2 instruments that he plays, the Flute and Piano.

We are going to grab Tim out of the database, and then put him in our ColdFusion Session scope, so we can keep track of him from request to request.


<cfscript>
    tim = EntityLoad("Musician", 2, true);
    
    session.musician = tim;    
</cfscript>


For this request, Tim will be persistent, however, on the next request, Tim is now detached, as the Hibernate Session will have closed at the end of the previous request.

Therefore, if I do this:


<cfscript>
    instruments = session.musician.getInstruments();    
</cfscript>

<cfdump var="#instruments#">


You will see the following (not well written) error:

failed to lazily initialize a collection of role: Musician.instruments, no session or session was closed

If you are running ColdFusion via the console, you will also see something similar to:

org.hibernate.LazyInitializationException: failed to lazily initialize
a collection of role: Musician.instruments, no session or session was
closed

What does this error even mean? 

First of all, we should remember that the collection of Instruments our Musician can play are lazy loaded. This means they aren't actually loaded from the database into the object until you actually request them.

The important thing to note here, is that lazy loading with Hibernate can only occur if the Hibernate Session that the object was originally loaded from is still open.  To put it another way, if the Hibernate Session has closed, then Hibernate has no record of what the object was doing, and therefore can't do any lazy loading.

In the above example, since the previous ColdFusion request ended, the Hibernate Session has closed, and if you attempt to access a relationship that is set to lazy load, and has yet to be loaded, this error occurs.

Its should be noted that singular properties are not lazy loaded –
therefore, if you are just grabbing a property on an object that is
detached, no error will be thrown.

What can we do to mitigate this issue? There are a few options, one of
which is, to simply retrieve the object all over again, like so:


<cfscript>
    session.musician = EntityLoad("Musician", session.musician.getMusicianID(), true);

    instruments = session.musician.getInstruments();    
</cfscript>

<cfdump var="#instruments#">

Or, like so:

<cfscript>
    session.musician = entityLoadByExample(session.musician, true);

    instruments = session.musician.getInstruments();    
</cfscript>

<cfdump var="#instruments#">


As then the object we are dealing with is considered persistent, as it is attached to the currently open Hibernate Session.  This tends to be the easiest of the options when dealing with detached objects.

Another interesting way of making our detached object persistent is like so:


<cfscript>
    EntitySave(session.musician);

    instruments = session.musician.getInstruments();    
</cfscript>

<cfdump var="#instruments#">


This may look strange, but what is actually happening when you call EntitySave(), is that it calls the Hibernate method saveOrUpdate()

A quick version of what saveOrUpdate() does is –

  • If the object being passed in is transient, INSERT it to the database.
  • If the object is already persistent in the current Hibernate Session, do nothing.
  • If there is already an object that represents the same Entity data persistent in the Session, throw an exception
  • Otherwise, update the database with the information in the passed in object, and set its state to persistent.

Therefore in the example above, the data stored in session.musician will be saved again to the database, and since session.musician is now deemed persistent, we can successfully do lazy loading of the instruments.

However, if a situation like this would occur:


<cfscript>
    musicianTwo = EntityLoad("Musician", session.musician.getMusicianID(), true);

    EntitySave(session.musician);
</cfscript>


We get the following error:

a different object with the same identifier value was already associated with the session: [Musician#2]

Since Hibernate deems the persistent object that is currently connected to the Hibernate Session much more important than the detached object, which makes sense, as the persistent object has the most up to date version of the data stored in the database.

Generally speaking, if you at all can, try and avoid having detached
objects in your application, as this is generally a Good Thing™, and
will allow you to avoid some pretty decent headaches as you develop
with CF9 ORM.  Strategies such as storing IDs in the ColdFusion Session
and having an API to return a new Object by that ID on request is often
a good way to go, rather than storing the Entity itself in the ColdFusion Session.  That being said, if your architecture means that you have to have detached objects this series should be enough to give you a basic grounding the concepts of
object state, and how to manage it.

If you want to learn
more about managing Hibernate Object States (and there is far more to it than I have written here), you can read more about this in the Hibernate documentation,
but this should hopefully have given you a decent enough overview to
avoid some possible pitfalls when handling persistence with ColdFusion
9's ORM integration.

Leave a Comment

Comments

  • Ben Nadel | August 4, 2009

    Mark, really fascinating post. I had no idea about any of this, so this has been really helpful.

  • Terrence Ryan | August 4, 2009

    This, Ben, is an example of one of the pearls of wisdom that Mark Mendel is capable of pulling out of his marsupial pouch.

  • Sam Farmer | August 4, 2009

    Pretty much what Ben said. Interesting stuff and a great explanation.

  • John Whish | August 4, 2009

    Excellent post Mark. I have run into this issue (with a logged in user entity) and ended up storing just the id in the session scope and loading the entity each time. Now I know why it was throwing errors!

  • Brian Klaas | August 4, 2009

    Great post, Mark! I was just reading about this very issue in my Grails studies this morning. In the Grails articles I was reading, they really didn’t give advice on how to handle detached objects stored in the current HttpSession (the "classic" ColdFusion session scope). Your advice of storing the ID of an object and having it loaded on each request seems like a good one. Do you see any advantage in terms of performance to running EntitySave() instead of loadByID on a HttpSession-scoped object that has become detached between requests if the object graph of the HttpSession-scoped object is relatively deep?

  • Mark | August 5, 2009

    @All, Glad you liked the article! I’ve got more to come, but I’m going to finish off an OSS Project first.

    @Brian – this is a classic ‘it depends’ scenario. I’ve stored an Entity in session scope directly, when 9/10 times, all I needed were property values, and then had an API to tell it to reload when I needed it to be connected to the current Session, so I could access relationships.

    Remember that EntitySave() will UPDATE the database with the details of whatever is in session, which may very well not be what you want.

    You asked specifically about ‘if the object graph of the … object is relatively deep’. The question is – how much of that graph is accessed all the time within the application? If it is accessed a lot, maybe it is worth making sure it is all loaded, and storing that in the session scope. If it is accessed infrequently, then it may not well matter.

    Also, don’t forget, you could also combine this sort of scenario with a multitude of caching strategies to improve performance on that particular object as well. So there are lots of ways to skin this proverbial cat.

  • Brian Klaas | August 5, 2009

    Thanks, Mark!

  • Glenn Seymon | December 9, 2009

    Thanks Mark – great article!
    I ran into the old "failed to lazily initialize a collection of role" problem yesterday.
    Your explanation really helped to make sense of what was going on.

  • Luis Majano | January 21, 2010

    Mark,

    Couldn’t you just do an EntityMerge(session.musician) ??

  • Mark | January 21, 2010

    @Luis – That is another option. The issue with entity merge is that EntityMerge will overwrite any current data for the given object with the data from session.musician. This may, or may not be what you want to do.

    That being said, given all the other potential issues with detached objects, they should (in general) be avoided if possible.

  • Dan Vega | February 25, 2010

    Mark – Thanks for this great post, really helped me understand what was going on. I was actually in the same scenario that you spoke of, a user instance. I am loading a user that has a many-to-many relationship for Roles.

    My question is this. In this scenario where I know roles is never going to be a long list. Does it make sense for me to just set lazy to false. It will load them all at once but at the most the user will have is 5. This works for this scenario and for when we are loading a single user to edit.

    Would like to hear what you think. I could def understand why you would not want to do this with a relationship that would pull in a lot of other objects though.

    If not I like the idea of storing the user id and grabbing the user when I need them.

    Thanks again!

  • Mark | February 25, 2010

    @Dan
    This again is where ‘it depends’. It really depends. Are you using your User object and its roles in other area of your site? If so, is there a performance impact on always getting the roles? Are there other collections on a User you want to know about?

    If you set up your API well, and abstract away the *how* the user is stored in Session (see: http://www.coldfusiondesignpatterns.org/wiki/Facade for a great article on how a proper Session Facade should be done), you can start with one implementation choice, and then switch it to another without any of your application knowing.