ColdFusion 9 ORM example
July 13, 2009
Mark Mandel has written a great article for the Adobe Devnet on Introducing ORM in Adobe ColdFusion 9 Beta. I just thought I'd hop on the bandwagon and blog about a simple example demonstrating some of the ORM features using the in-built cfartgallery datasource so you can run it without setting up a database.
I've created two CFC's called Art.cfc and Artist.cfc, these are my persistant CFCs. In others they are want I want to be saved in my database. I've written these using the nice new cfscript enhancements with JavaDoc style comments/metadata.
Let's look at Art.cfc first.
/**
* 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";
}
OK, this is pretty simple. I have an identifier (think primary key), some properties and a relationship to my Artist Entity. The relationship simply tells ColdFusion that a piece of Art has one Artist. I won't go into two much detail about this as it is all in Mark's article, I'll just say that from this ColdFusion will auto-magically create the getter and setter (accessors/mutators) methods for each property:
- getArtName()
- setArtName()
- getDescription()
- setDescription()
- getPrice()
- setPrice()
- getLargeImage()
- setLargeImage()
- getIsSold()
- setIsSold()
- getArtID()
- setArtID()
Pretty cool huh!
If you're wondering how ColdFusion knows about datatypes, then it simply introspects the database and figures it all out for you (assuming the database exists!). ColdFusion can actually create the tables for you, but I'll leave that for another post.
Moving on to my Artist.cfc.
/**
* I am a Artist Persisted Entity
* @output false
* @table ARTISTS
* @persistent true
*/
component
{
// identifier
property name="ArtistID" fieldtype="id" generator="native";
// properties
property name="Firstname";
property name="Lastname";
property name="Address";
property name="City";
property name="State";
property name="PostalCode";
property name="Email";
property name="Phone";
property name="Fax";
property name="ThePassword";
/* one artist can have many... */
/* return an array of Art objects */
property name="Art" fieldtype="one-to-many" cfc="Art" fkcolumn="ArtistID" type="array" orderby="Price Asc";
/* if we wanted to return a struct of Art objects we'd use this syntax */
// property name="Art" fieldtype="one-to-many" cfc="Art" fkcolumn="ArtistID" type="struct" structkeycolumn="ArtID" orderby="Price Asc";
/**
* I return the number of itemd of art associated with this Artist
* @output false
*/
public numeric function getArtCount()
{
if ( this.hasArt() )
{
return ArrayLen( this.getArt() );
}
else
{
return 0;
}
}
/**
* I return the number of items sold for this artist
* @output false
*/
public numeric function getSoldItemsCount()
{
return ORMExecuteQuery(
"SELECT COUNT(*) FROM Art as ArtEntity WHERE ArtEntity.Artist.ArtistID=:ArtistID AND ArtEntity.IsSold=:IsSold",
{ ArtistID=this.getArtistID(), IsSold=True }
, True );
}
/**
* I return the number of itemd of art associated with this Artist
* @output false
*/
public string function getFullname()
{
return this.getFirstName() & " " & this.getLastname();
}
}
Right, a bit more going on in this one. The sharp eyed among you may have noticed that the CFC is called Artist.cfc but the database table is called ARTISTS. ColdFusion handles this mapping for you if you specify the table attribute in your cfcomponent tag, or use the JavaDoc style metadata like I have above. If you don't include this attribute, then ColdFusion will assume that the Entity name and the Database table name are the same.
I've got a relationship to my Art cfc defined. This time one Artist can have many Art objects. In this example I'm returning an array of Art objects, but ColdFusion also allows you to return a struct of Art objects which can be pretty useful. I've commented the struct syntax out above but thought I'd show it. When you define a one-to-many or a many-to-many relationship then ColdFusion will generate the following methods for your:
- addArt( ArtEntity )
- hasArt()
- getArt()
- removeArt()
More info on generated methods is available here:
http://help.adobe.com/en_US/ColdFusion/9.0/Developing/WSf0ed2a6d7fb07688310730d81223d0356fc-7fff.html
I've added a few of my own methods to the Artist.cfc which I'll use later. These are getArtCount(), getSoldItemsCount() and getFullname(). The getFullname() is the simplest, all it does is concatinate the first and last name values. Note that I've prefixed "this." to my method name. You'll need to do that, otherwise ColdFusion will throw an error when referencing generated methods.
The getArtCount() method, simply checks to see if the Artist has any Art by called this.hasArt(), if it does then I return the size of the array of Art objects.
getSoldItemsCount() introduces a new function in ColdFusion, ORMExecuteQuery. All this does is to query your objects (not I said objects and not database). The syntax is very similar to SQL (it's called HQL), but you reference your objects and their properties instead of database tables and column names. In the code above, I'm getting a count of all the Art for the Artist, which has been sold.
So how do we use our new fangled Persistant Entites? Well, here's some sample code to demonstrate:
Application.cfc
component
{
this.name = Hash( GetCurrentTemplatePath() );
/* 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;
/**
* @output true
*/
public boolean function onRequestStart( targetPage )
{
/* this is just to ensure that ORM is up-to-date for demo */
ORMReload();
return true;
}
}
index.cfm
<!---- get all artists ordered by first name, note the second argument is a blank filter ---->
<cfset artists = EntityLoad( "Artist", {}, "firstname asc" ) />
<cfoutput>
<table width="400">
<tr>
<th>Artist</th>
<th>Items</th>
<th>Sold Items</th>
</tr>
<cfloop array="#artists#" index="index">
<tr>
<cfif index.hasArt()>
<td><a href="art.cfm?artistid=#index.getArtistID()#">#index.getFullName()#</a></td>
<cfelse>
<td>#index.getFullName()#</td>
</cfif>
<td>#index.getArtCount()#</td>
<td>#index.getSoldItemsCount()#</td>
</tr>
</cfloop>
</table>
</cfoutput>
art.cfm
<!---- get artist by id ---->
<cfset artist = EntityLoad( "artist", url.artistid, True ) />
<cfoutput>
<h2>#artist.getFullName()#</h2>
<table width="400">
<tr>
<th>Item</th>
<th>Price</th>
<th>Sold</th>
</tr>
<cfloop array="#artist.getArt()#" index="index">
<tr>
<td>#index.getArtName()#</td>
<td>#index.getPrice()#</td>
<td>#index.getIsSold()#</td>
</tr>
</cfloop>
</table>
<p><a href="index.cfm">View list</a></p>
</cfoutput>
I've bundled it all up into a zip for anyone who's interested.
- Posted in:
- ColdFusion
- OOP
14 comments
Leave a comment
If you found this post useful, interesting or just plain wrong, let me know - I like feedback :)





property userId;
property firstName;
property lastName;
Comment by Dan Vega – July 13, 2009
I quite like the syntax I used in the post as I think it looks a bit odd when you define identities with named attributes (but that's just personal preference!). I think you can also define the named attributes using the JavaDoc style but I haven't tried it (yet!).
Comment by John Whish – July 13, 2009
property name="SoldArt" fieldtype="one-to-many" cfc="Art" fkcolumn="ArtistID" type="array" where="IsSold=1" orderby="Price Asc";
This is what I concluded from reading the docs on relationships like this. Excellent post and thanks for taking the time to write it in cfscript.
Comment by Drew – August 03, 2009
Comment by John Whish – August 04, 2009
Mapping for component Artist not found.
Either the mapping for this component is missing or the application must be restarted to generate the mapping.
Any help would be appreciated.
thanks
dan
Comment by dan fredericks – November 09, 2009
Comment by John Whish – November 09, 2009
Comment by John Whish – November 09, 2009
What was the resolution of the "mapping for component not found" error? I'm stuck in this situation myself, and I can't make heads nor tails of it.
Thanks!
Comment by Nathan Strutz – December 01, 2009
Comment by David – December 19, 2009
Comment by John Whish – December 19, 2009
Comment by kenlam – December 25, 2009
Comment by John Whish – December 27, 2009
coldfusion.orm.EntityMappingNotFoundException: Mapping for component Books not found.
at coldfusion.orm.hibernate.HibernatePersistenceManager.getEntityName(HibernatePersistenceManager.java:196)
Which leads me to believe that out of the box it's not finding the Entity object in Hibernate.
Suggestions?
Comment by John Hall – December 28, 2009
Comment by John Hall – December 28, 2009