ibatis-user-java mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Niels Beekman" <n.beek...@wis.nl>
Subject RE: Transaction question
Date Sat, 20 Aug 2005 13:21:40 GMT
Brian,

Sorry for the late response, but I've been rather busy.

You are right, my code does not work in the situation you mention. I
only tried it with autocommit-semantics, i.e. I never called
transactionmethods explicitly. Besides, I use a custom
transactionmanager which supports nesting in a similar way as your code.
I would not have problems at all *if* iBATIS didn't bind its
resource-disposal to transactions (endTransaction). This causes
"ResultSet already closed" exceptions when nesting DAO-calls. I would
really like to see this solved/fixed/worked around by the devteam,
because these people have a good overview of the way
transactions/resources work, I obviously do not :)
It would also help to have some architectural overview (schema) on the
website.

Grtz,

Niels

-----Original Message-----
From: Barnett, Brian W. [mailto:brian.barnett@pearson.com] 
Sent: donderdag 18 augustus 2005 17:51
To: 'user-java@ibatis.apache.org'
Subject: RE: Transaction question

Niels,
Thanks for the info. Maybe I am not totally understanding how your
modified
code works, but is it meant to handle nested transactions?

If a transaction has already been started, then your modified code will
simply call invoke. I understand that. What happens when a nested
transaction calls commitTransaction()? (Like doSomethingElse() calling
doSomething() in the sample code below.) commitTransaction() gets called
twice in this example. I believe it will throw an exception.

What I was considering was to add a ThreadLocal transactionCounter
variable
to DaoContext. start, commit and end transaction methods might look
something like this:

  public void startTransaction() {
    transactionCounter++;          // <-- NEW LINE OF CODE
    if (transactionCounter == 1) { // <-- NEW LINE OF CODE
      if (state.get() != DaoTransactionState.ACTIVE) {
        DaoTransaction trans = transactionManager.startTransaction();
        transaction.set(trans);
        state.set(DaoTransactionState.ACTIVE);
        daoManager.addContextInTransaction(this);
      }
    }                              // <-- NEW LINE OF CODE
  }

  public void commitTransaction() {
    if (transactionCounter == 1) { // <-- NEW LINE OF CODE
      DaoTransaction trans = (DaoTransaction) transaction.get();
      if (state.get() == DaoTransactionState.ACTIVE) {
        transactionManager.commitTransaction(trans);
        state.set(DaoTransactionState.COMMITTED);
      } else {
        state.set(DaoTransactionState.INACTIVE);
      }
    }                              // <-- NEW LINE OF CODE
  }

  public void endTransaction() {
    try {                            // <-- NEW LINE OF CODE
      if (transactionCounter == 1) { // <-- NEW LINE OF CODE
        DaoTransaction trans = (DaoTransaction) transaction.get();
        if (state.get() == DaoTransactionState.ACTIVE) {
          try {
            transactionManager.rollbackTransaction(trans);
          } finally {
            state.set(DaoTransactionState.ROLLEDBACK);
            transaction.set(null);
          }
        } else if (transactionCounter == 1) {
          state.set(DaoTransactionState.INACTIVE);
          transaction.set(null);
        }
      }                     // <-- NEW LINE OF CODE
    } finally {             // <-- NEW LINE OF CODE
      transactionCounter--; // <-- NEW LINE OF CODE
    }                       // <-- NEW LINE OF CODE
  }

The idea being that the transaction related logic only executes when we
are
dealing with the first transaction bracket that was opened. I haven't
actually tried this out, but I think I will. Let me know if you, or
anyone
else, sees any problems with this approach.

Thanks,
Brian Barnett

-----Original Message-----
From: Niels Beekman [mailto:n.beekman@wis.nl] 
Sent: Wednesday, August 17, 2005 3:00 PM
To: user-java@ibatis.apache.org
Subject: RE: Transaction question


Hi,

I'm facing this exact same problem however in a somewhat different
context,
see the following archived thread:

http://www.mail-archive.com/ibatis-user-java@incubator.apache.org/msg025
80.html

http://www.mail-archive.com/user-java@ibatis.apache.org/msg00036.html

I recently restarted my investigation into this problem and have tried
some
hacks in the iBATIS code, the changes were made in DaoProxy.java (which
proxies DAO-interfaces to provide transaction-semantics) and
DaoContext.java
(which handles the transactions itself). Of course this is rather messy,
but
I really do not like the SavePoint-support mentioned in the thread
above, I
think it is rather a workaround than a solution.

Anyway, my changes (totally unverified, without any guarantees) in
package
com.ibatis.dao.engine.impl:

DaoContext.java, added isTransactionRunning():

public boolean isTransactionRunning() {
  return transaction.get() != null;
}

DaoProxy.java, modified invoke(): see attached file.

This seems to work pretty good in my case, however further investigation
is
required.

I hope the iBATIS devteam can comment on my solution, whether you think
it
will work, or when you believe it really sucks :)

Greetings,

Niels

-----Original Message-----
From: Barnett, Brian W. [mailto:brian.barnett@pearson.com] 
Sent: woensdag 17 augustus 2005 22:00
To: 'user-java@ibatis.apache.org'
Subject: Transaction question

What are some good options to deal with the following:

ServiceClass1
public void doSomething() {
	try {
		daoManager.startTransaction();
		// Write some stuff to a database
		daoManager.commitTransaction();
	} catch (Exception e) {
		throw e;
	} finally {
		daoManager.endTransaction();
	}
}

ServiceClass2
public void doSomethingElse() {
	try {
		daoManager.startTransaction();
		ServiceClass1 sc1 = new ServiceClass1();
		sc1.doSomething();
		// Write some stuff to a database
		daoManager.commitTransaction();
	} catch (Exception e) {
		throw e;
	} finally {
		daoManager.endTransaction();
	}
}

The doSomethingElse() method will fail because startTransaction() gets
called twice. I need a good way to be able to re-use business logic
methods
in different, or the same, service layer classes.

One thing I have done in the past is create a whole new layer,
essentially a
service delegate, where I have moved all the transaction logic to. I
didn't
like that too much because it was just a huge proliferation of methods
just
to solve the "no nested transaction" problem.

I have also passed flags into methods to indicate if a transaction has
already been started, but passing flags is messy to say the least.

Any great ideas out there?

TIA,
Brian Barnett

************************************************************************
**** 
This email may contain confidential material. 
If you were not an intended recipient, 
Please notify the sender and delete all copies. 
We may monitor email to and from our network. 
************************************************************************
****

************************************************************************
**** 
This email may contain confidential material. 
If you were not an intended recipient, 
Please notify the sender and delete all copies. 
We may monitor email to and from our network. 
************************************************************************
****


Mime
View raw message