SQL Zone is brought to you in partnership with:

John regularly blogs about ColdFusion, JavaScript and other web technologies and contributes to several FOSS projects. His hobbies include writing in the third person. John is a DZone MVB and is not an employee of DZone and has posted 32 posts at DZone. You can read more from them at their website. View Full User Profile

ORM hibernate sessions

06.19.2011
| 6989 views |
  • submit to reddit

I see quite a lot of confusion about how hibernate sessions work in ColdFusion 9's ORM (and I find it confusing as well!). So I thought I'd put together various examples to see what happens. These code samples are for ColdFusion 9.01 so if you're using 9.00 you will find that transactions work differently.

By default, ColdFusion will automatically save any modified objects at the end of a page request. Here's a quick example.

<cfscript>
Employee = EntityLoadByPK( "Employee", 1 );
Employee.setFirstName( "" );

// do some validation and save if OK
if ( Employee.getFirstName() != "" )
{
  EntitySave( Employee );
}
</cfscript>

Looking at the code above you'd may expect that the Employee will only be saved if the first name is not blank. In fact, running the code above will update the Employee record in the database even though we didn't explicity call EntitySave. This is because ColdFusion, by default will save any dirty (modified) objects automatically at the end of the request. This is not ideal so it is recommended that you use the this.ormsettings.flushatrequestend to false in you Application.cfc

After setting this.ormsettings.flushatrequestend to false in my Application.cfc, when I run the same code my Employee does not get updated in the database if the firstname is blank. However, we haven't solved the problem yet as if we run this code:

<cfscript>
Employee = EntityLoadByPK( "Employee", 1 );
Employee.setFirstName( "Fred" );

// do some validation and save if OK
if ( Employee.getFirstName() != "" )
{
  EntitySave( Employee );
}
</cfscript>

The above code will not update the database _at all_, even though the firstname is not blank! Why? Well because EntitySave doesn't actually save an existing object (you do need it with new objects though), in fact you don't need to use EntitySave at all if you are modifying an existing record. Confusing huh! So how do you actually save the record?

<cfscript>
Employee = EntityLoadByPK( "Employee", 1 );
Employee.setFirstName( "Fred" );

// do some validation and save if OK
if ( Employee.getFirstName() != "" )
{
  ORMFlush();
}
</cfscript>

OK, so I've replaced the EntitySave with an ORMFlush call. ORMFlush, makes hibernate commit any dirty (modified) objects to the database. So problem solved? Erm no! Let's look at something a bit more complicated.

<cfscript>
Employee = EntityLoadByPK( "Employee", 1 );
Employee.setFirstName( "" );

// do some validation and save if OK
if ( Employee.getFirstName() != "" )
{
  ORMFlush();
}

Department = EntityLoadByPK( "Department", 1 );
Department.setLocation( "Devon" );

// do some validation and save if OK
if ( Department.getLocation() != "" )
{
  ORMFlush();
}
</cfscript>

In the example above, we want to the Department's location set to Devon and saved, but we don't want to set the Employee's name to a blank string, but as you may already have guessed, that is exactly what is going to happen. Using ORMFlush, will save to database _any_ modified objects.

OK, so we need to stop the Employee record from being persisted. Let's try clearing the hibernate session before we update the Department:

<cfscript>
Employee = EntityLoadByPK( "Employee", 1 );
Employee.setFirstName( "" );

// do some validation and save if OK
if ( Employee.getFirstName() != "" )
{
  ORMFlush();
}
else
{
  ORMClearSession();
}

Department = EntityLoadByPK( "Department", 1 );
Department.setLocation( "Devon" );

// do some validation and save if OK
if ( Department.getLocation() != "" )
{
  ORMFlush();
}
else
{
  ORMClearSession();
}
</cfscript>

This time, when we run this script, then the Employee does not get persisted and the Department does. However, as we saw earlier ORMFlush is dangerous if we forget to use ORMSessionClear, and as we are dealing with persistence, we really should be using transactions (see Barney Boisvert's Why ORM Transactions are Crucial post. So, let's use some transactions. (It's worth noting that as Barney mentions in his post that if you're using ColdFusion 9 without the 9.01 update, then you need to use hibernate transactions like ORMGetSession().beginTransaction().)

<cfscript>
transaction
{
  Employee = EntityLoadByPK( "Employee", 1 );
  Employee.setFirstName( "" );
  
  // do some validation and save if OK
  if ( Employee.getFirstName() != "" )
  {
    transaction action="commit";
  }
  else
  {
    transaction action="rollback";
  }
} // end of transaction

transaction
{
  Department = EntityLoadByPK( "Department", 1 );
  Department.setLocation( "Devon" );
    
  // do some validation and save if OK
  if ( Department.getLocation() != "" )
  {
    transaction action="commit";
  }
  else
  {
    transaction action="rollback";
  }
} // end of transaction
</cfscript>

In the above example, if the object is valid, then commit, otherwise rollback the changes. I should point out that in CF9.01 then ColdFusion no longer closes the hibernate session when you start or end a transaction, so you can still refer to the objects later in your code. (Bob Silverberg explains this in his post (ColdFusion 9.0.1 Now Available - With ORM Goodies)

So, this all works as I want it to, but let's try it on some composed objects.

<cfscript>
transaction
{
  Employee = EntityLoadByPK( "Employee", 2 );
  Employee.setFirstName( "" );
  
  // do some validation and save if OK
  if ( Employee.getFirstName() != "" )
  {
    transaction action="commit";
  }
  else
  {
    transaction action="rollback";
  }
} // end of transaction

transaction
{
  Department = EntityLoadByPK( "Department", 1 );
  Department.setLocation( "Devon" );
  
  // allocate the Department to the Employee (not bi-directional)
  Employee.setDepartment( Department );
  
  // do some validation and save if OK
  if ( Department.getLocation() != "" )
  {
    transaction action="commit";
  }
  else
  {
    transaction action="rollback";
  }
} // end of transaction
</cfscript>

When we run this code, then the Department changes are persisted, but the Department is not assigned to the Employee. I'm not 100% certain why that is, but I believe that it is because the Employee belongs to a different hibernate session (as ColdFusion creates a different session per transaction).

The major takeaways I've found from experimenting and reading blogs and the cf-orm-dev group is that all changes to ORM entities should be wrapped in a transaction and this.ormsettings.flushatrequestend should be set to false. There is more that I need try such as the implications of the autoManageSession setting and what impacts lazy loading has, but for now, this seems to work for me. Corrections as always are welcome :)

References
Published at DZone with permission of John Whish, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Sirikant Noori replied on Sun, 2012/01/15 - 1:18pm

Thank you, my 1st CF9 ORM app is still using this.ormsettings.flushatrequestend = true (no such option pre CF9.01). What are some of the good reasons to flip it to false?

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.