Friday, July 31, 2009

ColdFusion ORM : Troubleshooting - 'Lazy' does not work

Few days back Manju logged a bug in CF-ORM saying 'lazy' does not work for many-to-one relation and that too on non-Windows machine. At first, I simply rejected the bug because a) ORM can not have anything to do with OS and therefore, if it works on Windows, it works on all the plaform and b) I know it works :-). But he did not agree and I had to take a look at that machine. And apparently he was right - lazy was not working ! The related entity was in-fact getting loaded immediately. (Question for you - how will you know that lazy is working or not?)

Even after seeing this, I did not believe it and asked him to replicate this on another system and he successfully showed that to me on one another system. And he agreed that it works fine on most of the configurations. The problem exists only on a few of the systems.

This got me thinking - Why would a relation get loaded immediately even after it is marked lazy? The only answer would be - if some one is accessing that lazy field and calling some method on it. I checked his code which was loading the entities to see if there could be any case, where the field would get loaded and unfortunately there was none.

And then suddenly it hit me - what if "memory tracking" is swithched on? That would access each of the field of each object recursively to compute the size of an object and that can definitely cause this. I immediately checked the server monitor and the "memory tracking" was right there staring at me in "red"! It was indeed enabled. I asked Manju to check the other system as well (where lazy was not working) and the memory tracking was enabled there as well.

So the lesson - If the 'memory tracking' is enabled on the server, the relationship will no longer remain lazy. And btw, you should enable "Memory tracking" on the server only if you need to track the memory for some troubleshooting. Memory tracking is really really expensive in terms of performance.

Another reason why it might not work for you could be - if you are sending the object to flex. Currently, during serialization, the related objects also get sent irrespective of the lazy attribute set on the relationship. We are still working on it and hopefully by the time we release, this will be fixed.

Monday, July 27, 2009

ColdFusion ORM : How to log SQL

When you use ORM for developing an application, SQLs are generated and executed by the underlying ORM engine (i.e Hibernate for ColdFusion ORM). However, for both troubleshooting and performance optimization, it is crucial to monitor what queries are getting generated. It can help you find out if there is any error in mapping that you have provided as well as it can help you decide various tuning strategies.

ColdFusion can log the SQLs generated by ORM either onto the console or a file. At the same time it leaves enough hook for you to log it anywhere you want.

ColdFusion ORM provides two ways to log the SQLs.

  1. Using application setting to log to console : This is a quick and simple way to log the sql to console. This is enabled by setting "logsql" in ormsettings.
       <cfset this.ormsettings.logsql="true">
    This setting is self sufficient and it will log all the sqls executed by hibernate to the console (or a file where the server output goes). However this is not a very flexible option. The sqls are always written to the console and it will be combined with any other output that goes to console. Also this option will not show the DDL queries used for creating or updating tables. It only logs the SQL for the entity operations.
  2. Using log4J.properties: Hibernate uses log4j for its logging and you can completely control its logging (including SQL) by modifying the log4j.properties. log4j.properties is present under <cf_home>/lib directory. Please note that you don't need to do any application specific setting for this.

I will go in details about using log4j.properties for SQL logging. Here is a snippet from log4j.properties file that is shipped with ColdFusion.

###--------------- Hibernate Log Settings ------ 
### Set Hibernate log
log4j.logger.org.hibernate=ERROR, HIBERNATECONSOLE

### log just the SQL
#log4j.logger.org.hibernate.SQL=DEBUG, HIBERNATECONSOLE
#log4j.additivity.org.hibernate.SQL=false
### Also log the parameter binding to the prepared statements.
#log4j.logger.org.hibernate.type=DEBUG
### log schema export/update ###
log4j.logger.org.hibernate.tool.hbm2ddl=DEBUG, HIBERNATECONSOLE
### log cache activity ###
log4j.logger.org.hibernate.cache=ERROR, HIBERNATECONSOLE

# HibernateConsole is set to be a ColsoleAppender for Hibernate message using a PatternLayout.
log4j.appender.HIBERNATECONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.HIBERNATECONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.HIBERNATECONSOLE.layout.ConversionPattern=%d{MM/dd HH:mm:ss} [%t] HIBERNATE %-5p - %m%n%n
#---------------------------------------------

First we will see the relevant settings for SQL logging:

  • log4j.logger.org.hibernate.SQL : Defines whether the SQL executed for entity operations will be logged and where it will be logged. The second value for this i.e 'HIBERNATECONSOLE' is a appender that controls where the SQLs will be logged. In the above example HIBERNATECONSOLE is a 'console' appender which means it will log the sql to console.
  • log4j.logger.org.hibernate.type : Defines whether parameter values used for parametrized query will be logged.
  • log4j.logger.org.hibernate.tool.hbm2ddl : Defines whether DDL sql statements will be logged.

To enable the SQL logging for console, we just need to uncomment the settings mentioned above. Here is how the hibernate log settings in log4j.properties file would look like

###--------------- Hibernate Log Settings ------ 
### Set Hibernate log
log4j.logger.org.hibernate=ERROR, HIBERNATECONSOLE

### log just the SQL
log4j.logger.org.hibernate.SQL=DEBUG, HIBERNATECONSOLE
log4j.additivity.org.hibernate.SQL=false
### Also log the parameter binding to the prepared statements.
#log4j.logger.org.hibernate.type=DEBUG
### log schema export/update ###
log4j.logger.org.hibernate.tool.hbm2ddl=DEBUG, HIBERNATECONSOLE
### log cache activity ###
log4j.logger.org.hibernate.cache=ERROR, HIBERNATECONSOLE

# HibernateConsole is set to be a ColsoleAppender for Hibernate message using a PatternLayout.
log4j.appender.HIBERNATECONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.HIBERNATECONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.HIBERNATECONSOLE.layout.ConversionPattern=%d{MM/dd HH:mm:ss} [%t] HIBERNATE %-5p - %m%n%n
#---------------------------------------------
Here is the complete log4j.properties for logging SQL for console. Ofcourse after changing this you need to restart the server. If you need to log the parameter values used for queries, you need to uncomment '#log4j.logger.org.hibernate.type=DEBUG' as well.

What if you want to log the SQL to a file and not to console? That is pretty easy. You just need to change the 'Appender' used here (HIBERNATECONSOLE) to point to a 'FileAppender' instead of a ConsoleAppender. Here is how the configuration for HIBERNATECONSOLE should look like after you point it to a File Appender.

log4j.appender.HIBERNATECONSOLE=org.apache.log4j.FileAppender
log4j.appender.HIBERNATECONSOLE.File=../hibernatesql.log
log4j.appender.HIBERNATECONSOLE.Append=true
log4j.appender.HIBERNATECONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.HIBERNATECONSOLE.layout.ConversionPattern=%d{MM/dd HH:mm:ss} [%t] HIBERNATE %-5p - %m%n%n

For standalone ColdFusion installation, the file 'hibernatesql.log' will be created in the /logs directory.You can also specify a full path of the file for property 'log4j.appender.HIBERNATECONSOLE.File' and the log will be written to that.

That was easy. Isn't it? What if you want a rolling log file where you dont want the log file size to grow infinitely. That is fairly easy too. All you need to do is to use an appropriate appender. The appender definition for that will look like

log4j.appender.HIBERNATECONSOLE=org.apache.log4j.RollingFileAppender
log4j.appender.HIBERNATECONSOLE.File=../hibernatesql.log
log4j.appender.HIBERNATECONSOLE.Append=true
log4j.appender.HIBERNATECONSOLE.MaxFileSize=500KB
log4j.appender.HIBERNATECONSOLE.MaxBackupIndex=3
log4j.appender.HIBERNATECONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.HIBERNATECONSOLE.layout.ConversionPattern=%d{MM/dd HH:mm:ss} [%t] HIBERNATE %-5p - %m%n%n

Here is the complete log4j.properties for logging SQL to a file that will be rolled automatically once it reaches 500KB.

Now that you have seen how easy it is to change one 'Appender' to another, you can pretty much log it anywhich way you want. Here are some of the interesting 'Appender's that come with log4j which you can easily use.

DailyRollingFileAppender, ExternallyRolledFileAppender, JMSAppender, SMTPAppender, SocketAppender, SyslogAppender

See log4J for more details on log4j settings.

Saturday, July 18, 2009

ColdFusion 9 : Implicit/Generated CFC Methods

How many times did you write the accessor methods (better known as getters and setters) for the fields of your CFC? And how many times did you feel that it was really a mundane job and wished there was a better way to do this? With ColdFusion 9, we have done just that! You no longer need to write those accessors. ColdFusion will automatically generate the accessor methods for you in the object. All you need to do is to define the properties using cfproperty.

Consider this CFC Person.cfc

Component Person{
property firstname;
property lastname;
property age;
property city;
property state;
}

When an object is created for this CFC, you can directly call the setters and getters for the properties as


person = new Person();
person.setFirstName("Brad");
person.setLastName("Pitt");
person.setAge(46);

writeOutput("Name : #person.getFirstName()# #person.getLastName()#");
writeOutput("Age : #person.getAge()#");


The implicit setter/getter uses the 'variable' scope, which is a private scope for a object, for storing the data. i.e the setter method puts the data in the variable scope and the getter method gets the data from the variable scope.

What if you want to override the setter/getter method? Ofcourse you can do that. You just need to define those methods in your CFC and ColdFusion will call your method instead of calling the implicit one.

Hmm.. What if you dont want ColdFusion to generate getter or setter or both methods for a particular property? You can disable that by adding attribute getter="false" and setter="false" on the property.

So if you define a property

property name="city" getter="false" setter="false";

ColdFusion will not generate getCity() and setCity() methods for this CFC.

The implicit methods can also do some nice validation if you have given the 'type' for properties. Consider the same Person.cfc with type specified where we have made age as a numeric type.


Component Person{
property string firstname;
property string lastname;
property numeric age;
property string city;
property string state;
}

ColdFusion will do the datatype validation whenever the setter method is called for any property. So if you call


person = new Person();
person.setFirstName("Brad");
person.setLastName("Pitt");
person.setAge("LA");

It will throw a nice error saying "The value cannot be converted to a numeric".

Whats more interesting is that you can do a whole lot of validation using cfproperty. We have added two more attributes on cfproperty named 'validate' and 'validateparams'. These attributes allow you do even more advanced validations of the property data when the setter method is called for a property. (This is similar to cfparam or cfform validation).

The possible values for validate attributes are

  • string
  • boolean
  • integer
  • numeric
  • date
  • time
  • creditcard: A 13-16 digit number conforming to the mod10 algorithm.
  • email: A valid e-mail address.
  • eurodate: A date-time value. Any date part must be in the format dd/mm/yy. The format can use /, -, or . characters as delimiters.
  • regex: Matches input against pattern specified in validateparams.
  • ssn: A U.S. social security number.
  • telephone: A standard U.S. telephone number.
  • UUID: A Home Universally Unique Identifier, formatted 'XXXXXXXX-XXXX-XXXX-XXXXXXXXXXXXXXX', where 'X' is a hexadecimal number.
  • guid: A Universally Unique Identifier of the form "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" where 'X' is a hexadecimal number
  • zipcode: U.S., 5- or 9-digit format ZIP codes

The 'validateparams' attribute available with <cfproperty> takes the parameters required by the validator specified in the 'validate' attribute. This needs to be specified in the implicit struct notation.
  • min: Minimum value if 'validate' is integer/numeric
  • max: Maximum value if the 'validate' is integer/numeric
  • minLength: Minimum length of the string if the 'validate' is string
  • maxLength: Maximum length of the string if the 'validate' is string
  • pattern: regex expression if the validator specified in 'validate' attribute is regex
Consider the Person again where we will add few more properties with some validation

Component Person{
property string firstname;
property string lastname;
property numeric age;
property string city;
/**
* @validate string
* @validateparams {minLength=2, maxLength=2}
*/
property string state;
/**
* @validate zipcode
*/
property numeric zip;
/**
* @validate telephone
*/
proeprty phone;
}

Now when you create an object of Person and call setState(state), before setting the data for state in variable scope, ColdFusion will validate that state value provided is of type 'string' and its length is 2. Similarly setZip() will validate that the input data is a valid zip code and setPhone() will validate that the input data is a valid telephone number.

For String type properties, you can also do regular expression validation allows you to have all sort of validation on a property of string type.

And to top it all, all of these nice goodies are available to you at a much lesser cost than regular UDFs. Yes, you read it right. Implicit methods perform much better than regular UDFs that you write in CFC. To confirm that, lets use this CFC where we will use implicit methods for 'firstName' and write our own accessors for 'lastName'.


component 
{
property String firstName;
lastName = "";

public void function setLastName(string name)
{
lastName = name;
}

public string function getLastName()
{
return lastName;
}
}

Both implicit methods and UDFs are doing exactly the same thing i.e settings and getting the data in/from variable scope. Now lets run this comparison test



person = new Person();
t = getTickCount();
for(i = 0; i < 100000; i++)
{
person.setfirstName("Brad");
fname = person.getFirstName();
}
writeOutput("Total Time in implicit methods " & (getTickCount() - t));
t = getTickCount();
for(i = 0; i < 100000; i++)
{
person.setLastName("Pitt");
lname = person.getLastName();
}
writeOutput("Total Time in UDF methods " & (getTickCount() - t));

Here is the output
Total Time in implicit methods 63
Total Time in UDF methods 485

Thats almost eight times faster than regular UDF. Isn't that sweet? You get those auto-generated methods, whole lot of auto-magical validations and to top that, incredible performance. I am sure writing accessors for your CFC fields will become a thing of past now !

Friday, July 17, 2009

A demo for ORM code generator in ColdFusion builder

Evelin created this nice demo that shows how to setup this "ORM code generator" extension and how to use it. Check out the demo here.

ColdFusion Builder : ORM code generator

If you have started using ORM (or you want to start) in ColdFusion 9, there is a nice plugin in ColdFusion builder that can help you quickly generate all the ORM code for you. All you do is select the tables from the RDS view, define the relationships, if there is any, and all the code including the service layer will be generated. Evelin talks about it here. Oh btw, if you did not notice, thats the ColdFusion Builder team blog.

You can download the plugin from here and download the instruction from here. Have Fun playing with it !!!

Thursday, July 16, 2009

ColdFusion ORM and CFC Performance

Since the day we started thinking about ColdFusion ORM, people have been raising concerns over it because of the CFC performance. We have heard people saying that ORM will be unusable because of poor CFC performance and it is a stupid idea to implement CFC based ORM. And today I heard Hal Helms and Brian kotek talking about it where Hal says that he shudders at the thought of hibernate integration in CF because of poor performance.

It is true that CFC performance is poor as compared to POJO and it will always be. ColdFusion CFC has lot of cost involved because of the dyanmic nature of it and additionally, it has reflection cost involved. I had shared some of the reasons with few of the community members and I thought it will be interesting to share it witha wider audience. Here are the main reasons :

  1. Unlike java where the class has a fully qualified name and is loaded only once in the classloader, when you create CFC, CF needs to search for the CFC file on the disk, compute the class name for it and then load it. And that happens on each object creation unless you have switched on trusted cache .
  2. CFC  is compiled at runtime, loaded and constructed using reflection. I am sure you will be aware that reflection is much slower (25 times easily) than direct invocation in java.
  3. CFC has two different scopes - this and variables scope which is a struct. So every object creation involves two additional struct creation cost.
  4. CFC creation also involves running the default constructor i.e any code outside cffunction and that happens for each object creation. That involves the complete setup for method invocation i.e setting up localscope, superscope etc (another two structs here), which is also significant
  5. CFC allows UDFs to be added/removed from the object at runtime and hence methods are added to each object on object creation and that too in both the scopes (adding each UDFs in both the scopes also has significant cost). Thats the reason why object creation becomes costlier as you add methods in the CFC. Higher the number of methods, higher the object creation cost.

We have done a lot of performance improvements regarding CFC creation and invocation in ColdFusion 9 which I hope you would have noticed. But even then it can not match POJO performance and thats an unrealistic target.

So given that, lets come to the question - How much does CFC creation performance matters when it comes to ORM. In reality, not much. We have done a number of performance comparison between CFC and POJO with hibernate keeping everything else absolutely same. And all the time, CFC performance was same or better than POJO persistence. It might sound unbelievable and in fact I also could not believe it myself when I saw the performance numbers first. But that is the truth. And here is why it happens - When it comes to persistence, the time taken in DB operation and the persistence layer is much more than the time taken in object creation. And that cost is same for POJO and CFC. So the only difference in cost between CFC and POJO with respect to ORM is object creation cost and cost of populating/fetching the properties data. For instantiating POJO object and for using properties accessors, Hibernate has to use reflection which is always costly and that removes this cost difference of CFC and POJO. And for few of the runs, CFC was able to beat POJO as well and explanation for that is the optimizations that we have done in using accessors for properties.

I would be curious to know about your experience regarding CF-ORM performance and CFC performance in general. How has your experience been so far regarding CF-ORM peformance?

ColdFusion ORM : The basics

Before starting on the advanced topics, I thought it will be better to build some ground and hence I decided to do a post on the ORM basics. In this post, we will build a simple example to get a taste of ColdFusion ORM (CF-ORM) and during that we will also understand some of the basic concepts.

ORM is object relational mapping and in ColdFusion, objects are created using CFC. CFCs that needs to be persisted are called persistent CFC and that is marked by setting 'persistent' attribute to true on the component. We also need to define what persistent fields will be there in a persistent CFC and that is defined using 'cfproperty'. A field/property is marked persistent by setting persistent attribute to true on the cfproperty. By default, if the CFC is persistent, all its properties are considered as persistent unless you mark a property non-persistent. So typically 'persistent' attribute on the property is used only when you need to make that property non-persistent.

Each persistent CFC in ColdFusion application maps to a table in the database and each property in the persistent CFC maps to a column in the table (not exactly true but we will come to that later.. For the time being lets keep it that way). We will use the cfartgallery datasource for this example which has Artists and Art tables.

The first thing you need to do is - enable ORM for the application and define a datasource to be used (What is an ORM without a datasource?). ColdFusion ORM uses Application.cfc to define all the ORM specific settings. (If you haven't started using Application.cfc for your application, its time to start using it!)

Application.cfc


Component {
this.name="ArtGallery";
this.ormenabled="true";
this.datasource="cfartgallery";
}

Note that the datasource setting defined here makes it the default datasource of your application which can be used by tags like cfquery, cfinsert, cfupdate, cfdbinfo. The same default datasource will be used by ORM as well.

There are a whole bunch of ORM related configuration that you can do in application.cfc which you can refer here.

Now that the application is configured, let us build the object and define the persistence information on it. To start with, we will first define the Artist.cfc
Artists.cfc

/**
* @persistent
*/
Component {
property name="artistid" generator="increment";
property firstname;
property lastname;
property address;
property city;
property state;
property postalcode;
property email;
property phone;
property fax;
property thepassword;
}

This is the most simplistic definition of the component where we have defined only the component and its properties names. Since the table for this CFC already exists in the database, we have not added any table specific information in this and we will let ORM infer all the information from the database. The only additional setting that we have added here is the 'generator' attribute which is used to auto-generate the primary key.

After the components are defined, the first request to this application (i.e a page in this application) will make CF-ORM do all the setup necessary (basically generation of hibernate configuration, mapping files, building the session factory etc). Once the setup is done, you are all set to work with the entities.

We will first list all the artists and here is what you need to do for that

listAll.cfm
<cfscript>
artists = EntityLoad("Artists");
writedump(artists);
</cfscript>

To load a particular Artists with its ID, here is what you do
list.cfm


<cfscript>
artist = EntityLoadByPK("Artists", 1);
writedump(artist);
</cfscript>

There are several flavors of EntityLoad functions details of which can be read here

Let us now see how to perform insert and update on it.

save.cfm


<cfscript>
// Insert a new artist
artist = new Artists();
artist.setfirstname("Leonardo");
artist.setlastname("Da vinci");
artist.setcity("Paris");
EntitySave(artist);
writeOutput(artist.getartistid());// Update an artist
artist = EntityLoadByPK("Artists", 2);
artist.setcity("NewYork"); // artist is automatically updated.
</cfscript>

As we see in the above example, EntitySave is used to insert/update an object in the table. There are some important things to note here
  • EntitySave is an intelligent function which automatically finds if a new row needs to be inserted for the given object or whether an existing row needs to be updated.
  • We called EntitySave for the insert here but not for update but even then artist '2' gets updated. So how did that happen? Actually what happens here is when you load an artist object, it becomes associated with the hibernate session which keeps track of any changes in the object and automatically saves it when the session is flushed. We will talk about more about hibernate session in a later post. For the time being lets just say that Hibernate Session is a short-lived object that represents a conversation between the application and the persistence layer and also acts as the first level of cache.
  • We did not write any setter or getter method for artist's properties in Artists.cfc but we are calling them here. That works because ColdFusion 9 automatically generates accessor methods for any property written in a CFC. More details on generated methods in a later post.
  • At line no 6, we called entitySave, but if you check the database, the row is not inserted yet. So when does that happen? Hibernate batches up all the operations till the end of the request or to be exact till hibernate session is flushed. ColdFusion ORM starts up a session when the first ORM method is called in the request and is automatically flushed when the request ends. The batching is done for performance reason so that hibernate executes the sql with the final state of the objects. It will be a huge performance bottleneck if ORM keeps executing sql for each changes in the object.

To delete an Artist, you need to call EntityDelete() passing the object to be deleted.

delete.cfm


artist = EntityLoadByPK("Artists", 15, true);
EntityDelete(artist);

Relationship


So far we have seen how to perform CRUD for a single entity. But in any application, there will be entities which are associated and ORM must load the associated object as well when loading a particular entity. For our example, an Art will have an Artist and when loading the art object, it should also load the associated artist. So lets build the model first after which we will see how to work with the association.

In cfartgallery, the table Artists has a one-to-many relationship with Art table, which are joined using the foreign key column ARTISTSID. This means that each artist has created multiple arts and each art is created by one artist. To represent this in the object model, each ARTIST object would contain an array of ART objects. Each ART object will also contain a reference to its ARTIST object thereby forming a bidirectional relation.

To achieve this, we will need to add an extra property 'arts' to 'Artists' that contains an array of ART objects for that Artist. The modified Artists.cfc would look like
/**
* @persistent
*/
Component {
property name="artistid" generator="increment";
property firstname;
property lastname;
property address;
property city;
property state;
property postalcode;
property email;
property phone;
property fax;
property thepassword;
property name="arts" fieldtype="one-to-many" fkcolumn="artistid" cfc="Art" cascade="all" inverse="true";
}

Here is the Art.cfc

/**
* @persistent
*/
Component {
property name="artid" generator="increment";
property artname;
property price;
property largeimage;
property mediaid;
property issold;
property name="artist" fieldtype="many-to-one" fkcolumn="artistid" cfc="Artists" ;
}

Notice the artist property above which is of many-to-one type. Also notice that both the property use the same value for fkcolumn attribute i.e 'artistid' of Art table that references artistID pk of Artist table.


Since we have added a new persistent CFC (Art.cfc) after the application was loaded, we need to re-initialize the ORM for this application so that mappings for Art.cfc also gets generated. This can be done by calling ORMReload() method. There are some nice ways to do this but for the time being lets keep it simple by putting this in a separate page which we will call to reload ORM.

initializeORM.cfm
<cfset ormReload()>

If you load and dump Artist (using listAll.cfm), you should also see the associated art objects for artists.

Now let us create a new Art and associate it with an existing Artist.
artCreate.cfm

<cfscript>
artist = EntityLoad("artists", 1 ,"true");
art = new Art();
art.setartname("landscape");
art.setPrice(1500);
art.setissold(false);
art.setartist(artist);
artist.addArts(art);
EntitySave(art);
</cfscript>

If you notice line 7-8 above, we associate artist to art by calling art.setArtist(artist) as well as art to artist by calling artist.addArts(art). Hibernate needs us to do this in order to set up the bidirectional relation properly. Since it is a bidirectional relation, you must also decide which side will set the relation in the database. i.e which side of the relation will set the fkcolumn in the table. This is controlled by the "inverse" attribute of proeprty, which if set to true, tells hibernate that this is a inverse of the other relation and this side of relation should be ignored for persistance. If you don't set inverse to true, Hibernate will unnecessarily fire two sqls for the same association.

So there you have it. We have seen how you can use ORM to perform the basic CRUD operations on entities. For more details, you can refer to the ORM doc and Hibernate docs.

Monday, July 13, 2009

ColdFusion ORM - An Evolution in building datacentric application

As we all know, ColdFusion was born as DBML - a script that was very similar to HTML but had certain tags that could perform database operation and it revolutionized the way web applications were built. Tags like cfquery, cfinsert, cfupdate made it very easy to perform database operations while building web application. Even after 14 years of CFML, (which we incidently completed day before yesterday), cfquery is *the* most popular and the most commonly used tag. However there are few downsides to it

  • Database vendor dependency - The queries that are written are DB specific. If you ever need to switch the database or if you need to make the application database agnostic, it becomes too much of pain. You will end up changing the entire application code or lot of conditional code to handle queries for different databases.
  • Very tight coupling with the database - The queries have very tight coupling with the database. This means that if there is any change in the table name or column name, you will end up with changes all over your application code.
  • Repititive and hence low on productivity - For any CRUD (Create, read, update and delete) operation, you need to write queries which are more or less same for all the tables. You must have felt that you are repeating the same code everywhere in the application. There is too much of repititiveness which reduces productivity.
  • Error prone - How many times did you write insert/update queries and then you had to struggle to debug because the order of column and its values were different? And how many times did you get frustrated because there was a datatype mismatch between the value you had specified and the one expected? How many times did you write a query that was prone to sql injection and you were reminded to use query param? I am sure all of this has happened to all of us and its completely natural given the way queries are written. They are error prone.

ColdFusion 9 introduces a new way to build datacentric application using ORM, that handles all the downsides we saw above. It is not a replacement to cfquery/cfinsert/cfupdate tags but its a different approach altogether. It allows you to build your application using objects where you focus on the business logic and all the grunt work of persistence is taken care automatically (In simple words you dont write SQL queries generally).

ORM (Object relational mapping) is a well known strategy/technique to map relational data to the object model. In an object model, business objects are not aware of the database structure. Objects have properties and references to other objects whereas Databases consist of tables with columns that are related to other tables using foreign key. ORM provides a bridge between the relational database and the object model allowing you to access and update data entirely using the object model of an application.

ORM is not new to ColdFusion either - Transfer and Reactor being the most popular ones. ColdFusion ORM (or CF-ORM) is much more extensive and sophisticated ORM solution than these ORMs. ColdFusion ORM is built on top of Hibernate - the best java ORM engine out there, which is quite powerful and extensive. ColdFusion ORM makes it very easy to use persistence with objects and at the same time allows you to leverage the full power of Hibernate.

ColdFusion ORM provides features such as

  • Defining the persistence mapping and managing the object's persistence using very simple methods.
  • Database vendor independence
  • Loose coupling between database and application - The application becomes very adaptive to changes as the configuration is central and changes will be required only there.
  • Auto-generation of schema - ORM lets you auto-generate the schema for your application. So you never need to bother about keeping your object model and the database schema in sync. It can automatically happen.
  • Productivity and manageability - Since ORM takes care of all persistence grunt work, you focus on your application logic and thus your application becomes much more cleaner and manageable. It also makes you lot more productive as application can be built much more faster.
  • Concurrency - ORM inherently takes care of concurrency control. So you dont need to worry much about how things would work when multiple database operations happen in parallel web requests.
  • Performance - ORM provides lot of performance optimizations that can make your application run faster. The optimizations include two levels of caching (including pluggable distributed cache), lazy loading, various settings to tune the sql queries generated by ORM.
  • Secure - Since queries will be executed by ORM, issues like SQL injection no longer exist and thus your application becomes secure.
  • Inbuilt pagination
  • SQL like query (HQL) for a finer control of the data to be loaded.
  • ...

In this series of posts, I will be talking lot more about ORM which I hope you would like. In case you have not downloaded ColdFusion 9 beta, go download it and start playing with it.

Moving my blog to a new address

With the release of ColdFusion 9 beta, I am also moving my blog from blogger to a new address (http://www.rupeshk.org). I have moved all the content including comments from blogger to the new site. The new blog is still in beta as I still need to do lot of cleanup.

I will keep posting the entries to both the blogs for some time till I feel that the traffic has moved to the new blog. So please put all your comments on the new blog so that its all at one place :-)

If you are subscribing to my feed or if you have linked to this blog, you can change that to use the new feed. See you over at http://www.rupeshk.org

Sunday, July 12, 2009

ColdFusion 9 and ColdFusion builder beta available

I am sure by the time you will be reading this, the blogosphere will be buzzing with this news :-). We just (13th July 12:01 AM EST) went live with the beta of ColdFusion 9 and ColdFusion builder. All of us in the team had been working really hard for last one and half years to put in features, that will take RAD to the next level, features that will make your life easy, features that you are going to love. ColdFusion has always been known for "making hard things easy" and I am sure this release will make even harder things easy :-)

Ben talked about the new features in ColdFusion 9 and ColdFusion beta here :

http://www.adobe.com/devnet/coldfusion/articles/coldfusion9_whatsnew.html

http://www.adobe.com/devnet/coldfusion/articles/cfbuilder_whatsnew.html

Here are the links for public beta

ColdFusion 9 : http://labs.adobe.com/technologies/coldfusion9/

ColdFusion Builder : http://labs.adobe.com/technologies/coldfusionbuilder/

I also wanted to highlight an excellent devnet article written by Mark Mandel introducing ColdFusion ORM. I am sure ColdFusion ORM is going to completely change the way ColdFusion applications are built and is going to make you much more productive. Hey, I am not boasting because its my baby, but I genuinely feel this :-)