Reactor save() & transaction gotcha
Tuesday August 1, 2006 9:50 PM
Word Count: 311
So today I came across a problem that the fine folks on the reactor list server help me solve. I wanted to run a transaction on multiple object so that if any of them failed I could back out. Here is a quick example of what I was trying to do.
<cftransaction action="BEGIN">
<cfset contactRecord = application.reactor.createRecord("contact")/>
<cfset contactRecord.setContactId(form.contactId)>
<cfset contactRecord.setFname(form.fname)>
<cfset contactRecord.setLname(form.lname)>
<cfset contactRecord.validate() />
<cfset errorCollection = contactRecord._getErrorCollection() />
<cfset contactRecord.save()>
<cfset userRecord = application.reactor.createRecord("user")/>
<cfset userRecord.setuserId(form.userId)>
<cfset userRecord.setUsername(form.username)>
<cfset userRecord.setPassword(form.password)>
<cfset userRecord.validate() />
<cfset errorCollection = userRecord._getErrorCollection() />
<cfset contactRecord.save()>
<cfif ErrorCollection.hasErrors()>
<cftransaction action="ROLLBACK"/>
<cfelse>
<cfset contactRecord.save()>
<cftransaction action="COMMIT"/>
<cflocation URL="contacts.cfm" addtoken="false"/>
</cfif>
</cftransaction>
<cfset contactRecord = application.reactor.createRecord("contact")/>
<cfset contactRecord.setContactId(form.contactId)>
<cfset contactRecord.setFname(form.fname)>
<cfset contactRecord.setLname(form.lname)>
<cfset contactRecord.validate() />
<cfset errorCollection = contactRecord._getErrorCollection() />
<cfset contactRecord.save()>
<cfset userRecord = application.reactor.createRecord("user")/>
<cfset userRecord.setuserId(form.userId)>
<cfset userRecord.setUsername(form.username)>
<cfset userRecord.setPassword(form.password)>
<cfset userRecord.validate() />
<cfset errorCollection = userRecord._getErrorCollection() />
<cfset contactRecord.save()>
<cfif ErrorCollection.hasErrors()>
<cftransaction action="ROLLBACK"/>
<cfelse>
<cfset contactRecord.save()>
<cftransaction action="COMMIT"/>
<cflocation URL="contacts.cfm" addtoken="false"/>
</cfif>
</cftransaction>
The problem with this code is that it was throwing a transaction nested exception error. You can not place cftransaction tags inside themselves. I couldnt figure out why that was until I dug into the abstractRecord.cfc and found this.
<cffunction name="save" access="public" hint="I save the record." output="false" returntype="void">
<cfargument name="useTransaction" hint="I indicate if this save should be executed within a transaction." required="no" type="boolean" default="true" />
<cfif arguments.useTransaction>
<cfset saveInTransaction() />
<cfelse>
<cfset executeSave() />
</cfif>
</cffunction>
<!--- saveInTransaction --->
<cffunction name="saveInTransaction" access="private" hint="I save the record in a transaction." output="false" returntype="void">
<cftransaction>
<cfset executeSave() />
</cftransaction>
</cffunction>
<cfargument name="useTransaction" hint="I indicate if this save should be executed within a transaction." required="no" type="boolean" default="true" />
<cfif arguments.useTransaction>
<cfset saveInTransaction() />
<cfelse>
<cfset executeSave() />
</cfif>
</cffunction>
<!--- saveInTransaction --->
<cffunction name="saveInTransaction" access="private" hint="I save the record in a transaction." output="false" returntype="void">
<cftransaction>
<cfset executeSave() />
</cftransaction>
</cffunction>
It now made perfect sense why this was happening. If you would like to tell reactor not to execute a save in a transaction simply pass false when calling the save method, the default value for this is true.
<cfset contactRecord.save(false)>
