I received a small test application today for a bug, that said that the EntityReload performance was really bad. My first reaction was – there is nothing much we can do – thats all Hibernate code there. On a second thought, I decided to have a look at the application. The application had an object where one of the field’s value was generated by DB. Since the application never sets the value for this property while insertion, it had defined the property with attribute “insert” set to false. When the object is saved, the sql does not include this property and hence its value is generated by the database. So far so good. However the application needs to get the value that is generated by the database immediately after the object is saved. In order to do that, the application calls EntityReload() on the object and the generated value for the property gets loaded. Perfect? Not at all.

Here is the simplified component definition

/**
* @persistent
*/
Component Group
{
/**
* @fieldtype Id
* @generator native
*/
property id;
 
property name;
 
/**
* @dbdefault "'Y'"
* @insert false
*/
property char active;
 
/**
* @fieldtype one-to-many
* @cfc User
* @fkcolumn userid
* @lazy true
* @cascade all
* @inverse
*/
property users;
 
}

Here ‘active’ is the property that is generated by DB and the application calls EntityReload, so that all the property values including ‘active’ are fetched and populated in the object.

There are two issues here

  • EntityReload is getting called immediately after the insertion which is completely unnecessary in this case. All we need here is to get the generated value from the database and you can do that by specifying ‘generated’ attribute on the property. This will tell hibernate that active property is generated by database at the time of insertion and thus suggests hibernate to fetch the value of ‘active’ property from DB right after the row is inserted. When the object is persisted in the DB, the value for ‘active’ property will be populated back in the object immediately. This will save all the overheads of EntityReload.
  • Notice the value for cascade here – it is ‘all’. This means that all the operations, including EntityReload, will be cascaded to the association. Thus all the associated objects will get reloaded irrespective of whether the association is lazy or not. And that is not all – If the associated objects has further associations with ‘cascade’ set on them, the operation will be cascaded on them as well. As a result, the whole object graph will get loaded irrespective of the lazy value on the relationship and that is so so expensive.

The lesson here is not to use EntityReload unless it is necessary. And if you need to use it, check if you really need to cascade this operation to the association. Whenever you specify the cascade value for the association, think about the operations that need to be cascaded and then specify only those values that apply. The valid values for cascade are – save-update, delete, refresh, delete-orphan, merge, all, all-delete-orphan. This is how they map to the operations

cascade value Function
save-update EntitySave
delete EntityDelete
refresh EntityReload
merge EntityMerge

Here is how the CFC looks after fixing these two issues.

/**
* @persistent
*/
Component Group
{
/**
* @fieldtype Id
* @generator native
*/
property id;
 
property name;
 
/**
* @dbdefault "'Y'"
* @generated insert
*/
property char active;
 
/**
* @fieldtype one-to-many
* @cfc User
* @fkcolumn userid
* @lazy true
* @cascade "save-update,delete,merge"
* @inverse
*/
property users;
}