Self Validating Entities with CF9
My style of writing code is heavily influenced by Paul Marcotte's Metro and Bob Silverberg's blog. Both of those guys use rich business objects which can validate themselves. Essential this is a technique where you can populate an object with invalid data (from a form for instance). You then ask the object to check if the data it holds is valid. If it isn't valid you don't save the changes, but you can still use the object to re-populate a form.
The default settings for ColdFusion 9's ORM capabilities mean that you can't write code this way. The problem is that ColdFusion will automatically save any changes made to a persisted object at the end of the request regardless of whether you call EntitySave or not.
Thankfully, you can override this behaviour by adding this line to Application.cfc (thanks to Bob Silverberg and Mark Mandel for pointing this out to me)
this.ormsettings.flushatrequestend = false;
By adding this line of code, ColdFusion won't save any changes you make to an entity at the end of the request. It also won't save any changes even if you call EntitySave( myObject ), unless you call ORMFlush() before the end of the request, typically this will probably be straight after the EntitySave.
I've created and updated version of the port of Mark Mandel's tBlog sample application I did to include some basic validation (via an Abstract class) which you can download here.
It should be noted that ColdFusion 9 is set up to only run the SQL statements at the end of the request to improve performance, so changing the behaviour could have an impact on your application's performance.
If you are interested in a much more sophisticated validation, then you should check out Bob silverberg's ValidateThis! framework.
- Posted in:
- ColdFusion
- OOP
- Hibernate


Comment by Dan Vega – August 24, 2009
Comment by david buhler – August 24, 2009
Comment by John Whish – August 24, 2009
Comment by Dan Vega – August 24, 2009
Comment by John Whish – August 24, 2009
I also take this approach. What are your thoughts about using the event model and preUpdate() method to call the validate() method?
I guess you still have the bunk data in the object, and it might be a little janky/difficult to "revert" object to the previous state.
Liking your blog and LOVING CF9... it is just TOOOOO good.
Comment by John Allen – August 24, 2009
Comment by Bob Silverberg – August 25, 2009
Comment by Dan Vega – August 25, 2009
Comment by Ben Nadel – August 25, 2009
Comment by John Whish – August 25, 2009
This code is from a few betas back but (hopefully!) should work
/**
* Event handler for ORM operations
* @output false
* @implements cfide.orm.IEventHandler
*/
component
{
/**
* This method is called before the data is loaded from the DB.
*/
public void function preLoad( any entity )
{
var Validator = application.beanFactory.getBean( "Validator" );
arguments.entity.setValidator( Validator );
}
....
}
It does break encapsulation by accessing the application scope, and I'm sure this can be improved but I hope it helps.
Comment by John Whish – August 25, 2009
this.ormsettings.eventHandler = "model.interceptor.EventHandler";
Comment by John Whish – August 25, 2009
Hmm, interesting idea, I think there are a few things that could trip you up though. The preUpdate method may only fire at the end of the request unless you've set the flushatrequestend to false or called ORMFlush(). Also, the preUpdate method doesn't return anything so you'd still have to go through the steps of checking if it was valid. You might as well get the obj.hasErrors() method to call obj.validate internally.
Let me know if you do try it out, as I'd be interested to hear how you get on.
Comment by John Whish – August 25, 2009
Comment by Dan Vega – August 25, 2009
wheelersoftware.com/articles/hibernate-validator.html
Comment by Qasim Rasheed – August 26, 2009
Comment by John Whish – August 26, 2009
Comment by John Whish – August 26, 2009
this.ormsettings.flushatrequestend = false;
If you make changes to an entity, and you do not use EntitySave(), but still call ORMFlush(), your data is still persisted to the DB persisted.
My point is that while you might not explicitly call ORMFlush() in the current file where you're working with an entity, it might be called somewhere else further along in the request, thus unintentionally persisting your entity to the db.
There is a way around this. If you call ORMCloseSession(), then change a property (i.e. setFirstName('Dutch'), the only way the object would be persisted is to use EntitySave() *and* ORMFlush().
Now, it makes me wonder. Let's say I do validations as part of an isValid method or even as part of preInsert() or preUpdate(). Can I call ORMSessionClose() from within the entity itself? If so, what are the implications there?
Regardless, it sounds like the only way to prevent an object from being automatically saved during a flush, regardless of what this.ormsettings.flushatrequestend is set to, is to call ORMCloseSession() after each EntityLoad().
Comment by Dutch Rapley – October 23, 2009
You can evict an entity from the session by calling ormGetSession().evict(myObject). You can also get total control by wrapping all of your entity manipulation in a transaction. Then, if you decide that you don't want the changes committed to the database, you can call transactionRollback(). That latter approach is probably going to be my preferred method.
Comment by Bob Silverberg – October 23, 2009
Thanks for the clarification there. I'm a Hibernate noob. When it says "persistence framework," that's what it is, it persists, no matter what, unless you tell it not to.
I've used Reactor, Transfer, and even ActiveRecord (to some extent in Rails). I've dug into the CF documentation. While it's good, it leaves some questions to be answered.
I think there's a few gotchas in there that some ORM noobs won't understand, feel they are shortcomings, and will eventually abandon ORM support in CF. To really understand how ORM support works in CF, I feel it's important to understand Hibernate (which I'll be doing this weekend).
I agree with you on using transactions to work around some shortcomings (nice to see script support there) and was going to mention it in my last comment.
I knew the hibernate session object is available, but just don't know what's there yet. There is a CF equiv to evict with ORMEvictEntity(), which I guess can always be used in a manner similar to the following
if (!user.isValid()) { ORMEvictEntity( "User", user.getId() ); }
My one question there is - after you "evict" an object, can you change properties and pass it to EntitySave()?
Comment by Dutch Rapley – October 23, 2009
sorry, I meant to prefix that last comment with your name and not @John, it's Friday afternoon - what can I say?
Comment by Dutch Rapley – October 23, 2009
1. ORMEvictEntity() will not evict the object from the Hibernate session - it is only used to evict objects from the secondary cache. Yes, this is yet another thing that is confusing about CF/Hibernate.
2. I would assume that after evicting an object (using evict(), not ORMEvictEntity) you can change properties and then call EntitySave() to place it back into the session. There's one way to know for sure, right?
Also, I totally agree that there is a lot that people are going to have to learn/understand about Hibernate sessions in order to work with CF ORM. I gave a presentation on it at CFinNC, and will be presenting it again to the Online ColdFusion Meetup group. I also hope to publish a number of blog posts on the subject.
Comment by Bob Silverberg – October 23, 2009
As usual, nothing but pearls of wisdom from you!
I'm looking forward to your preso on the Online Meetup, and wish I could have made it to CFinNC.
And yes, I was wondering about the primary and secondary cache.
Comment by Dutch Rapley – October 23, 2009
Comment by John Whish – October 24, 2009
I'm Application.cfc, I'm telling the application that I will handle flushing with
<cfset this.ormsettings.flushatrequestend = false />
Let's consider the following snippet:
<cfset var user = entityLoad("User", 123, "true")/>
<cfset user.setFirstName('John')/>
<cftransaction>
</cftransaction>
We all know that a transaction closes previous sessions and starts a new one. If I have an entity that I've loaded and updated a property on, then begin a transaction, the session the user entity belongs to is flushed and the user entity is actually persisted to the database.
If I take out the <cftransaction> tags, the user entity is never persisited (which behaves as expected).
For the above example, I wouldd probably have UserService.cfc with create() and update() functions that would take a structure (i.e. form) as a parameter. Load the user entity and populate/modify it from within the transaction.
I can see where EntityReload() may be a good choice on some occasions where you're only wanting to update a single property and not wanting to add yet another method to UserService.cfc if you don't have to.
Comment by Dutch Rapley – October 26, 2009
Comment by John Whish – October 27, 2009
Here's a preliminary, it should work. The insert() function is part of UserService.cfc
public struct function insert(Struct user) {
var results = {};
results['success'] = false;
transaction {
try {
// instantiate new user object
var newUser = EntityNew("User");
//set user properties here
newUser.setFirstName(user.firstName);
newUser.setLastName(user.lastName);
// validate user
if(!newUser.isValid()) {
// user is not valid - do not save'
transactionRollback();
results['errorMsg'] = 'The data is invalid. Please review and correct errors below.';
results['errors'] = newUser.errors();
}
else {
// save user
EntitySave(newUser);
transactionCommit();
results['success'] = true;
}
} catch(Any excp) {
transactionRollback();
results['errorMsg'] = 'There was an error saving the data. Please try again';
}
} // end transaction
return results;
}
Comment by Dutch Rapley – October 27, 2009
Comment by John Whish – October 27, 2009
Another comment, when you create a new entity, via EntityNew for example, no matter where or when you do it it will never be persisted to the database unless you call EntitySave on it. So you don't actually need the rollback in your example when the validation fails. Simply not calling EntitySave will ensure that the object is not persisted to the database.
Comment by Bob Silverberg – October 27, 2009
Thanks for the tips, they're helpful and greatly appreciated as I work towards leveraging Hibernate in CF. This is some good stuff! You guys should really consider writing a series of articles on CFHibernate best practices. I've read through Chapter 8 on ORM in the dev guide a couple of times now. While it does a great job at introducing the power of Hibernate in CF and covering basic concepts, I felt there were a ton of gaps in understanding how to leverage it effectively.
Thanks again!
Comment by Dutch Rapley – October 27, 2009