Sam Farmer head shot

Sam Farmer

Growing up I never imagined I would play bass guitar for the Dave Matthews Band. And indeed it never happened.

But I have become a passionate and pretty good web developer.


Slides and Code: 5 Ways To Improve Your App with ORM

Here are the slides and code from my Adobe MAX ColdFusion Unconference talk 5 Ways To Improve Your App with ORM:

And the code which will set up the Derby DSNs and tables needed.

Available as a download and from Github. After installing:

  1. Go to setUp.cfm
  2. Enter in your cfadmin password and click 'Make Datasources'
  3. Go back to setUp.cfm and click on 'Go to App'

All of the mini apps/examples I used in the demo will now work.

CF Dude, What's in the cache?

Over the weekend I needed to see what was in my cache. I whipped up a quick script to list everything in cache and from there:

  • be able to remove an item
  • view the content of an item (limited to top 10 records)
  • view the metadata of an item.
I've included the code below.

view plain print about
1<cfscript>
2writeOutput('<h2>CF Dude, What''s in the cache?</h2><div><a href="#cgi.script_name#">Reload</a><br><br></div>');
3
4param name="url.do" default="";
5
6switch (url.do) {
7    case "remove":
8        cacheRemove(url.item);
9        writeOutput("<div style='color: green; font-weight: bold'>Removed #url.item#</div>");
10    break;
11    case "view":
12        writeDump( var=cacheGet(url.item), label="Cache contents of: " & url.item, top=10 );
13    break;
14    case "meta":
15        writeDump( var=cacheGetMetadata(url.item), label="Metadata for: " & url.item );
16    break;
17}
18
19incache = cacheGetAllIds();
20arraySort(incache,"textnocase");
21
</cfscript>
22
23<cfoutput>
24<h3>Cache IDs</h3>
25
26    <table cellspacing="4">
27    <cfloop array="#incache#" index="c">
28        <tr>
29            <td>#c#</td>
30            <td><a href="#cgi.script_name#?do=remove&item=#c#">remove</a></td>
31            <td><a href="#cgi.script_name#?do=view&item=#c#">view</a></td>
32            <td><a href="#cgi.script_name#?do=meta&item=#c#">meta</a></td>
33        </tr>
34
35    </cfloop>
36    </table>
37
38<h3>Cache Properties</h3>
39<cfdump var="#cacheGetProperties()#">
40</cfoutput>

The one where I try ColdFusion 9 ORM secondary cache and decide its awesome

I've pretty much overlooked the secondary cache for ORM and, having now played with it, I've realized what a big feature and performance enhancer this is.

In short with secondary cache on ColdFusion can get data from ehCache instead of the database. Set up ColdFusion 9.0.1 and you can share entity data across both ColdFusion instances and servers!

Let backtrack and take a look at a basic set up:

Application.cfc
view plain print about
1component {
2this.datasource = "cacheFun";
3this.ormEnabled = true;
4this.ormSettings = {secondaryCacheEnabled=true, logsql=true};
5}

To turn on secondary cache set secondaryCacheEnabled to true (the default is false). In this example I have also turned logsql to true*. Now lets look at our persistent cfc:

User.cfc

view plain print about
1component persistent="true" cachename="userCache" cacheuse="transactional" {
2property name="id" column="id" ormtype="int" fieldtype="id" generated="always" generator="native";
3property name="firstname" ormtype="string";
4property name="lastname" ormtype="string";
5property name="version" fieldtype="version" datatype="int" ;
6
7function getLastName() {
8    return uCase (variables.lastname);
9}
10}

That really is it. The two cache attributes tell ColdFusion to use secondary cache for this data. From now on a call like:

view plain print about
1user = entityLoad("User", {id=1}, true);

will use the secondary cache when possible.

To help work out what secondary cache is doing I did two things in User.cfc:

  1. added a version field which would definitively show when Hibernate had updated the record (another very cool underused feature)
  2. added my own getter to verify that secondary cache returns data via the cfc and not just flat

Now there are four types of cacheuse, here are the differences:

  • transactional cache is updated on the update statement and the next request of the data will get it straight from cache and not run a select
  • nonstrict-read-write Cache is updated on the first select after data is updated or inserted
  • read-write I could not get this to work with caching (docs say its the worst performing anyway)
  • read-only Only works with data that will not be updated. Produces an error if you attempt to update or insert records. (Marc Esher wrote a long, funny blog entry about using secondary cache to cache for a read-only view)

I'm going to look more into secondary cache and hopefully come up with real examples but for now I know two things: 1) its very powerful, 2) its very easy to use.

* If ColdFusion is started from the Builder server panel the console tab will display all the SQL Hibernate produces. I used it to see when Hibernate went to cache and when the database.

Starting Solr for ColdFusion on a Mac

Here is a straightforward way to start Solr when running ColdFusion 9 on a Mac. If it is not running in the ColdFusion Administrator under Data & Services > ColdFusion Collections the following message will appear:

Unable to retrieve collections from any of the Search Services. Please verify that atleast one of ColdFusion Search Services is installed and running.

To get it running run the following two lines in Terminal:

view plain print about
1cd /Applications/JRun4/servers/cfusion/cfusion-ear/cfusion-war/WEB-INF/cfusion/solr
2./cfsolr start

Thats for multi-instance installs for ColdFusion 9 standard install it will be:

view plain print about
1cd /Applications/ColdFusion9/solr
2./cfsolr start

Within seconds Solr will be up and running and collections will show on the page above.

(As a quick note don't delete core0 collection as its needed to get everything up and running.)

Sharing Data Across Instances with objectSave() and objectLoad()

Running multiple ColdFusion instances is pretty awesome from failover to performance. Sharing data among them throws up a couple possibilities:

  • Use a database (save from one, retrieve from another)
  • Use ColdFusion 9.0.1 with Distributed Ehcache and then use the cachePut() and cacheGet() functions.
  • Use objectSave() and objectLoad() while saving the object to disk.

Recently I needed to do just this with a dataset of structures and arrays that could have changed at anytime. This made option 1 a little tricky. Option 2 was my favorite but required some extra installs with Ehcache (which is a simple 7 step process, but would have required testing, yada, yada). So, option 3 was what we picked and it worked well.

Here is a simple example of how to do it. First create and save your object:

view plain print about
1myObject = {
2    funRun = [1,3,5],
3    hardcore = [10,26,1000]
4};
5objectSave( myObject, "myObject.cfo");

The objectSave() function takes two arguments; first the object, and second the filename. By default it saves it relatively to the running file. The documentation use a ".out" extension but I think a ".cfo" is more fun (ColdFusion Object or if you need to please your CFO tell them you named it after them!).

Retrieval is just as easy:

view plain print about
1<cfset myObject = objectLoad("myObject.cfo")>

And that will work across any instance.

If you have large objects you may run into issues with writes and saves coming at the same time. In that case wrap the objectLoad() in a try/catch.

ORM and Dates? You probably want timestamp

Data types for dates and times are slightly different across databases which often makes life difficult for us developers. ColdFusion ORM makes this pretty simple:

  • For a date with time use ormtype="timestamp"
  • Just a date use ormtype="date"

There are no other options!

Full example with the property tag:

view plain print about
1property name="aFullDateWithTime" ormtype="timestamp";
2property name="justADate" ormtype="date";

Quick fix for 9.0.1 dump not showing method changes

ColdFusion 9.0.1 introduces a small bug into cfdump where changes to function arguments will not show up in cfdump if the dump has already been run once.  I don't know the background but my guess is that in order to speed things up CF is using the cache to store the dump of function code.

The fix to this is to clear from cache anything that starts with _cf_dump with this code:

<cfscript> for ( key in cacheGetAllIds() ) {  if ( left( key, 8) == "_CF_DUMP") {   cacheRemove( key );  } }  </cfscript>

If you are doing a lot of looking at cfdump after changing methods (something I almost never do) then you can place that code in Application.cfc onRequestEnd.

Example of multiple datasources for ORM in 9.0.1

ColdFusion 9.0.1 brings multiple datasources support for ORM. Here is how to set it up.

In this example, we are going to use both the cfartgallery and cfbookclub datasources.  In the Application.cfc we will make the cfartgallery the default datasource.  Optionally we are going to set the dbcreate rules differently for the two datasources.  (We could also do this for the schema, catalog, dialect and sqlscript settings.)

 

component
{
this.name="multi";
this.ormEnabled=true;
this.datasource="cfartgallery";
this.ormsettings = { dbcreate={ cfartgallery="update",
cfbookclub="none"} };
}

 

From cfartgallery we will use the Art table as an object.  As cfartgallery is the default datasource, this is set up as usual:

 

component persistent="true"
{
property name="artID" fieldtype="id" ;
property name="artistID";
property name="artname";
property name="description";
property name="prize";
property name="largeimage";
property name="mediaID";
property name="issold";
property name="samColumn";
}

 

For any tables from the cfbookclub datasource there is an additional attribute "datasource" for the component:

 

component persistent="true" datasource="cfbookclub"
{
property name="bookID" fieldtype="id";
property name="authorID";
property name="title";
property name="bookdescription";
property name="isspotlight";
}

 

We can then get the data and dump it like so:

 

books = entityLoad("BOOKS", {}, {maxResults=2});
writeDump(books);
art = entityLoad("ART", {}, {maxResults=2});
writeDump(art);

 

 Pretty straighforward and pretty cool stuff.

The First Post

The first post. I've decided to start a blog primarily because I like writing. And having been a programmer for the last 10 years I haven't written much during that time. I also realize I like writing about technical issues and have a fair amount on my mind at the moment so it seems like a good time to start a blog. Note, I did not say I was a grammatically correct writer. So, expect the odd typo and grammatically incorrect sentence from time to time. This is a blog and I'm not going to use the grammar corrector in Word before posting.

I am also intrigued by social networking so InstantSpot seemed a good spot (pun intended) to start my blog.

I aim to cover a bunch of topics, some tutorials and explanations of basic ColdFusion functionality, thoughts on the future of the web and ColdFusion, conferences, and anything else that comes up. I will mostly be focusing on ColdFusion (and will probably start using cf as an abbreviation real soon) though I do hope to post on other non-cf topics like JavaScript, MySQL and databases in general and who knows what else in the future.

I am not going to aim to publish on a regular basis and have pledged to keep this blog a purely non-work time activity. I'm busy enough there anyway. Good busy. I also don't believe blogs die. So if I have nothing good or interesting to say I will say nothing. I also don't think this will turn into a news blog with short entries and links to the latest news. There are enough of those, and, I rely on them for my tech-news as much as anywhere else. In short, if you like what you are reading I suggest using the RSS feeds or subscribing over email.

More to come...

BlogCFC was created by Raymond Camden. This blog is running version 5.9.7. Contact Blog Owner