We used to blog more before twitter came along.
ColdFusion 9: ORM, Part 1
So the challenge tonight is to bang out a quick little member management system using the ColdFusion 9 ORM tools. Basically, a test of the "look, ma, no code!" approach to software development.
Step 1: create blank database:

Ok, so that was boring. Next step: make a basic CFC using ColdFusion Builder's CFC generator. All I specified were properties that I expected to map to columns in the database. Still haven't written code - here's what was generated for me.
<cfproperty name="personID" type="numeric" />
<cfproperty name="firstName" type="string" />
<cfproperty name="lastName" type="string" />
<cfproperty name="address1" type="string" />
<cfproperty name="address2" type="string" />
<cfproperty name="city" type="string" />
<cfproperty name="state" type="string" />
<cfproperty name="postalCode" type="string" />
<cfproperty name="phoneNumber" type="string" />
<cfproperty name="emailAddress" type="string" />
<cfproperty name="profession" type="string" />
<cfproperty name="chapterName" type="string" />
<cfproperty name="initiationYear" type="numeric" />
</cfcomponent>
So far, pretty simple. Had to fight the urge to create getters and setters for each property...we'll let the ORM engine take care of that for us. I saved this CFC in person.cfc under the application's home directory in my webroot.
Next step: setting up Application.cfc.
<cfset this.name = "ChiOApp">
<cfset this.ormenabled = "true">
<cfset this.datasource = "chiomega">
<cfset this.ormsettings={dbcreate="update", logsql="true"}>
<cfset this.clientmanagement="false">
<cfset this.sessionmanagement="true">
<cfset this.sessiontimeout="#createtimespan(0,0,10,0)#">
<cfset this.applicationtimeout="#createtimespan(5,0,0,0)#">
</cfcomponent>
The only three lines that are different from our "usual" config are this.ormenabled, this.datasource and this.ormsettings. The first tells ColdFusion to pay attention to our CFCs and automagically configure the ORM mappings for us. The second is another new feature of ColdFusion 9 - it sets a "default" dsn for all cfquery and cfstoredproc tags so we don't have to type datasource="#request.dsn#" anymore. Rock! Also, the ORM engine uses this value as the database to persist CFC data in. The third tells the ORM engine that I want it to create the necessary database tables and (when the time comes) log all the sql statements fired by the generated code. You'll see those log entries fly by if you start up CF from the command line.
Total code CF code written so far: three lines. Awesome.
Consulting the docs, I now need to add a few attributes to the cfcomponent tag to have the table created in the database for me.
<cfproperty name="personID" type="numeric" fieldtype="id" generator="increment" />
...
Let's look at what I added:
- persistent="true" - think of it to mean "please store the properties of this cfc as columns in a table you create for me"
- fieldtype="id" - this is the primary key field
- generator="increment" - I want it to auto increment for each new record (I'm using MSSQL05 for this example)
So let's take a peek at what happens when I run a simple script.
obj = EntityNew("person");
</cfscript>
<cfdump var="#obj#" />

Nothing too special - it's just a cfc. But this is where the new hotness resides:

So let's jack some data into the db using this new hotness. Please note: the spreadsheet I'm using has the typical "not created by a programmer" column names with spaces, so I'm using array syntax to extract the field data.
<cfloop query="sheet" startrow="2">
<cfscript>
obj = EntityNew("member");
obj.setFirstName(sheet["FIRST NAME"][sheet.currentRow]);
obj.setLastName(sheet["LAST NAME"][sheet.currentRow]);
obj.setAddress1(sheet["ADDRESS"][sheet.currentRow]);
obj.setAddress2(sheet["APT"][sheet.currentRow]);
obj.setCity(sheet["CITY"][sheet.currentRow]);
obj.setstate(sheet["STATE"][sheet.currentRow]);
obj.setPostalCode(sheet["ZIP"][sheet.currentRow]);
obj.setPhoneNumber(sheet["PHONE ##"][sheet.currentRow]);
obj.setEmailAddress(sheet["EMAIL"][sheet.currentRow]);
obj.setProfession(sheet["PROFESSION"][sheet.currentRow]);
obj.setChapterName(sheet["CHAPTER"][sheet.currentRow]);
obj.setInitiationYear(val(sheet["INITIATION YEAR"][sheet.currentRow]));
entitySave(obj);
</cfscript>
</cfloop>
<cfset ormFlush() />
Those last two lines are the fun part and should be explained. entitySave causes Hibernate to store the object in its own cache for really, really fast retrieval. To actually get the data written to the database, we call ormFlush(). If the object has been changed, it gets pushed to the database. In this example, 245 records are created in the cache, then all written out to the database.
Our database:

So now that all of that info is loaded into the database without having written a single line of SQL (c'mon, don't act like you're not impressed), let's slap together a quick browse interface. Do not use this code as an example of "how to write CF"...it's a hacktacular approach just to show the basics. Please.
<!--- get all the people in the database --->
<cfset aPeople = EntityLoad("person") />
<!--- set up my postback form, populating the dropdown with people --->
<form id="browse" method="post" action="">
<select id="personID" name="personID">
<cfloop array="#aPeople#" index="person">
<cfoutput>
<option value="#person.getPersonID()#" <cfif isDefined("form.personID") and form.personID is person.getPersonID()>selected</cfif>>#person.getLastName()#, #person.getFirstName()#</option>
</cfoutput>
</cfloop>
</select>
<input type="submit" value="Get" />
</form>
<!--- if a person was selected, dump out their information --->
<cfif form.personID is not 0>
<!--- the second argument is the structure of matching criteria --->
<cfset person = entityLoad("person", {personID=form.personID}) />
<cfdump var="#person#" />
</cfif>
In "old school" code we would have had to write a query to get any of this data out of the db. Instead, we're asking the ORM engine to do all the heavy lifting. You can see that we're adding a match between the personID field and the form field of the same name. You can specify as many matching criteria as you wish, again without writing any <cfif> this or <cfelse> that.
The result (blurred for privacy reasons):

In my next post, I'll create the edit form AND start working on the one-to-many relationship between a person and a "comments" table. This is where we'll get the most utility out of ORM, but also where we'll find the most complexity.
So far, though, I'm totally impressed. This really was my first experiment with ColdFusion's ORM capabilities. There's plenty more to learn and I'm sure I'll refine my technique even more as I dig in to it.


There are no comments for this entry.
[Add Comment]