iBATIS does not support nested transactions.  Take a look at the class com.ibatis.sqlmap.engine.transaction.jta.JtaTransaction to see what's going on.
 
If a JTA transaction already exists...
 
1. The iBATIS "startTransaction" will not start another transaction
2. The iBATIS commit does nothing
3. The iBATIS rollback marks the transaction for rollback, but does not rollback itself
 
When there are externally managed transactions, the iBATIS rollback will mark the external transaction for rollback, but the other two methods do, essentially, nothing.
 
Jeff Butler

On Tue, Apr 8, 2008 at 9:44 AM, Alistair Young <alistair.j.young@gmail.com> wrote:
Once again I seem to have found a solution shortly after asking the question...

On 08/04/2008, Alistair Young <alistair.j.young@gmail.com> wrote:
> [ ... snip ... ]
>
>        // start a transaction
>        // [iBATIS starts a new JTA transaction...?]
>        sqlMapClient.startTransaction();
>
>        // ... do some database processing ...
>
>        // start an inner transaction
>        // [iBATIS joins the existing JTA transaction...?]
>        sqlMapClient.startTransaction();
>
>        // ... do some more database processing ...
>
>        // abandon the inner transaction
>        // [iBATIS issues setRollbackOnly on JTA transaction...?]
>        sqlMapClient.endTransaction();
>
>        // commit the outer transaction
>        // [iBATIS issues commit on JTA transaction...?]
>        sqlMapClient.commitTransaction();
>
>  with the final result that the database is unchanged.  However,
>  instead, I get an exception on my second call to startTransaction()
>  complaining that a transaction is already started.
>
>  [ ... snip ... ]

After a bit of experimentation, I think that I need to explicitly
start the JTA transaction outside of the iBATIS transactions.  The
following does what I'd expected:

      // start a JTA transaction
      Context ctx = new InitialContext();
      UserTransaction ut =
(UserTransaction)ctx.lookup("java:comp/UserTransaction");
      ut.begin();

      // start an iBATIS transaction
      // [iBATIS joins the JTA transaction]
      sqlMapClient.startTransaction();

      // ... do some database processing ...

      // commit the iBATIS transaction
      // [iBATIS does not actually commit to the db]
      sqlMapClient.commitTransaction();

      // start another iBATIS transaction
      // [iBATIS joins the JTA transaction]
      sqlMapClient.startTransaction();

      // ... do some more database processing ...

      // abandon the iBATIS transaction
      // [iBATIS issues setRollbackOnly on JTA transaction]
      sqlMapClient.endTransaction();

      // commit the JTA transaction
      try {
             ut.commit();
      } catch (RollbackException x) {
             // will be raised because the commit did
             // not take place (since the second iBATIS
             // transaction was cancelled)
      }

Please feel free to suggest a better way, if one should exist!


Alistair.