Working With Transfer ORM - Transfer Transaction Advice
2008 November 28
Maintaining referential integrity in your database is a good thing. Any decent RDBMS will include ACID (Atomicity, Consistency, Isolation, Durability) transaction support for ensuring consistency and recoverability of your database. With the Transfer ORM Transaction object, one can apply transactions easily to a Service or Gateway. Here's how one can use it to wrap all "save" and "delete" methods for a Service.
The Transfer transaction object applies an aspect oriented programming (aop) advice to a method specified in the transaction advise() method. Which makes it dead simple to apply transactions with Transfer.
<cfset transaction.advise(targetComponent,method)>
You can also specify multiple methods using a regular expression.
<cfset transaction.advise(targetComponent,regex)>
The first step is to create singleton instance of the Transfer Transaction object in Coldspring and pass that as a constructor or setter argument to the Service. Here's a snippet from a sample Coldspring xml file showing the transfer, datasource, transaction, Service and Gateway bean definitions for a fictitious Image Gallery application with two objects. A Gallery that can have one to many Images. I've left out the transfer factory setup for brevity.
<bean id="transfer" factory-bean="transferFactory" factory-method="getTransfer" />
<bean id="datasource" factory-bean="transferFactory" factory-method="getDatasource" />
<bean id="transaction" factory-bean="transferFactory" factory-method="getTransaction" />
<bean id="GalleryGateway" class="model.gallery.GalleryGateway">
<constructor-arg name="transfer">
<ref bean="transfer" />
</constructor-arg>
<constructor-arg name="datasource">
<ref bean="datasource" />
</constructor-arg>
</bean>
<bean id="ImageGateway" class="model.gallery.ImageGateway">
<constructor-arg name="transfer">
<ref bean="transfer" />
</constructor-arg>
<constructor-arg name="datasource">
<ref bean="datasource" />
</constructor-arg>
</bean>
<bean id="GalleryService" class="model.gallery.GalleryService">
<constructor-arg name="transaction">
<ref bean="transaction" />
</constructor-arg>
<property name="GalleryGateway">
<ref bean="GalleryGateway" />
</property>
<property name="ImageGateway">
<ref bean="ImageGateway" />
</property>
</bean>
In the Service init method, the transaction advice is applied with one line of code.
<cffunction name="init" access="public" output="false" returntype="any">
<cfargument name="transaction" type="transfer.com.sql.transaction.Transaction" required="true" />
<cfset arguments.transaction.advise(this,"^(save|delete)")>
<cfreturn this>
</cffunction>
To delete a Gallery and all of its Images, I might have the following method in my Service.
<cffunction name="deleteGallery" access="public" returntype="void" output="false">
<cfargument name="GalleryId" type="numeric" required="true">
<cfset var Gallery = getGalleryGateway().get(arguments.GalleryId)
<cfset var images = Gallery.getImageArray()>
<cfset var i = 1>
<cfloop from="1" to="#ArrayLen(images)#" index="i">
<cfset getImageGateway().delete(images[i])>
</cfloop>
<cfset getGalleryGateway().delete(Gallery)>
</cffunction>
Having applied the transaction advice to all save and delete methods in my Service ensures that the database will be left intact should anything fail. For an in depth explanation of working with transactions in transfer, see the wiki documentation.