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:

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:

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.

 


14 comments

  1. Nice Writeup! Just a quick example with properties. If all you are doing is providing the property name in script you can use this is as shorthand.

    property userId;
    property firstName;
    property lastName;

    Comment by Dan Vega – July 13, 2009
  2. Hey Dan - thanks :) Good tip about the property shorthand.

    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
  3. I kept getting errors when calling your getSoldItemCount function... something about Art is not mapped. I instead added a new relationship with a where that filtered on sold items.

    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
  4. Hi Drew, I guess that's the problem with writing code samples for a beta build! You probably want to check out the filter attribute of cfproperty which lets you do some cool stuff :)

    Comment by John Whish – August 04, 2009
  5. Here is the error I get when I add all these files into an ORM folder:

    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
  6. Hey dan, I've just tested the code in the post and I'm not getting any errors so I'm not sure why you are getting that message. I'll ping you my code.

    Comment by John Whish – November 09, 2009
  7. @Dan, I tried to reply to send you a zip to your email address but it's returning "DNS Error: Domain name not found" so let me know of another email address I can use.

    Comment by John Whish – November 09, 2009
  8. Hey John, Dan,
    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
  9. I have the same issue here about Either the mapping for this component is missing or the application must be restarted to generate the mapping. I have got one table works perfectly but when I introduce another one,, I have the error message. Now, I am stuck

    Comment by David – December 19, 2009
  10. @David I've finally got round to attaching the zip file (at the bottom of the post). Can you download that and let me know if that works for you?

    Comment by John Whish – December 19, 2009
  11. Just as the situation above, I also found the info about "Mapping for component XXX not found" and have no way to solve it. Beacuse of the two tables in my database are isolated, so I did not created the relationship between them, was it the cause of the problem?

    Comment by kenlam – December 25, 2009
  12. If someone wants to send me their code I'll be happy to have a look!

    Comment by John Whish – December 27, 2009
  13. I'm getting the same thing and rds debugging I see the message:

    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
  14. Whoops, bouncing back and forth between examples. The entity is looking for Artist component

    Comment by John Hall – December 28, 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.