
Wish List
Tags
adobe air ajax cf community cfml coldfusion examples ext flash flex javascript max2007 max2008 misc programming railo technology ui
Recent Entries
Wikipedia Raises 6 Million
30GB Zunes Self Destruct On New Years
Happy New Year
Server Move Update 2
Server Move
Currently Reading
Search
RSS
Subscribe
Blogroll
An Architect's View
Ben Forta
CFSilence
Coldfusion Jedi
Rey Bango
TalkingTree
Cftransaction Explained
I've seen some code lately that has prompted me to write this blog entry. The code in question uses cftransactions heavily and in my opinion in completely unnecessary places in addition to the use of serializable isolation levels which are also unnecessary. So what is the cftransaction tag used for, when should I use it and what about that isolation level thing I just mentioned?
Lets start with the first question.
What is CFtransaction?
The CFtransaction tag allows a developer to tell the database to treat a series of queries as a single unit of work. Transactions are all or nothing either all queries execute successfully or none are committed to the database. (For Completion sake in the case of CFtransactions in CF8 that isn't exactly true because of the new "save point" attribute but we won't go into that now). According to the wikipedia definition of a database transaction
A database transaction, by definition, must be atomic, consistent, isolated and durable. These properties of database transactions are often referred to by the acronym ACID
So in the following code example all the queries inside the cftransaction block will be executed by the database as a single unit of work.
<cftransaction>
<cfquery name="first" datasource="#request.dsn#">
update tblOne
set name=<cfqueryparam value="#arguments.name#" cfsqltype="cf_sql_varchar">
</cfquery>
<cfquery name="second" datasource="#request.dsn#">
update tblTwo
set name=<cfqueryparam value="#arguments.name#" cfsqltype="cf_sql_varchar">
</cfquery>
<cfquery name="third" datasource="#request.dsn#">
update tblThree
set name=<cfqueryparam value="#arguments.name#" cfsqltype="cf_sql_varchar">
</cfquery>
</cftransaction>
Note also that CFtransaction tag adds overhead (time). Use it wisely. It's not something you need to put around a single select statement, same goes for a single update statement, or for that matter any single query statement. A single statement is already a single unit of work so the use of a CFtransaction in this case is just adding unnecessary overhead to your application.
What is Isolation Level?
Isolation is a database property that defines how and when changes made by one transaction are visible to another concurrent transaction. This means that every time you execute a query against a database it's in the context of a transaction. Every transaction has an isolation level associated to it, whether you use the cftransaction tag or not.
The isolation level defines how the database will lock the resources that one transaction is accessing as well as to how well it adheres to locks by other concurrent transactions
Isolation level comes in a number of different flavors from quite unrestrictive (read_uncommitted) to very restrictive (serializable).
The default isolation level for most databases is READ_COMMITTED, this does not allow dirty reads but it does allow so called phantom reads.
The most restrictive isolation level is SERIALIZABLE. This basically forces the database to execute all the transactions completely isolated from another; one transaction is executed after the other.
Because of this you should be very careful when and where you use SERIALIZABLE isolation level. The main purpose of the SERIALIZABLE isolation is to prevent race conditions in your database
So when should you use a cftransaction?
-
When the execution of a successive queries depends on the successful completion of the previous.
Note this is just for example purposes, you wouldn't really do it this way.
<cftransaction>
<cfquery name="deductFromAccount" datasource="#request.dsn#">
update customerAccount
set accountBalance = accountBalance - #amount#
where customerID = 1
</cfquery>
<cfquery name="addToAccount" datasource="#request.dsn#">
update customerAccount
set accountBalance = accountBalance + #amount#
where customerID = 2
</cfquery>
</cftransaction> - When retrieving the id of an inserted record.
notice here the use of isolation="serializable". Depending on your database this may be the only way to be sure that you get the correct ID back as another row may be inserted before the first insert is committed, by putting the two statements in a transaction with the isolation of serializable you guarantee that another record won't be inserted between the first insert and the select.
<cftransaction isolation="serializable">
<cfquery name ="insertRecord" datasource="#request.dsn#">
insert into foo (firstCol,SecondCol)
values(1,2)
</cfquery>
<cfquery name="getID" datasource="#request.dsn#">
select max(id) from foo
</cfquery>
</cftransaction>
Further Reading
Simon Horwith has a good write up on Devnet on transactions that is worth a read.
On wikipedia you can read more about the individual isolation levels.
You can also read about race conditions on wikipedia but not in the context of databases, but it should provide you with a good idea of the problem.
Happy Coding...


around every database transaction. You made me appreciate that I was just
adding extra unnecessary overhead on surrounding single transactions with cftransaction.
<cfquery name ="insertRecord" datasource="#request.dsn#">
set nocount on
insert into foo (firstCol,SecondCol)
values(1,2)
select scope_identity() as new_id
set nocount off
</cfquery>
No cftransaction is required, and insertRecord.new_id will contain your newly created ID. If you then needed to insert this new key into additional tables, you would use cftransaction in that case with additional cfquery tags. Note: Many people mistakenly use @@IDENTITY instead of SCOPE_IDENTITY(), but you should not do that if there is any chance of there ever being triggers on the table(s) because you would then get back the LAST identity, not necessarily the one for the table in which you inserted the record.
There has been quite a bit of a debate about whether to use @@identity or scope_identity(). I tend to agree with you on the use of scope_identity instead of @@identity which eliminates the need for the serializable isolation as you indicated.
Coldfusion 8 also has the ability to return the identity of an inserted row when using the result attribute. I would assume that to also be a safe method too.
That feature in CF8 is very cool...you do have to "normalize" the returned result structure key based on the DB you are using, however. There is a UDF on CFLib that takes care of that for you at:
http://cflib.org/udf/getGeneratedKey
Ray Camden just added that tip to the CF Cookbook at:
http://www.coldfusioncookbook.com/entry/146/How-do...