Monday, February 21, 2011

ColdFusion ORM: Computed Property Gotcha

I was fighting an ORM issue today (well, several) and I came across an issue that is more of a gotcha than a real problem.  ColdFusion ORM supports the idea of computed properties.  I was using a computed property to give me the sum of all approved amounts in a child collection like so:

<cfcomponent displayname="Parent" persistent="true">
...
  <cfproperty name="totalFundingApproved" formula="select sum(ch.approved_amount) from child ch where ch.parent_id = id" />
...
</cfcomponent>

I'm using Ajax to persist records, and then I am returning the parent object to my callback function so I can then parse the object and update the UI with the new values.  The super-simplified service method would like like this:

var parent = entityLoadByPK('Parent', arguments.id);

child = parent.getChild();
child.setTotalFundingApproved(arguments.approvedAmount);
entitySave(parent);

return parent;

Now, it would be silly for ColdFusion to reload the values on the calculated property every time we ask for it.  The problem is, I need it to be updated when the child is updated!  There are a few things we could do to get the functionality we want.  First, we can move the computed property out into a method call:

<cffunction name="getTotalFundingApproved" access="public" returntype="query" output="false">
  <cfset var q = '' />
  
  <cfquery name="q" datasource="#dsn#">
  select sum(ch.approved_amount) from child ch where ch.parent_id = #getID()#
  </cfquery>
  
  <cfreturn q />
</cffunction>

The problem with that in my specific case is that I need to be able to sort on that property, and no matter how hard you try, you just can't sort on a method ;)  Another option is to create a method like getTotalFundingApproved().  This seems kludgy to me.  With proper documentation it could work, but you could have two possible values for the same property at any given time depending on the child collection's state.

I ended up reloading the object and returning that copy.  To ensure a fresh read I'm using the ORMClearSession() method:

ormclearsession();
var parent = entityLoadByPK('Parent', id);
return parent;

This works, but I'd love to see a way to trigger a reload of the computed property that I could use in the preUpdate() or preInsert() event handlers.

Update (2/22/2011): I was looking through the new functions in CF 9 today and found entityReload(). This could be used instead of ormClearSession(). ormClearSession() worked for me here since no other objects were in the session at the time. In other cases, entityReload() just makes more sense.

Thursday, February 17, 2011

ColdFusion ORM: Answering Some Event Handling Quandaries

I'm working with ColdFusion ORM event handlers and had a few questions I couldn't find answers to so I figured I'd write some tests.  I wanted to determine:
  1. Does preUpdate fire if no properties have changed?
  2. Does postUpdate fire if no properties have changed?
  3. What is the order of operations when using both a CFC event handler and object event handler method?
I figured the best way to test this would be to persist a bunch of tick counts during each event handlers process.  The persistent CFC is as follows:

<cfcomponent displayname="EventRecorder" persistent="true">
  
  <!--- properties --->
  <cfproperty name="id" fieldtype="id" type="numeric" unsavedvalue="-1" generator="increment" />
  <cfproperty name="preInsertTick" type="int" />
  <cfproperty name="postInsertTick" type="int" />
  <cfproperty name="preUpdateTick" type="int" />
  <cfproperty name="postUpdateTick" type="int" />
  <cfproperty name="sysPreInsertTick" type="int" />
  <cfproperty name="sysPostInsertTick" type="int" />
  <cfproperty name="sysPreUpdateTick" type="int" />
  <cfproperty name="sysPostUpdateTick" type="int" />
  
  <cffunction name="init" returntype="EventRecorder" output="false">
    <cfset setID(-1) />
    <cfreturn this />
  </cffunction>
  
  <cffunction name="preInsert" returntype="void" output="false">
    <cfset setPreInsertTick(getTickCount()) />
  </cffunction>
  
  <cffunction name="postInsert" returntype="void" output="false">
    <cfset setPostInsertTick(getTickCount()) />
  </cffunction>
  
  <cffunction name="preUpdate" returntype="void" output="false">
    <cfset setPreUpdateTick(getTickCount()) />
  </cffunction>
  
  <cffunction name="postUpdate" returntype="void" output="false">
    <cfset setPostUpdateTick(getTickCount()) />
  </cffunction>
  
</cfcomponent>

You'll notice that right before and right after each insert and update the current timer is pushed into the EventRecorder object. The same is done in our application event handler CFC:

component implements="cfide.orm.IEventHandler" {
  
  public void function preLoad(any entity) {}
  public void function postLoad(any entity) {}
  
  public void function preInsert (any entity) {
    entity.setSysPreInsertTick(getTickCount());
  }
  
  public void function postInsert (any entity) {
    entity.setSysPostInsertTick(getTickCount());
  }
  
  public void function preUpdate (any entity, struct oldData) {
    entity.setSysPreUpdateTick(getTickCount());
  }
  
  public void function postUpdate (any entity) {
    entity.setSysPostUpdateTick(getTickCount());
  }
  
  public void function preDelete(any entity) {}
  public void function postDelete(any entity) {}
  
}

The only requirement when creating an event handler CFC is that it must implement cfide.orm.IEventHandler.  While we only need 4 methods, remember that we need to implement each method with the appropriate signatures defined in the interface.  Don't forget that we need to tell ColdFusion that we want to enable event handler as well as define the event handler CFC:

this.ormSettings.eventHandling = true;
this.ormSettings.eventHandler = 'blogexamples.hql.cfc.EventHandler';

The cfm file that kicks it all off looks like this:

<cfscript>
  o = entityNew('EventRecorder');
  entitySave(o);
  ormFlush();
  
  entitySave(o);
  ormFlush();
</cfscript>

When the above was executed we see the following results:

  1. Does preUpdate fire if no properties have changed? Yes
  2. Does postUpdate fire if no properties have changed? Yes
  3. What is the order of operations when using both a CFC event handler and object event handler method? They appear to fire at the exact same time
I'm not happy with the results for #3.  Let's rerun the test with a delay set into both preUpdate methods by adding:

sleep(1000);


Eureka!  The class's event handler method fires before the application event handler CFC.

Wednesday, February 16, 2011

jQuery: remove() Performance Issues Tested

I was reading the comments on the jquery .remove() function, and came across this:
If you've got a container with a lot of child elements you had better empty the container element before removing it. Due to the way jQuery handles remove vs empty, empty is thousands of times faster, at least in this situation.

So do this:
$('#container').empty().remove();

…instead of this:
$('#container').remove();
So for each time I've used remove() since then, I've been doing empty().remove(), as it is "thousands of times faster".  Well, I've been meaning to test this so here we go.  I created a simple template for testing.  The idea was to created a table with 5000 rows with elements nested 3 levels deep.  As expected, jQuery drops these things from the DOM pretty quickly.  Here is the template (I'm looping using ColdFusion, but you could recreate this in any other language:

<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<script type="text/javascript">
  function go (){
    var start = new Date();
    $('#tbltest').empty().remove();
    var end = new Date();
    alert(end - start);
  }
</script>
<button onclick="go();">Remove</button>
<table id="tbltest">
  <tbody>
    <cfloop from="1" to="5000" index="i">
      <tr>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
        <td><a href="javascript:void(0);"><span>Text</span></a></td>
      </tr>
    </cfloop>
  </tbody>
</table>

Note that I'm using jQuery 1.4.2.  The plan was to allow the page to load fully and then trigger the remove() or empty().remove().

First, when running this 10 times using remove():
  1. 869ms
  2. 906ms
  3. 907ms
  4. 919ms
  5. 889ms
  6. 912ms
  7. 948ms
  8. 1055ms
  9. 933ms
  10. 900ms
Then, 10 times using empty().remove():
  1. 941ms
  2. 897ms
  3. 881ms
  4. 923ms
  5. 954ms
  6. 951ms
  7. 1015ms
  8. 968ms
  9. 939ms
  10. 962ms
When averaged the remove() method took 924ms and empty().remove() averaged 943ms.  Not the results I was expecting to see, but I'm not ready to call this busted just yet. I had a hunch that the issue was with deeply nested objects and the way that jQuery traverses the DOM.  I changed the code to have several layers of nested tables as such:

<script type="text/javascript" src="jquery-1.4.2.min.js"></script>
<script type="text/javascript">
  function go (){
    var start = new Date();
    $('#tbltest').empty().remove();
    var end = new Date();
    alert(end - start);
  }
</script>
<button onclick="go();">Remove</button>
<table id="tbltest">
  <tbody>
    <cfloop from="1" to="2000" index="i">
      <tr>
        <td>
          <table>
          <tbody>
            <tr>
              <td>
                <table>
                <tbody>
                  <tr>
                    <td>
                      <table>
                      <tbody>
                        <tr>
                          <td>
                          <span><span><span><span><span><span><a href="javascript:void(0);"><span>Text</span></a></span></span></span></span></span></span>
                          </td>
                          <td>
                          <span><span><span><span><span><span><a href="javascript:void(0);"><span>Text</span></a></span></span></span></span></span></span>
                          </td>
                          <td>
                          <span><span><span><span><span><span><a href="javascript:void(0);"><span>Text</span></a></span></span></span></span></span></span>
                          </td>
                          <td>
                          <span><span><span><span><span><span><a href="javascript:void(0);"><span>Text</span></a></span></span></span></span></span></span>
                          </td>
                          <td>
                          <span><span><span><span><span><span><a href="javascript:void(0);"><span>Text</span></a></span></span></span></span></span></span>
                          </td>
                        </tr>
                      </tbody>
                      </table>
                    </td>
                  </tr>
                </tbody>
                </table>
              </td>
            </tr>
          </tbody>
          </table>
        </td>
      </tr>
      </tr>
    </cfloop>
  </tbody>
</table>

remove():
  1. 769ms
  2. 812ms
  3. 767ms
  4. 744ms
  5. 822ms
  6. 778ms
  7. 766ms
  8. 822ms
  9. 1018ms
  10. 778ms
empty().remove():
  1. 997ms
  2. 782ms
  3. 792ms
  4. 978ms
  5. 1062ms
  6. 765ms
  7. 728ms
  8. 759ms
  9. 1003ms
  10. 834ms
Averaged, remove() took 808ms and empty().remove() took 772ms.  This result made sense and was more the expectation I expected to see.  Perhaps over a large set of elements it would actually matter.  I'd say it isn't worth worrying about, but at least my curiosity has been quelled.

A few things to call out here are that I am using jQuery 1.4.2 on Firefox 3.6.13.  This matters as different results could be given in IE with it's JS engine, as well as the comment on the jQuery remove() page may have been referring to another version, although I did choose 1.4.2 to mimic the date of the post.

    Tuesday, February 15, 2011

    ColdFusion ORM: Filtering on Composite Objects

    I've had the need a few times lately to return a subset of ColdFusion ORM objects. In this case EntityLoad() might not always be the way to go. This post will hopefully help those new to ORM determine the appropriate action.

    First, lets chat about entityLoad(). EntityLoad() looks like the perfect way to query for a collection of objects.  Given an ORM object of type Pig, consider the following definition:
    <cfcomponent displayname="Pig" persistent="true">
        
        <!--- properties --->
        <cfproperty name="id" fieldtype="id" type="numeric" unsavedvalue="-1" />
        <cfproperty name="color" type="string" />
        <cfproperty name="age" type="numeric" />
        
        <cfproperty name="breed"
              fieldtype="many-to-one" cfc="Breed"
              fkcolumn="breedid" lazy="true" />
        
    </cfcomponent>
    

    It's easy to use entityLoad() to load up all pigs that are pink:
    entityLoad('Pig', {color='Pink'});
    

    Now notice that each Pig has a breed.  Let's say we want to get all pigs of breed 'Yorkshire'.  Here's where entityLoad() falls on its face.  If we tried to use entityLoad() it would look like this:
    entityLoad('Pig', {breedid=1})
    

    We get the following error:
    Property BREEDID not present in the entity. 
    

    This is because entityLoad() is a convenience function that only works against defined properties.  To entityLoad(), breedid is not a property.  Instead, Breed is the property, being of type Breed.  To get around this we can use HQL instead.  HQL is very easy to understand if you know SQL, and very easy to execute.  Please refer to my last blog entry ColdFusion ORM: Using HQL for a primer on HQL with ColdFusion ORM. For the examples below we will be using ORMExecuteQuery() as opposed to <cfquery>

    Filtering the objects to get all Pigs which are Yorkshires, we can do this:
    ormExecuteQuery("from Pig where breedid = 1");
    

    ormExecuteQuery() returns an array of matching objects.  This is the case unless we are using aggregate functions like sum() or count().  In that case you get an array of arrays.  See the last section of ColdFusion ORM: Using HQL for a primer on HQL for more details on how aggregate function results are returned.

    Instead of querying on a property of Pig itself, we can even query against properties of the composite object Breed:
    ormExecuteQuery("from Pig pig where pig.breed.name = 'Yorkshire'");
    

    One more thing, HQL also allows us to use alternate operators.  We can then use ORMExecuteQuery() to do things like:
    ormExecuteQuery("from Pig where age < 2");
    

    ColdFusion ORM: Using HQL

    It is important to note in ColdFusion ORM that functions like entityLoadByPK() and entityLoad() are merely convenience functions. Each (so far as I know) simply hides the underlying HQL (Hibernate Query Language). HQL is much like SQL, aside from the fact that not all SQL aggregates and functions are supported as well as we can reference object relational mappings in our HQL as Hibernate understands how to translate them into the appropriate SQL based on our datasource type.

    There are two simple ways to execute HQL in a ColdFusion application. The first is with ORMExecuteQuery(). The second is to use a cfquery block with dbtype="hql". For this set of examples we will use ORMExecuteQuery().

    We can do a few cool things with HQL. For one, since HQL knows how our objects are mapped we can use the shortcut 'id' to reference a property. Given an ORM object with the cfproperty id definition:
    <cfproperty name="chicken_id" fieldtype="id" type="numeric" unsavedvalue="-1" />
    

    The following HQL statements are considered synonymous:
    from Chicken where id = 1;
    from Chicken where chicken_id = 1;
    

    Please note that the object name 'Chicken' is case sensitive. If you mistype the case here you will receive the error 'org.hibernate.hql.ast.QuerySyntaxException: Chicken is not mapped [ from chicken where chicken_id = 1 ]'.

    Another cool thing, we can query on composite objects as well. Given Rooster in a one-to-many relationship with Hens, we can get all roosters who are associated with a red hen like this:
    from Rooster rooster where rooster.hen.color = 'red';
    

    Note that the query above uses an inner join by default. Other join types are supported by explicitly specifying the join just like in standard SQL.

    Just like in SQL, we can also use 'order by' to sort the resulting collection of ORM objects. To return a collection ordered alphabetically by the Rooster's name:
    from Rooster order by name asc;
    

    Lastly (well, certainly not last, but the end of this primer), HQL supports aggregate functions. This works a bit differently in that the result is an array of arrays. This is technically how a query object works in ColdFusion, but without the niceties of struct-type column names. This confuses me as HQL supports 'as' (like select sum(column) as mysum from Object). Maybe that's something coming in CF X? For now expect an array for each object found with an array of properties.

    Friday, February 11, 2011

    jQuery: Partial Loads

    I had an issue in jQuery the other day where a load() command wasn't working properly.  There could have been an error in my target HTML, but I sure couldn't find it. What I was doing was using Ajax to load table rows into a form.  The HTML being loaded just wasn't maintaining structure (<TD>s nested into a <TR>.  To fix this I read about jQuery partial load.  I don't know how I never saw this before as I can see a few cool uses.

    A partial load works like this: add a selector after the url in a load() method call and jQuery will load all matching elements into your specified element.
    // load all td elements from the specified url into myElement
    
    $('#myElement').load('http://url/template.html td');
    

    This is shorthand for the following:
    $.get(''http://url/template.html', function (html) {
    
        $(html).find('td').each(function (){
    
            $('#myElement').append(this);
    
        });
    
    }
    

    The documentation can be seen here.

    My Take on the ColdFusion Team Changes

    So, if you haven't already read the news, head on over to Adam Lehman's blog and read about what he dubs "The Modern Age of ColdFusion", aka some major shifts in the direction of the ColdFusion platform.  I figure I'd throw out my interpretation...

    When I first heard about this, I grilled Adam pretty hard and I only had one concern: Will the new product champion(s) have at much at stake in ColdFusion's success as he and Alison did?  Time will tell, but Adam has assured me that the person leading this up not only understands what he and Alison have laid out on the ColdFusion roadmap, but also came to many of the same conclusions.  Another good thing (my understanding) is that the stellar team who has been creating ColdFusion is still there.

    What this translates to is that moving ColdFusion under a new business unit gives its developers (not end users, the people who write the ColdFusion engine) a unique opportunity to take the time and money necessary to have the next version of ColdFusion fulfill Adam and Alison's vision, which, coincidentally is what the community at large has been asking for.

    On that note, I'd like to express my gratitude to both Adam and Alison for not only the awesome roadmap they have laid out for the next version of CF, but for the dedication they have demonstrated and the flak they have taken on behalf of (and sometimes at the hands of) the ColdFusion community.  Look at where we've gone in the last two versions of ColdFusion including my love/hate relationship with ORM, extensibility features with Microsoft Office, ColdFusion as a Service (CFAAS), enhanced CF script including MUCH better ECMAScript compliance, image manipulation, core engine improvements and all of ColdFusion Builder!  Heck, that only scratches the surface.  So I raise my glass to Alison and Adam and say 'Viva la ColdFusion!'