I use transactions in my "low-level" methods when neccesssary, as well as higher level methods.   However, I almost never include method calls within a transaction, only cfquerys..    Everything will work fine if you are religious with use of ctry/cfcatch/cfthrow. 
 
All of my lower level methods will throw an error if there is a problem during a database operation.  They can do their own non-SQL rollback operations when an error is caught, if needed, and the database takes care of the rest.  The higher methods always wrap calls to lower level methods in a cftry as well, so you don't need to worry about rolling back subsequent operations when an error has occured, because the methods won't even be called. 
 
Your higher level methods can catch different types of errors and handle each one differently...sometimes you may want to proceed with the operation, sometimes you will want to bubble the error up, etc.
 
Here is an example of the technique I tend to use:
 
<cfcomponent displayname="com.foo.highLevel" output="yes">
 <cfset instance = structNew() />
 <cffunction name="init" access="public" output="no" returntype="com.foo.highLevel">
  <cfargument name="dsn" type="string" required="yes" />
  <cfset instance.dsn = arguments.dsn />
  <cfreturn this />
 </cffunction>
 <cffunction name="delete" access="public" output="no" returntype="boolean">
  <cfargument name="categoryID" type="numeric" required="yes" />
  <cfset var lowlevel = createObject("component","com.foo.lowLevel").init(instance.dsn) />
  <cfset var archiveCategory = True />
  
  <!--- a lower level method call that shouldn't jeopordize the transaction --->
  <cftry>
   <cfset lowLevel.archiveForCategory(arguments.categoryID) />
   <cfcatch type="com.foo.lowLevel.nomeNonFatalException">
    <cfset archiveCategory = False />
   </cfcatch>
   <cfcatch type="com.foo.lowLevel.someFatalException">
    <cfthrow type="com.foo.highLevel.deleteException" message="#cfcatch.message#" />
   </cfcatch>
  </cftry>
 
  <!--- lower level calls that can jeopordize the transaction --->
  <cftry>
   <cfset lowLevel.deleteForCategory(arguments.categoryID) />
   <cfif archiveCategory>
    <cfset archive(arguments.categoryID) />
   </cfif>
   <cfcatch type="com.foo">
    <cfthrow type="com.foo.highLevel.deleteException" message="#cfcatch.message#" />
   </cfcatch>
  </cftry>
  <cftry>
   <cftransaction>
    <cfquery name="qDeleteCat" datasource="#instance.dsn#">
     delete Category where categoryID=#arguments.categoryID#
    </cfquery>
   </cftransaction>
   
   <cfcatch type="Database">
    <cfthrow type="com.foo.highLevel.category.deleteException" message="#cfcatch.message#" />
   </cfcatch>
  </cftry>
  <cfreturn True />
 </cffunction>
 <cffunction name="archive" access="private" output="no" returntype="boolean">
  <!--- ... --->
 </cffunction>
</cfcomponent>
 
Basically, good exception handling can take the place of a need for large transaction blocks and keep your rollback operations much more modular.  I suppose you could also build a custom transaction system like Barney suggested...but honestly that seems like major pain in the ass and I think it would be much more difficult to implement than it looks. 

Jim Davis <[EMAIL PROTECTED]> wrote:
I agree that for the most part the implementing developer of the CFC
shouldn't need to worry about transactions - for the most part I wonder
if they even need to know that there's a database. ;^)

I'm new to OO and struggled with this as well. I ended up with (perhaps
a poor) model that seems to do it. In my case I first dealt with this
because of recursive calls. For example a component might, as a
property, link to another (a parent or a child). When I delete one I
wanted to cascade the delete to the children.

In my case all of my persistent methods call out to external includes.
A "datasource" component determines which include file to call (this way
the same object model can support pretty much any persistence method).
The call uses an "internal" argument "RecursiveCall" like this:


template="DB_#this.DPDataSource.getType()#\Venue_save.cfm">


template="DB_#this.DPDataSource.getType()#\Venue_save.cfm">



So a transaction is begun only when called from outside. When called
from inside the internal argument is used. Since the DB logic is out in
an include file there's no complexity in setting the transaction or not.

I had also considering using the current taglist to determine if we were
in a transaction... but that seemed heavy to me.

It would be nice if the CFTransaction tag had some sort of "continue"
action that would continue a current action or start a new one.

The main problem with this is that although this will maintain database
integrity it won't maintain the integrity of the component model. You
have to do something to rebuild the component or otherwise deal with
things if an error occurs and you have to roll things back.

Again... this may not be a very good way to do things, but it's working
fairly well for me.

Jim Davis

> -----Original Message-----
> From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] On
Behalf
> Of Nathan Dintenfass
> Sent: Tuesday, November 18, 2003 5:03 PM
> To: [EMAIL PROTECTED]
> Subject: RE: [CFCDev] cftransactions in low level modules
>
> Can I ask: why not just build a CFC layer that exposes a simple API
that
> does the transaction internally, then have that layer make use of your
> underlying persistence mechanism? That is, why make an end-developer
have
> to even think about transactions if they are a known quantity in
advance?
> Instead, just build one or more CFCs that are the "business layer" and
> then
> let the developer use those. So, for instance, if you have a
persister
> with
> methods like:
>
> insertUser(id,fname,lname,email);
> associateUserWithDepartment(userID,deptID);
> associateUserAsDirectReport(userID,bossID);
>
> and you have a transaction, which will be "insert a user and associate
> him/her with a given department and make them a direct a report of
another
> given user" you can have your persister layer expose a granular API
that
> is
> transaction agnostic, then build a UserManager.cfc (or whatever) that
has
> a
> method like:
>
> saveUser(user,deptID,directReportID);
>
> Your saveUser method can then call the methods of the persister in a
> transaction.
>
> I am no OO expert, but in my experience thus far with CFCs I find it's
> generally best to not have the end-developer interact directly with a
> persistence layer in anything other than the simplest systems,
preferring
> instead to encapsulate that complexity with a higher-order API.
>
> Or, am I missing the boat here?
>
> - Nathan
>
>
>
>
>
>
>
> > -----Original Message-----
> > From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED]
> > Behalf Of Barney Boisvert
> > Sent: Tuesday, November 18, 2003 1:47 PM
> > To: [EMAIL PROTECTED]
> > Subject: RE: [CFCDev] cftransactions in low level modules
> >
> >
> > I'm not sure I have an answer to that one, as you're kind of mixing
> > different styles. I think I'd still say that a transaction can only
be
> > controlled from the upper level, outside the function, so you'd
> > need to add
> > CFTRANSACTION tags around the first CFINVOKE, just like the second
set.
> >
> > Here's another idea:
> >
> > This is totally shooting from the hip (with code below), but what
about
> > making beginTransaction, rollbackTransaction and
> > commitTransaction functions
> > in your CFC. Inside the function body, they'd use a
> > request-scoped flag to
> > track whether there is a transaction currently open, and would
> > only actually
> > do anything when the state changed. Then you would call them from
> inside
> > your functions (taking care of case one), and you could also call
> > them from
> > outside the functions (case two), in which case the inside calls
> > wouldn't do
> > anything, because there is already a transaction in progress.
> >
> > function beginTransaction() {
> > if (request.transactionNext EQ 0) {
> > CFTRANSACTION ACTION=""
> > }
> > request.transactionNext = request.transactionNext + 1;
> > }
> >
> > function rollbackTransaction() {
> > if (request.transactionNext EQ 0) {
> > CFTHROW TYPE="NoValidTransactionException"
> > }
> > CFTRANSACTION ACTION=""
> > request.transactionNext = 0;
> > }
> >
> > function commitTransaction() {
> > request.transactionNext = request.transactionNext - 1;
> > if (request.transactionNext EQ 0) {
> > CFTRANSACTION ACTION=""
> > }
> > }
> >
> > > -----Original Message-----
> > > From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED]
> > > Behalf Of Jay Gibb
> > > Sent: Tuesday, November 18, 2003 12:34 PM
> > > To: [EMAIL PROTECTED]
> > > Subject: RE: [CFCDev] cftransactions in low level modules
> > >
> > >
> > > Hey Barney,
> > >
> > > What about if you're not using OO modelling? This is for a
procedural
> > > system. A pseudo implementation might be...
> > >
> > >
> > > > > > returnVariable="result">
> > >
> > >
> > >
> > > > > > useTransaction="no"
> > > returnVariable="result">
> > >
> > > > > > useTransaction="no"
> > > returnVariable="result">
> > >
> > > > > > useTransaction="no"
> > > returnVariable="result">
> > >

> > >
> > > - j.
> > >
> >
> > ----------------------------------------------------------
> > You are subscribed to cfcdev. To unsubscribe, send an email
> > to [EMAIL PROTECTED] with the word 'unsubscribe cfcdev'
> > in the message of the email.
> >
> > CFCDev is run by CFCZone (www.cfczone.org) and supported
> > by Mindtool, Corporation (www.mindtool.com).
> >
> > An archive of the CFCDev list is available at
> www.mail-archive.com/[EMAIL PROTECTED]
>
> ----------------------------------------------------------
> You are subscribed to cfcdev. To unsubscribe, send an email
> to [EMAIL PROTECTED] with the word 'unsubscribe cfcdev'
> in the message of the email.
>
> CFCDev is run by CFCZone (www.cfczone.org) and supported
> by Mindtool, Corporation (www.mindtool.com).
>
> An archive of the CFCDev list is available at www.mail-
> archive.com/[EMAIL PROTECTED]


----------------------------------------------------------
You are subscribed to cfcdev. To unsubscribe, send an email
to [EMAIL PROTECTED] with the word 'unsubscribe cfcdev'
in the message of the email.

CFCDev is run by CFCZone (www.cfczone.org) and supported
by Mindtool, Corporation (www.mindtool.com).

An archive of the CFCDev list is available at www.mail-archive.com/[EMAIL PROTECTED]


Do you Yahoo!?
Protect your identity with Yahoo! Mail AddressGuard

Reply via email to