Aliaspooryorik
ColdFusion ORM Book

Introducing the ColdFusion 9 ORM Event Handler

Usually when you build applications, you want to have some kind of logging. A classic example of this is to log when an object has been deleted (removed from the database). Thankfully ColdFusion 9 has a feature called Event Handling which is really useful when you want to log events. So how do you use it?

First of all you need to be using Persisted Entities and then enable the eventhandling orm setting in your Application.cfc like this:


/**
* I am the Application.cfc
* @output false
*/

component
{
this.name = "eventhandlerdemo";
// define the application wide datasource
this.datasource = "cfartgallery";
// enable hibernate support for this application
this.ormenabled = "true";
// create a struct of ORM settings
this.ormsettings = {};
// turn on event handling
this.ormsettings.eventhandling = true;
}

Now ColdFusion will look at your Persisted Entities for one of these methods depending on which event is happening:

  • preLoad(): This method is called before the load operation or before the data is loaded from the database.
  • postLoad(): This method is called after the load operation is complete.
  • preInsert(): This method is called just before the object is inserted.
  • postInsert(): This method is called after the insert operation is complete.
  • preUpdate(Struct oldData): This method is called just before the object is updated. A struct of old data is passed to this method to know the original state of the entity being updated.
  • postUpdate(): This method is called after the update operation is complete.
  • preDelete(): This method is called before the object is deleted.
  • postDelete(): This method is called after the delete operation is complete.

The names are self explainitory so I won't describe each one. If you want to know more read the livedocs
http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WS33331E6A-B64C-4089-8A41-AEB3A4133D59.html

Going back to my example of logging a deletion, we would need to have a CFC that includes the postDelete() method, something like this:


/**
* I am a Art Persisted Entity
* @output false
* @table ART
* @persistent true
*/

component
{
// identifier
property name="ArtID" fieldtype="id" generator="native" ;

// properties
property name="ArtName" ;
property name="Description" ;
property name="Price" ;
property name="LargeImage" ;
property name="IsSold" ;

property name="Artist" fieldtype="many-to-one" fkcolumn="ArtistID" cfc="Artist";

/**
* I am called after the delete operation is complete.
* @output false
*/

public void function postDelete()
{
// writeLog is the cfscript equililent of cflog
writeLog( type="Information", file="ormevent.log", text="Art entity #this.getArtID()# deleted" );
}
}

That was easy, but it would get a bit tedious having to add this method to each Entity. Luckily ColdFusion also has an application-wide event handler. To set this up is easy, just add in a new ormsetting in the Application.cfc so that we now have:


/**
* I am the Application.cfc
* @output false
*/

component
{
this.name = "eventhandlerdemo";
// define the application wide datasource
this.datasource = "cfartgallery";
// enable hibernate support for this application
this.ormenabled = "true";
// create a struct of ORM settings
this.ormsettings = {};
// turn on event handling
this.ormsettings.eventhandling = true;
this.ormsettings.eventhandler = "my.cfc.path.EventHandler";
}

Now we need to create the EventHandler.cfc. It is important to note that the EventHandler needs to impliment the cfide.orm.IEventHandler interface. Here is a simple one that logs all ORM events:


/**
* Application wide 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 )
{
logEvent( "preload", entity );
}

/**
* This method is called after the load operation is complete.
*/

public void function postLoad( any entity )
{
logEvent( "postload", entity );
}

/**
* This method is called just before the object is inserted.
*/

public void function preInsert( any entity )
{
logEvent( "preinsert", arguments.entity );
}

/**
* This method is called after the insert operation is complete.
*/

public void function postInsert( any entity )
{
logEvent( "postinsert", arguments.entity );
}

/**
* This method is called just before the object is updated. A struct of old data is passed to this method to know the original state of the entity being updated
*/

public void function preUpdate( any entity, Struct oldData )
{
logEvent( "preupdate", arguments.entity );
}

/**
* This method will be called after the update operation is complete.
*/

public void function postUpdate( any entity )
{
logEvent( "postupdate", arguments.entity );
}

/**
* This method is called before the object is deleted.
*/

public void function preDelete( any entity )
{
logEvent( "predelete", arguments.entity );
}

/**
* This method is called after the delete operation is complete.
*/

public void function postDelete( any entity )
{
logEvent( "postdelete", arguments.entity );
}

/**
* Writes operation details to the log.
* @output false
*/

private void function logEvent( required string event, required cfc )
{
// arguments.event : Name of the event being called.
// arguments.cfc : The cfc being acted upon.
var message = "Entity: #getMetaData( arguments.cfc ).fullname#, Event: #arguments.event#";
Trace( text=message );
WriteLog( type="event", file="ormevent.log", text=message );
}
}

There you have it, an application wide logging for orm events. This example is pretty basic, but hopefully it's given you some idea of the power of this feature.


9 comments

  1. John, great posts, thanks. I haven't had time to take a look at the ORM docs yet so your posts are providing a great introductory reference. I'll look forward to your next installment.

    Comment by Kevan Stannard – July 14, 2009
  2. Hi Kevan, glad you found the posts useful. The docs are pretty good but no substitution for a bit of trial and a lot of errors! So I thought I share my code. I haven't thought of another instalment yet :)

    Comment by John Whish – July 14, 2009
  3. It sounds like database triggers. Is there a way to tie these event handlers to a specific table, instead of application wide?

    Comment by Nick – July 14, 2009
  4. Hi Nick, if you don't set up the application wide eventhandler (this.ormsettings.eventhandler), then ColdFusion will look for the methods in each Persisted Entity, if it doesn't find it it will just carry on. In some code I've written I'm using the application wide event handler my preUpdate() method checks the entity for a property called "updated" and it finds it then it calls setUpdated( Now() ). Is that what you're thinking of doing?

    Comment by John Whish – July 14, 2009
  5. I should also add that if the method exists in both the Persisted Entity and an Event Handler, then they both are fired. The Persisted Entity method is triggered first.

    Comment by John Whish – July 14, 2009
  6. Hello John,

    I am using pretty much the same logging type system as this exept i write the log to a table called 'Audit', I create the audit object in the postInsert() event trigger, set the properties and call entitySave('Audit'). The event gets triggered and i can dump the audit object and all looks perfect but it does not get saved to the database and i have no idea why. I am really scratching my head with this one and cannot find any information. Would you have any ideas?

    Comment by Gavin Stewart – July 24, 2009
  7. Hi Gavin,

    I'm not at my dev workstation so I can't test it out but I can see that you could end up with a problem if your logging class is a persisted entity, as when you try to do anything with it you'll be firing the event handler each time. The other thing that happens with the way that hibernate is handled in CF9 is that commits only happen when the request ends, or you force it to commit by using ORMFlush() (or wrap it in a transaction) so it might be something to do with that.

    You might actually be better off with a straightforward cfquery if all you want to do is save the info in the database as using an object has no real benefit.

    Hope that helps, let me know.

    Comment by John Whish – July 24, 2009
  8. These kinds of hooks are just awesome, I've become so reliant upon them from my experience of ORM within Python for things like saving a serialized version of an object to the FS automatically along with a database insert/update.

    This morning has really been my first proper assessment of the ORM implementation within CF and I really can't praise it enough, it really does seem so very intuitive! Adobe have done an excellent job of all this.

    Rob

    Comment by Robert Rawlins – August 22, 2009
  9. Hey Rob, yeah they are pretty powerful. I agree that Adobe have done a really good job of implementing the hibernate integration. I also like that you can save/edit the hbm xml files to get even more control over hibernate.

    Comment by John Whish – August 24, 2009

Leave a comment

If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)

Please note: If you haven't commented before, then your comments will be moderated before they are displayed.

Please subscribe me to any further comments
 

Search

Wish List

Found something helpful & want to say ’thanks‘? Then visit my Amazon Wish List :)

Categories

Recent Posts