I’ve been reading quite a few mailing list posts on ColdFusion based ORM, and I find that I’m seeing a lot of repeats on what configuration settings people are generally using when setting up ORM in their Applicaiton.cfc, and why they should use certain settings.
I figured it would be useful if I wrote down how I like to have my base configuration of my Application.cfc in regards to ORM, and also take a step by step look through why I set things up the way I do. Hopefully this will help out a few people when they first start out with ORM, and are looking at all the different options for ORM in ColdFusion.
<cfscript>
this.datasource = “mydatasource”;
//orm settings
this.ormEnabled = true;
this.ormSettings.cfclocation = expandPath("/model");
this.ormSettings.automanageSession = false;
this.ormSettings.flushatrequestend = false;
this.ormSettings.useDBForMapping = false;
this.ormSettings.autogenmap = false;
if(isDevEnvironment())
{
this.ormSettings.dbcreate = "dropcreate";
this.ormSettings.sqlscript = expandPath("/import.sql");
}
</cfscript>
So breaking this down line by line.
this.datasource = "mydatasource";
This isn’t specifically an ORM setting, but I love how in CF9, we can set a default datasource across the application in one place. Also like that ORM picks this up too, although it can be overwritten with ormSettings.
this.ormEnabled = true;
Again, nothing special. We turn on ORM. Without this line, nothing happens.
this.ormSettings.cfclocation = expandPath("/model");
When ORM is enabled, by default, ColdFusion will scan from your web root, looking for any CFC’s that are marked as persistent. As a performance enhancement, it’s useful to tell it specifically what folder to look at. Also, if you have your components stored in a mapped directory, this is the only way it can be done.
I’ve seen this set a few ways (dot notation, relative paths), and seen a fair few people have issues with this. Personally, I’ve never had an issue when using the expandPath(), so I stick with that as the way to go.
this.ormSettings.automanageSession = false;
this.ormSettings.flushatrequestend = false;
I bundle these two together because they are quite related in what they do. They basically control how the Hibernate session works in ColdFusion ORM. This combination of settings does the following:
- Turns off the Hibernate session being flushed at the end of the request
- Stops the Hibernate session being flushed at the beginning of a transaction
- Stops the Hibernate session being cleared when a transaction is rolled back
For me, this is very, very, very important. The default setting of having these both on for ORM makes doing basic things with ORM very easy, but when you start getting into more complex scenarios, in which issues like object validation or concurrency arise, these default settings can throw some fun Hibernate exceptions on your screen super fast.
By setting the above to false, you have complete control over when a session will flush (only when a transaction commits), and you know it will only be cleared when you request it to be. This makes it a lot easier to manage your Hibernate object states in those more tricky scenarios.
(You can use ormFlush() with the above settings, but please don’t do that. Please use transactions. Your database will thank you.)
this.ormSettings.useDBForMapping = false;
This is primarily a performance setting. By turning this off, it stops ColdFusion from going out to your database and looking at what tables and columns you have and attempting to generate it’s object mappings from there. Personally I can’t stand this functionality, as your DB then drives you model (not an ideal scenario), and also because, if you are using annotations / HBMXML config files, there is absolutely no reason to do this (and I’ve read of people on Oracle having crazy issues with having this enabled by default as well).
this.ormSettings.autogenmap = false;
This setting is most definitely a personal preference thing. When I work with ORM I use the HBMXML mapping files, as every time I use the annotations, I find it doesn’t default the way I would expect/want, or I am looking to use some Hibernate functionality that isn’t exposed through annotations. Then I have to go back and move all my configuration from annotations to a HBMXML file. It’s far easier to me to start with a HBMXL file, and never look back. Since I also have a CFBuilder extension to help generate the stub files, it makes it an easy decision.
Setting autogenmap to false, means that even if someone were to come along and use annotations, they wouldn’t work, so it makes sure I don’t have that "Just this once I’ll do annotations..." thought which inevitably leads me back to rewriting annotations into HBMXML again.
this.ormSettings.dbcreate = "dropcreate";
this.ormSettings.sqlscript = expandPath("/import.sql");
This setting is more for greenfield projects. On legacy projects, I find that they tend to frown on having all their development data dropped, for obvious reasons, and it can be a large project to write an import script for it all. However, when starting from scratch, it’s really nice to have a clean data set on every reload, especially from a unit testing perspective.
There are many more settings that can be used, that are generally more application specific, such as the SQL dialect, or whether or not you enable the second level cache, but this is generally speaking, the base configuration I start most project from.
Bob Silverberg and I are back at it again, with a one day pre-conference lab at
Adobe MAX this year covering an introduction to Object Relational Mapping in ColdFusion 9.
As the description states:
Learn about one of the most compelling new features in Adobe ColdFusion 9 from two seasoned ColdFusion veterans. This release includes the integration of Hibernate, an enterprise-class, Java based ORM. Join us to hear how to get started using ColdFusion 9 ORM features in your next project. Through interactive learning and hands-on labs, we'll start with the basics and work together to add features to an application that takes advantage of the ORM. Then we'll discuss best practices and potential pitfalls, making this an ideal combination of introductory material and tips and tricks.
If you are interested in getting your feet wet with ColdFusion 9 ORM, you can get more information on the
MAX pre-conference page .
Hope to see you there!
Like
Tyra Banks giving away Vaseline to her entire audience, the attendees of
Bob Silverberg's and I's
ColdFusion 9 ORM Training, that we are running at
cf.Objective(), ever student who attends our training will also get a copy of:
Java Persistence with Hibernate!
You get a book! You get a book! You get a book! Yoouuuuuu get a Booooooooook!!!!
While this is a Java book, and hey, we all write ColdFusion, it is one of
the
books to read on utilising Hibernate within your applications. Reading
this book will give you a huge insight into what ColdFusion is doing
under the covers, which will help out a great deal when building
applications with ColdFusion 9.
So not only do you get some great training from Bob and I, you also get a fantastic reference to take home with you as well!
If that doesn't push you over the edge to
come join us, I don't know what will.
(Ordering now does not provide you with a free set of steak knives, but it will make Bob and I very happy)
I believe it is time for some shameless self promotion!
If you were looking for more information on the
ColdFusion 9 Object Relational Mapping Training that the venerable
Bob Silverberg and I are putting on at
cf.Objective() next April, we have just fleshed out the content section of our site to include a
detailed outline of the topics that we will be covering during the training program.
You'll know Bob from his blogging at
http://www.silverwareconsulting.com/, on a variety of all too clever stuff, including CF9 ORM, Git, Unit Testing, and
ValidateThis - his own open source validation framework. He's also an all-around nice guy
You'll
see that we cover a variety of topics with ColdFusion 9 ORM that will
allow you to hit the ground running once you have completed our
training, and it will be taught in a clear, step by step manner that
will leave you wondering why you ever thought the term "Object
Relational Mapper" sounded so scary and complicated.
So if you
are keen on expanding your skill-set with ColdFusion 9, and
specifically with its integrated ORM, we think this is the course for
you, and we hope to see you at cf.Objective()!
cf.Objective() Preconference Classes
In one of my favourite lines from the
Princess Bride -
[Vizzini has just cut the rope The Dread Pirate Roberts is climbing up]
Vizzini: HE DIDN'T FALL? INCONCEIVABLE.
Inigo Montoya: You keep using that word. I do not think it means what you think it means.
..and when using Transaction support with ColdFusion 9 ORM and Hibernate Sessions...It did not do what
I thought it did. It did something else.
To give some back story: As we have
discussed previously, a Hibernate Session is meant to flush at the end of a Hibernate marked Transaction.
So if we are talking to Hibernate natively (for example set up with JavaLoader), and had the following code:
mySession = sessionFactory.openSession();
foo = mySession.get("Foo", 1);
foo.setBar("fooBar");
trans = mySession.beginTransaction();
mySession.save(foo);
trans.commit();
mySession.close();
Which then translates to the following:
- Start a Hibernate Session
- Retrieve Entity 'Foo', with a key of 1, and assign it to a variable foo
- set the property 'Bar' on foo
- Start a Hibernate Transaction, which is tied to the Hibernate Session
- Tell the Hibernate Session to save() our Entity foo
- Commit our Hibernate Transaction, which will flush the Hibernate Session
- Close the Hibernate Session
If we attempt to do the same thing in ColdFusion, it may look something like this:
foo = EntityLoad("Foo", 1, true);
foo.setBar("fooBar");
transation {
EntitySave(foo);
}
Which you would think would work exactly the same as above. Except or one small thing - It Doesn't.
If you read the
ColdFusion Documentation on ORM Transactions, it states:
When <cftransaction> begins, any existing
ORM session is flushed and closed, and a new ORM session is created.
The <cftransaction> can be committed or rolled
back using appropriate ColdFusion tags in <cftransaction>.
When the transaction ends and has not been committed or rolled back
explicitly, it is automatically committed and the ORM session is closed.
If there is any error inside the transaction, without any exception handling,
the transaction is rolled back.
This means that the above ColdFusion code, will actually perform like so:
- Start a Hibernate Session with the ColdFusion Request
- Retrieve Entity 'Foo', with a key of 1, and assign it to a variable foo
- set the property 'Bar' on foo
- Hit the start of the Transaction, flush the current Hibernate Session, close it, and then start another one. At this point, foo has been persisted to the database, and is now detached (and remember detached is generally bad)
- Call EntitySave() on the now detached object foo
- Commit the Hibernate Transaction, and flush the Hibernate Session
- Flush the Hibernate Session, when the ColdFusion Request Ends
Or if you want to look at it another way, it would be the equivalent of doing the following native communication with Hibernate:
mySession = sessionFactory.openSession();
foo = mySession.get("Foo", 1);
foo.setBar("fooBar");
//foo actually gets persisted to the DB at this point
mySession.flush();
mySession.close();
mySession = sessionFactory.openSession();
trans = mySession.beginTransaction();
//foo is now detached
mySession.save(foo);
trans.commit();
mySession.flush();
mySession.close();
The big question at this point, is of course -
Well, why does ColdFusion do it this way then?
There is actually a very good reason, although that reason (imho) is based on a few assumptions.
The first assumption, is that you are going to write your ORM interactions inside your transaction block, like so:
transation {
foo = EntityLoad("Foo", 1, true);
foo.setBar("fooBar");
EntitySave(foo);
}
The second assumption is that, as a developer you will leave the
ORM setting flushatrequestend set to its default of
true, and therefore a split between the Hibernate Sessions is required.
For example, if we had the following code:
transation {
foo = EntityLoad("Foo", 1, true);
foo.setBar("fooBar");
myMethodHereThrowsAnException();
EntitySave(foo);
}
If
we didn't have separate Hibernate Sessions, or at least clearing of the
Session after Hibernate Transaction rollback, , the following would
occur:
- Start a Hibernate Session with the ColdFusion Request
- Start the Hibernate Transaction
- Retrieve Entity 'Foo', with a key of 1, and assign it to a variable foo
- set the property 'Bar' on foo
- Throw an exception, which causes the Transaction to rollback
- The request ends, the Hibernate Session is flushed, and foo gets saved back to the database, since it is still available in the Hibernate Session.
Which is very much against the nature of a rolled-back transaction.
Instead, what actually happens is:
- Start a Hibernate Session with the ColdFusion Request
- Start the Hibernate Transaction, which closed the above Session, and starts a new one
- Retrieve Entity 'Foo', with a key of 1, and assign it to a variable foo
- set the property 'Bar' on foo
- Throw an exception, which causes the Transaction to rollback
- The Transaction end closes the current Hibernate Session, and creates a new one
- The request ends, the Hibernate Session is flushed, and nothing gets saved to the database, as there is nothing in the current Hibernate Session
Given the above assumptions, this is much better, since we want to have the Transaction actually rollback.
That
all being said, what happens to those of us that want to work, or have
an architecture that inclined towards our previous example:
foo = EntityLoad("Foo", 1, true);
foo.setBar("fooBar");
transation {
EntitySave(foo);
}
I.e.
We want to have our Transaction interact with the current Hibernate
Transaction, and not flush at the end of the ColdFusion request, which,
I find to be the solution I prefer for when building applications with
Hibernate.
What can we do then?
There are two things that need to be done, if we want to do this kinda of approach. The first is, to set the ORM setting
flushatrequestend
to false. This means that the Hibernate session won't flush at the end
of the ColdFusion request, and we have complete control over when it
does flush.
To make sure the Transaction interacts with the current request, we can't use <cftransaction> or a transaction {} block, we have to natively interact with the Hibernate Session directly, which is quite straight forward.
foo = EntityLoad("Foo", 1, true);
foo.setBar("fooBar");
trans = getORMSession().beginTransaction();
try
{
EntitySave(foo);
trans.commit();
}
catch(Any exc)
{
trans.rollback();
rethrow;
}
We have to do the extra work to manage the
commit and/or
rollback of the Transaction manually, but this something that could be implemented with a UDF or using Aspect Oriented Programming for reuse.
Now I can use Transactions
to demarcate when I want the Hibernate Session to be flushed, and I'm
not tied to wrapping my entire Entity operations inside a <cftransaction> block.
This is something I really like about how Adobe implemented the ORM integration. If I don't particularly
like the way an individual aspect has been implemented, I have direct
access to the underlying engine, and can therefore interact with it in
whatever way that suits the way I develop and thearchitecture of my application.
Now Transactions mean what I think they mean.
UPDATE 28/10/01:
I just wanted to add a small conclusion to this post, that sums what I am trying to get across simply.
Basically what this all boils down to is: If you are using <cftransaction> or a transaction{} block, make sure you are wrapping them around the entire operation, not just the EntitySave() portion at the end, as otherwise bad things can happen.
If you want to just wrap transactions around the EntitySave() part of your code, you will need to use native Hibernate Transactions like I have done above.
Hopefully that boils the large post above into a nice tasty nugget of knowledge that is far easier to swallow.
I'm a little behind the times on this blog post - but I am presenting and also teaching a course at
cf.Objective() this year.
I will be presenting 2 sessions at the conference:
Dependency Injection Redefined - ColdSpring 2.0: Narwhal
ColdSpring
2.0, codenamed 'Narwhal', is a project that has been going on a little
'behind the scenes', except for the occasional tweet from either
myself, or
Chris Scott.
In
this presentation, we'll look at some of the motivations behind the
complete rewrite of ColdSpring for the 2.0 version, and some of the new
features that will be available that should make dependency injection
easier, and way more flexible than ever before.
I've had in my
to-do list a reminder to write a long blog post on what is going on
with Narwhal, which I should write at some point soon, so you have a
good idea what to look out for. That being said, Narwhal is taking
shape nicely, and I think is going to be a very powerful addition to
the ColdFusion framework landscape.
Advanced Java & ColdFusion Integration with JavaLoader 1.0
This
talk will focus on the new features of JavaLoader 1.0, and how you can
use them to integrate Java with your ColdFusion platform in some new
and exciting ways. If you are interested in JavaLoader 1.0, check out
this
previous blog post, it gives you a good run down on the new features it brings to the table.
We
will also be investigating some of more common 'gotchas' with Java and
ColdFusion integration, especially around ClassLoader issues, which
should be useful for all involved.
Bob Silverberg and I are also going to be a teaching a 2 day training course, just before cf.Objective():
This
will be as very hands on session, where Bob and I will be going through
building an application using ColdFuson 9's Object Relational Mapper
from beginning to end. This will include many best practices,
discussions as well as theory about how the underlying Hibernate engine
works with ColdFusion. No ORM/Hibernate experience necessary.
More detail on the course can be found on
our website, and also on the
preconference page, where you can also check out all the other fantastic training content.
Don't
forget that the early bird pricing for both conference tickets, and
training finishes on the 29th of January, which is coming up soon!
cf.Objective() is looking to be a great conference. Hope to see you there!
The interview I did with Dzone at the Adobe MAX conference is now online!
In the interview we talk about a variety of topics, mainly centred about how and why to integrate ColdFusion and Java together, but also touching on things like Object Relational Mappers, including ColdFusion 9's integration with Hibernate.
Hope you like it, and please give it a nice 'up' on Dzone.
A question I've seen show up quite often in Hibernate forums is 'How do
I get Hibernate to generate UTF-8 Compliant Tables and/or columns when
it generates my database tables when using mySQL?'
It's
actually, quite easy to do, but often (myself included), people end up
looking deep into Hibernate for some mysterious setting.
In
reality, it's just a question of setting up your database correctly.
The first thing you need to do is set your database to use UTF-8
character coding as its default, like so, when creating it:
create database my_new_database default CHARACTER SET = utf8 default COLLATE = utf8_general_ci;
This will ensure all tables that are created by ColdFusion's ORM are created with a UTF-8 character set.
That being said, I have seen issues when data is being sent up to MySQL in UTF-8 form getting corrupted. To ensure the database drivers use the correct character set as well we add useUnicode=true&characterEncoding=UTF-8 to our MySQL connection string in the ColdFusion Admin under the datasource's Advanced Settings.
There you go! UTF-8 support in mySQL with ColdFusion ORM!
This is something I just ran into with ColdFusion 9's ORM implementation, that thankfully Rupesh got back to me very fast on, otherwise, I probably would have been confused for quite a while.
I
needed to overwrite a get and set value in a CFC I was creating. We
will say for arguments sake I was hashing a password on a User object
(even though I was doing something completely different), like so:
component output="false" persistent="true"
{
property name="userid" fieldtype="id" ormtype="int" generator="identity" hint="primary key";
property name="name" type="string" notnull="true" length="200" hint="The user's name";
property name="password" type="string" length="200" hint="The user's password";
public void function setPassword(String password)
{
instance.password = hash(arguments.password);
}
public String function getPassword()
{
return instance.password;
}
}
This is how I would usually have written my Components, storing my data in variables.instance, so they wouldn't overlap with any of the data stored in my variables scope.
What I couldn't understand was, my password would keep getting stored in my database as NULL, even though I had explicitly set it.
Thankfully Rupesh explained that when the ColdFusion ORM retrieves and populates data in an ORM managed CFC, it directly accesses the variables scope in the CFC. So in the above instance, the ORM was finding no data for the password, and interpreting that to mean that I wanted to insert that as NULL into my database.
Therefore, to get my code to work was to store my data in the variables scope, like so:
component output="false" persistent="true"
{
property name="userid" fieldtype="id" ormtype="int" generator="identity" hint="primary key";
property name="name" type="string" notnull="true" length="200" hint="The user's name";
property name="password" type="string" length="200" hint="The user's password";
public void function setPassword(String password)
{
variables.password = hash(arguments.password);
}
public String function getPassword()
{
return variables.password;
}
}
This way the ORM can find exactly what it is looking for, and I can manipulate my data the way I want.
The fact that the ORM integration works this way is actually a very good thing, as it means that my password above doesn't get hash'd
over and over again, but it is something we all should be aware of when
you want to overwrite the implicit get and set functions that get
generated by <cfproperty> declarations.
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.
My apologies if this is a little belated, but here is an update to the sample code that was posted with my DevNet article Introducing ORM in Adobe ColdFusion 9 beta.
Unfortunately several things changed in CF9 between the time that I wrote the article and when the public beta came out.
The major differences between the sample on Adobe.com and this one are:
- The datatype attribute on cfproperty was changed to ormtype.
- Disable Implicit UDF Registration is now turned on by default, so any implicit setter/getter inside a CFC has to be prefixed by 'this.' for them to function.
I will endeavour to keep this download up to date as new versions of ColdFusion 9 are release.
coldfusion9_orm_sample.zip
Hopefully with my previous article, I've got you understanding how Hibernate Sessions
work behind the scenes of ColdFusion. However, there is one statement
I made that requires further clarification before we go any further.
I wrote:
"This means that changes to your objects will also not be persisted to your database, until the end of your ColdFusion request, as that is when the Hibernate Session is closed."
Technically this isn't exactly true, but it was a concept that confused me a lot
when first starting with Hibernate, so I wanted to get people thinking
in the right direction without complicating things too much.
The Problem
For example, a situation like this made me scratch my head for a long time when I first started working with Hibernate:
We have 5 Musicians in our database, and we want to delete one, and then re-list all of them again.
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
<p>We are now deleting!</p>
<cfset musician = entityLoad("Musician", 3, true)>
<cfset EntityDelete(musician)>
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
And we would see a result like:
Tom, Jerry, Ben, Bob, Richard
We are now deleting!
Tom, Jerry, Ben, Bob, Richard
This shows me exactly the same data we before and after my
EntityDelete(). Wait... what? Didn't I delete Musician Number 3?
The fact of the matter is, yes, we deleted Musician #3, except since the
Hibernate Session hasn't been
flushed yet, the data hasn't been persisted to the database. Therefore, when we run out HQL query, we see all the Musicians!
Hibernate Session Flushing
Before we go directly into how to solve this problem, what does the
Hibernate documentation say about when
Session Flushing actually occurs?
This process, called
flush, occurs by default at the following
points:
-
before some query executions
-
from org.hibernate.Transaction.commit()
-
from Session.flush()
So what does that actually mean in terms of ColdFusion?
- Before some query executions - So at some arbitrary points during the Hibernate Session life cycle it may flush. Obviously not something we can rely upon.
- At the end of a Transaction. So if we our code in a <cftransaction> block, it should flush at the end of that transaction, and therefore the changes to our objects will be persisted in the database.
- When we call ORMFlush(), or at the end of a Session when it is called implicitly.
Therefore, if we wrap the relevant code in or Transaction, like so:
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
<cftransaction>
<p>We are now deleting!</p>
<cfset musician = entityLoad("Musician", 3, true)>
<cfset EntityDelete(musician)>
</cftransaction>
<!--- Session should be flushed now --->
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
We should get a result of:
Tom, Jerry, Ben, Bob, Richard
We are now deleting!
Tom, Jerry, Bob, Richard
Alternatively, we could also do:
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
<p>We are now deleting!</p>
<cfset musician = entityLoad("Musician", 3, true)>
<cfset ORMFlush()>
<!--- Session should be flushed now --->
<cfdump var="#ormExecuteQuery('from Musician')#" label="query">
And we would also see the same results.
So as you can see it is not exactly
true that your object data will only get persisted at the end of you
request, as you actually have a lot of control over exactly when the Session gets flushed.
It is important to note that 9/10 times you probably won't care when the Session gets flushed
and the changes to your objects actually get persisted in the
database. However, there will be instances in which you will care, and
your code depends upon having your data persisted in your database. You
now have the tools to manage your persistence life cycle and know what
is going to happen when.
Now that ColdFusion 9 is in public beta, I wanted to write some
articles explaining how Hibernate is actually working under the hood of
the nice ORM integration we now have. Like all powerful tools, they
can do a great many things - but only if you really understand how they
work. Otherwise, they are potential for disaster if you don't know
what you are doing.
Hibernate Sessions
The first thing we need to cover is the concept of
Hibernate Sessions,
which has been nicely hidden away from ColdFusion developers by the
Adobe Engineering team, but is still something that you need to really
understand to be able to leverage Hibernate in CF9 properly. (At least
IMHO).
First things first - don't confuse this with ColdFusion Sessions, or
any sort of browser session. Hibernate is built as a generic solution
to ORM, so it has no specific ties to web state. A
Hibernate Session
is a lightweight object that is used to delineate when Hibernate is
meant to start working, and when it is meant to stop, which is often
referred to as a
unit of work. This may span several transactions and/or INSERT, UPDATE, DELETE and SELECT statements as the Session gets used.
This is very much like the start and stop of a conversation, we say
hello at the beginning, and say goodbye at the end, and whatever we
need to discuss goes on in between, except we just tell the Session to
open() and
close(), rather than "hello" and "goodbye".
Actual Hibernate Java code would look something like this:
//open the session from the SessionFactory (which is where sessions come from)
Session session = sessionFactory.openSession();
//get fred, our caveman
Caveman fred = session.get("Caveman", 2);
//change his age to 32
fred.age(32);
//close the session, which will update fred.
session.close();
I'm not going to get into where the
SessionFactory comes from, as all you really need to know is: it's where
Hibernate Sessions come from.
So what we are doing here is:
- opening a Session, i.e. we tell Hibernate 'Hi', which starts our conversation.
- Tell Hibernate to retrieve our Caveman fred, through our session.
- We change the age of fred to 32.
- We close our session, and say 'GoodBye' to Hibernate for now.
There are several things that happen here that are worth noting at this point, even in this relatively simple piece of code.
The first is, by doing
session.get("Caveman", 2), we have effectively told our
Session to track this object, and make note of any changes that happen to it.
We have also cached a copy of
fred in the Session itself, so that if we were to attempt to retrieve
fred again, we would get the same copy. This is both for performance, and also so that the
Hibernate Session only has to deal with one object that represents the data behind
fred, which will be a fairly important concept as we continue in this article series.
Since our
Session is tracking
fred, any change we make to
fred will be persisted to the database when we close our
Session. In that way, Hibernate is quite transparent in the way it implements persistence.
It should also be noted at this point: Unless you specify otherwise,
Hibernate will only run INSERT, UPDATE and DELETE when the session is
closed, generally as a batch. This is so that, for example, multiple
changes happen to
fred during the length of our
Session,
there only needs to be one UPDATE statement when we are finished. This
is an important thing to note, as it is possible to get confused as to
why your changes to your objects aren't appearing in your database during your
Session, but do appear afterwards.
Hibernate Sessions and Web Applications
In web applications, it is usual to manage
Hibernate Sessions by
opening one up at the beginning of a HTTP request, and then closing at
the end of the request. The length of a single HTTP request is a good
size to carry on a nice conversation with Hibernate.
Strangely enough, this is
exactly what ColdFusion is doing for you behind the scenes. It starts a
Hibernate Session
when your request starts, and ties it to that particular request, and
then closes it at the end of the request. (It is quite likely that the
Session actually only gets created when first requested, but for the
sake of argument it is easier to explain it as it gets opened at the
start of the request)
From there, each of the ORM functions, EntityLoad, EntitySave, EntityDelete,
etc, all interact with that
Session that exists behind the scenes, for that request.
This means that changes to your objects will also not be persisted to your database, until the
end of your ColdFusion request, as that is when the
Hibernate Session is closed. In later articles, I'm going to cover some ways this can cause some trouble, and how to work around them.
You are actually able to flush the Session, which
forces it to persist any changes to the database right then and there,
and not wait until the end of the request, which can sometimes be very
useful.
To do this in Java, you could go:
session.flush();
But in ColdFusion you write:
ORMFlush();
UPDATE: You can read more about Session Flushing here, which outlines that there are times in which Hibernate will flush a Session in the middle of Session, however, there is often no guarantee except in specific circumstances.
It's also interesting to note you have direct access to the Hibernate Session if you so desire through ORMGetSession(), which gives you access to that request's Hibernate Session object, so you can interact with it directly.
There is much more to Hibernate Sessions and how they relate to the
objects that they manage, but this will give us a good start to build
upon for now.
You can read more about ColdFusion and Hibernate Sessions in the documentation Hibernate Session Management.
As I'm sure you have all noticed, ColdFusion 9 Public Beta is now available for download.
I am really excited about this release, and think it is going to change
the way a lot of people are going to be developing applications going
forward.
Some of my favourite new features are:
- Hibernate ORM
As we all well know, I have a special place in
my heart of Object Relational Mapping, and the inclusion of Hibernate
in ColdFusion gives me some lovely goosebumps. I've worked with
Hibernate before, and it really is a top-notch piece of software, and a
great ORM to work with. I can also honestly say that the engineers at
Adobe have done an excellent
job of providing it to ColdFusion developers in a way that (a) is quite
easy for new users to pick up and use and (b) exposes much of the
internals of Hibernate to those who are more experienced, and want to
do some more complicated things.
I wrote an article for Adobe Devnet, called Introducing ORM in Adobe ColdFusion 9 Beta,
which I think is a nice introduction to the basics of ORM, and takes
you through some simple steps of using Hibernate in Java without
getting too complicated.
Over the next few weeks I plan on writing several more blog posts
outlining Hibernate within the ColdFusion context, and giving people
more of an idea of what is going on under the hood so they can avoid
some 'gotchas' I think people will run into when starting to play with
Hibernate.
- Language Enhancements
I am really enjoying writing so much more of my application in
CFScript. Coming originally from a Java background, CFScript is a much
more natural fit for the way I like to write code, so I am very excited
to be able to do so much more of my development using CFScript, rather
than being forced to cut between CFScript and CFTags.
Oh yeah, and let's not forget things like implicit getters and setters, the new and import keywords, and nested transactions (finally!). So many good things!
- ColdFusion Builder
I could go into each and every part of ColdFusion Builder, for
which there is a fair amount, but overall I think this is an amazing
release for what is essentially a 1.0 IDE.
My favourite part of CF Builder is quite likely just the code
assistance in scripts and tags. I have a ridiculous ability to
misspell/miss-type 'arguments' on a regular basis. Now I just type
'arg', hit ctrl+space, and it enters it for me. Aaaah, no more misspellings !
So if you haven't already, grab a copy of
ColdFusion 9 Beta, and
ColdFusion Builder Beta, and enjoy!