Transfer ORM Quickstart

Transfer ORM is, as the name implies, an object relational mapper for Coldfusion. Transfer has a myriad of advanced features which can be intimidating for newcomers. Fortunately, Transfer can be installed and used very easily without learning some of the more advanced features. What follows are instructions you can use to install, configure and use transfer in a few simple steps.

Installation

UPDATE: Release Candidate 1.0 is now available.

The last official release of transfer is 0.6.3. This version is now quite old and it is recommended that you use the bleeding edge release (BER) from subversion. If you have any misgivings about using the BER, rest assured that many production systems are running the transfer BER. It's stable.

To install the transfer BER, point your subversion client to http://svn.riaforge.org/transfer and checkout or export the source from /trunk/transfer. You can either set a mapping for "/transfer" in the cf administrator, or drop the source transfer folder in your web root.

Configuration

To use transfer in an application you'll need to setup two configuration files. One for the datasource that transfer will use and another to describe the objects that transfer maps from your relational database. These files are typically named Datasource.xml and Transfer.xml, but you can use any name you like and/or append a .cfm extension to prevent the files from being visible in a web browser.

Datasource.xml

The transfer datasource.xml config specifies the values for the following:

- a datasource name that currently exists in the cf administrator - a username (if required - leave blank otherwise) - a password (if required - leave blank otherwise)

<?xml version="1.0" encoding="UTF-8"?>
<datasource xsi:noNamespaceSchemaLocation="../xsd/datasource.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<name>dsn_name</name>
<username></username>
<password></password>
</datasource>

Transfer.xml

The transfer.xml file is used to define the properties and relationships of transfer objects that are mapped to the relational tables in your database. Objects can have onetomany, manytoone, or manytomany relationships. The following xml is the object definitions from the tBlog sample app.

<?xml version="1.0" encoding="UTF-8"?>
<transfer xsi:noNamespaceSchemaLocation="../xsd/transfer.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InvalidTagDefinitions>
<package name="user">
<!-- User details -->
<InvalidTag name="User" table="tbl_User">
<id name="IDUser" type="numeric"/>
<property name="Name" type="string" column="user_Name"/>
<property name="Email" type="string" column="user_Email"/>
</object>
</package>
<package name="system">
<!-- Different categories for Blog Posts -->
<InvalidTag name="Category" table="tbl_Category">
<id name="IDCategory" type="numeric"/>
<property name="Name" type="string" column="category_Name"/>
<property name="OrderIndex" type="numeric" column="category_OrderIndex"/>
</object>
</package>
<package name="post">
<!-- A Blog Post, with a Decorator of type com.Post -->
<InvalidTag name="Post" table="tbl_Post" decorator="tblog.com.Post">
<id name="IDPost" type="numeric"/>
<property name="Title" type="string" column="post_Title"/>
<property name="Body" type="string" column="post_Body"/>
<property name="DateTime" type="date" column="post_DateTime"/>
<!-- Link between a Post and the User it who wrote it -->
<manytoone name="User">
<link to="user.User" column="lnkIDUser"/>
</manytoone>
<!--
Link between a post and it's array of Comments
-->

<onetomany name="Comment">
<link to="post.Comment" column="lnkIDPost"/>
<collection type="array">
<order property="DateTime" order="asc"/>
</collection>
</onetomany>
<manytomany name="Category" table="lnk_PostCategory">
<link to="post.Post" column="lnkIDPost"/>
<link to="system.Category" column="lnkIDCategory"/>
<collection type="array">
<order property="OrderIndex" order="asc"/>
</collection>
</manytomany>
</object>
<!-- A comment for a blog post -->
<InvalidTag name="Comment" table="tbl_Comment">
<id name="IDComment" type="numeric"/>
<property name="Name" type="string" column="comment_Name"/>
<property name="Value" type="string" column="comment_Value"/>
<property name="DateTime" type="date" column="comment_DateTime"/>
</object>
</package>
</objectDefinitions>
</transfer>

Integration

Using Transfer with Coldspring

<bean id="transferFactory" class="transfer.TransferFactory">
<constructor-arg name="datasourcePath"><value>/config/transfer/Datasource.xml</value></constructor-arg>
<constructor-arg name="configPath"><value>/config/transfer/Transfer.xml</value></constructor-arg>
<constructor-arg name="definitionPath"><value>/path/to/definitions</value></constructor-arg>
</bean>

<bean id="datasource" factory-bean="transferFactory" factory-method="getDatasource" />

<bean id="transfer" factory-bean="transferFactory" factory-method="getTransfer" />

Using transfer as an application scope "singleton".

<cfset transferConfig = StructNew() />
<cfset transferConfig["datasourcePath"] = "/config/transfer/Datasource.xml" />
<cfset transferConfig["configPath"] = "/config/transfer/Transfer.xml" />
<cfset transferConfig["definitionPath"] = "/path/to/definitions" />
<cfset application.transferFactory = CreateObject("component","transfer.TransferFactory").init(transferConfig) />
<cfset application.transfer = application.transferFactory.getTransfer() />

Usage

To grasp the full breadth of the transfer API, you really need to review the Transfer docs. I'll cover the four methods you will likely work with the most in the beginning. They are, get(), new(), save() and delete().

Using the tBlog object definitions above, to retrieve a post object use:

<cfset application.transfer.get("post.Post",1) />

To have transfer return a new instance:

<cfset post application.transfer.new("post.Post") />

To modify and save a new post:

<!--- create a new post --->
<cfset post = application.transfer.new("post.Post") />
<!--- retrieve category with id 1 --->
<cfset category = application.transfer.get("system.Category",1) />
<!--- set properties (not shown) --->
<!--- set category for post --->
<cfset post.setCategory(category) />
<!--- save the post --->
<cfset application.transfer.save(post) />

A small, but significant, benefit of using Transfer ORM, is that when you save an new object the object's state reflects it's new persisted state. So you can find out the new id by simply calling.

<cfset newid = post.getID() />

Lastly, transfer delete() method works like save(), you pass the transfer object to the method.

<cfset application.transfer.delete(post) />

Much, much, more

This quick start is intended to help impatient types (like myself) get a taste for how simple it is to start using Transfer ORM. As you become comfortable with the Transfer API, you will definitely want to explore features like Transfer Object Decorators, the Transfer Event Model and Transfer Query Language (TQL). Mark Mandel has put a great deal of effort to into Transfer since it turned POSS. If you are not comfortable using the bleeding edge source code from subversion, keep an eye out for a 1.0 Release Candidate.

Of Views and Models and Working With Transfer ORM

Yesterday, I asked Mark Mandel for some advice on tuning my object model for performance. Mark, being an ever-so-helpful (P)OSS author, gave me a few suggestions. Turns out that someone asked basically the same question on the transfer list today. Jaime Metcher made a couple of excellent points on choosing between queries and object composition. Paraphrasing, Jaime, when working with large data sets (the type of thing you might be tempted to use arrays of objects for), you will get better performance using a query. If you want to display a single instance of some object and the view is truly a composite, then your model should reflect that composition. Understanding this helps with choosing between onetomany or manytoone relationships for your object compositions.

[More]

Working with Transfer ORM: TQL, MSSQL and Reserved Words

I'm definitely marking this down in the "I should have known better" category. If you use MSSQL, you probably know by know that "user" is a reserved word. So, unless you use [user] as a table identifier it's imperative that you name your table something like "tbl_user" or use the plural "users". I don't like plural table names, so I opted for tbl_user. I just ran into a gotcha with my TQL for a query against tbl_user. Why? Here's a snippet from my method to check the uniqueness of a user e-mail.

// local vars struct
var local = StructNew();
// default result
local.result = false;
// tql for list
local.tql = "from user.User as user where user.Email = :Email AND user.Id != :Id";
//create a query object
local.query = getTransfer().createQuery(local.tql);
//set the named parameters
local.query.setParam("Email", getEmail(), "string");
local.query.setParam("Id", getId(), "numeric");
//run it
local.userList = getTransfer().listByQuery(local.query);
// if no records exist e-mail passes unique test
if (local.userList.recordcount eq 0)
{
	local.result = true;
}	
return local.result;

My transfer object class name is user.User, so why not alias that as "user"? That's a logical alias, right? Wrong!

After pulling my hair out debugging the ever-so-descriptive error message, [Macromedia][SQLServer JDBC Driver][SQLServer]Line 1: Incorrect syntax near '.'.

I finally decided to write up a unit test to try and get some meaningful debugging information. From there I pulled a query, ran that in the Query Analyzer. Scratched head a bit more, then...voila! Reserved word issue. How silly....

Of course, this is not purely a Transfer or even TQL problem, merely a cautionary tale regarding reserved words in MSSQL that I happened upon via Transfer TQL.

For the record, the new TQL query reads:

local.tql = "from user.User where user.User.Email = :Email AND user.User.Id != :Id";			

The other big a-ha moment for me is that if an error occurs within a method invocation, I should debug by unit testing, not re-initializing, rinsing and repeating on the client side.

Working with Transfer ORM: An Event Model Example

If you use Transfer ORM you may have seen Mark Mandel's Advanced Transfer ORM Techniques presentation. One of the most interesting portions of this presentation (for me) was his coverage of the Transfer Event Model. Mark demonstrates how to setup a service (in this case a CryptService) as a Transfer Event listener. There are several events that objects can register to as listeners. One event type is "AfterNew", which is fired when a new transfer object is created. In the presentation Mark demonstrates how to leverage the Event Model with an AfterNew event and a decorated transfer object to encrypt/decrypt a user password. I wanted to use this concept for a new project, but rather than copying code verbatim from the presentation, I decided to try something a little different.

[More]

Scaffolding a Generic Admin - Part 6 - Bean, Form and List Views - The Finale

In this post I'll provide a deeper insight into how Dispatcher works and introduce the three generic views that make up the "generic admin". After generating some code with Illudium, we'll have all the pieces to perform CRUD operations...

[More]

Scaffolding a Generic Admin - Part 5 - Dispatcher, Transfer and ColdSpring Setup and Config

I introduced a lot of pieces to the puzzle during parts 1 through 4 of this series and with this post, I hope to put them together. We'll look at a file and folder setup, ColdSpring and Transfer XML configuration files and the other files that form application for the generic admin...

[More]

Scaffolding a Generic Admin - Part 3 - Illudium Code Generator Template

If you are not using Illudium to generate your application classes, I encourage you to check it out. Out of the box, the code generator comes with several templates for generating CFCs, Transfer and ColdSpring XML, Actionscript VOs (value objects) and TOs (simple transfer objects designed for Flex development). Modifying, or creating your own templates is encouraged and easy. The template I developed for this series uses ColdFusion templating, but you can also use XSLT or a combination of both...

[More]

Use ColdSpring, Transfer, Illudium and Dispatcher to Scaffold a Generic Admin - An Introduction

Generating scaffolds for list, edit and view pages for business objects is an essential part of a many frameworks (in many languages) available to developers. I've done a little window shopping with RoR, Symfony, and MG:U, but have never committed the time to make a go of any one framework. When recently faced with a "quick and dirty" build for trivia game for a friend, I felt that familiar dread of building a "back-end" administration system. Fortunately, I had spent enough time working with ColdSpring, Illudium, Transfer and my own Event Delegation based front controller (which I call Dispatcher), that I was willing to give building my own scaffold based generic admin a go...

[More]

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