Wednesday, August 17, 2011

Key Concepts to Understand Spring's Consistent Transaction Handling

Class FooBarService shows simplified pseudo code for different transaction handling paradigms using JDBC,Hibernate and JTA.
Class FooBarServiceSpring shows
simplified pseudo code for consistent transaction handling using Spring.
Obviously Spring's transaction handling is quite different from JDBC and Hibernate; but is closer to JTA. However, by no mean does Spring only support JTA. Instead, it is only a matter of configuring different PlatformTransactionManager (PTM hereafter) in your application context in order to support different transaction handling paradigms.  For example, Spring's DataSourceTransactionManager, HibernateTransactionManager and JTATransactionManager are for JDBC, Hibernate and JTA, respectively.

This discussion can help readers understand some internals of Spring's  PTM implementations on heterogeneous native transaction platforms.


//different transaction paradigms using JDBC,Hibernate and JTA
class FooBarService {
  //local transaction paradigm using JDBC
  public void foo() {
    //creates a session with the backend database
    Connection conn = dataSource.getConnection();
    //starts a new local transaction.
    conn.setAutoCommit(false);

    updates-data-through-this-conn

    //commits the local transaction
    conn.commit();
    //closes the session
    conn.close();
  }

  //local transaction paradigm using Hibernate
  public void bar() {
    //creates a session with the backend database
    Session session = sessionFactory.openSession();
    //starts a new local transaction.
    Transaction tx = session.beginTransaction();

    updates-data-through-this-session

    //commits the local transaction
    tx.commit();
    //closes the session
    session.close();
  }

  //Global transaction paradigm using JTA
  public void fooBar() {
    userTransaction.begin();

    //creates a session with the backend database1
    Connection conn1 = dataSource1.getConnection();
    updates-data-through-this-conn1

    //creates another session with the backend database2
    Connection conn2 = dataSource2.getConnection();
    updates-data-through-this-conn1

    //closes the two sessions
    conn1.close();
    conn2.close();

    userTransaction.commit();
  }
}


//consistent transaction handling paradigms using Spring
class FooBarServiceSpring {
  @Transactional
  public void foo() {
    //updates data through some data source connection
  }
 
  @Transactional(propagation=Propagation.REQUIRES_NEW)
  public void bar() {
    //updates data through some data source connection
  }
  @Transactional
  public void fooBar() {
    foo();
    bar();
  }
}


1.
Key Transaction Abstraction
The following 3 interfaces are key to understand Spring's transaction handling:
  • TransactionDefinition
    It allows you to define transaction requirements (isolation level, propagation behavior, timeout and read-only status) before your transaction is started.
    In FooBarServiceSpring, you specify your TransactionDefinition in @Transactional.
  • TransactionStatus
    Once your transaction is started, this interface allows you to query its current status including rollback flag and new vs existing flag, and to mark it to rollback only (for example when you encounter an exception).
    Spring's default TransactionStatus implementation also includes the underlying native transaction such as a connection for JDBC, a session for Hibernate and a user transaction for JTA.
    In FooBarServiceSpring, @Transactional allows you to specify your rollback rules. For other status information, you either implicitly knows(such as IsNewTransactio and IsComplete) or can retrieve from the underlying native transaction bound to the thread (Spring binds JDBC connection and Hibernate Session to the thread; JTA's UserTransaction is also bound to a standard JNDI name). Please note that Spring doesn't bind TransactionStatus to the thread because PTM can pass it around in your AOP proxied methods.
  • PlatformTransactionManager
    It wires together the above 2 interface and works as a coordinate overall.
    Specifically its getTransaction() method takes your TransactionDefinition and creates a TransactionStatus representing either a new or existing transaction.  Its commit() and rollback() methods take the returned TransactionStatus and commit and rollback the target transaction, respectively.
    It allows you to demarcate transactions as a singleton because it can get different transactions bound on different threads.
    In FooBarServiceSpring, @Transactional uses whatever transaction manager you configured in your application context such as DataSourceTransactionManager for JDBC.
When we say Spring transaction, we really mean TransactionDefinition and TransactionStatus. The PTM is more like a JTA UserTransaction / TransactionManager. But once again, it handles all types of native transaction API's behind the scene.
    2. Physical Transaction and Logical Transaction
    FooBarServiceSpring's foobar() calls foo() and bar(). Suppose there is no transaction before you call foobar(). So foobar() creates a new physical transaction while foo() starts a nested logical transaction due to its default propagation being REQUIRED; the logic transaction ends when foo() returns. 
    But bar() suspends the current transaction and creates a new physical transaction due to its propagation being REQUIRED_NEW. The new transaction ends and the suspended transaction is resumed when bar() exits.
    Because each (physical or logic) transaction always has a begin and end, we sometimes use transaction scope.

    For data persistence, only the outer physical transaction can commit or rollback. But
    on each individual inner logic transaction (method) level, you always specify your TransactionStatus and can mark transaction to rollback . However the embed native transaction in TransactionStatus is either new (if it is a physical one) or existing (if it participates in an existing one).
    You can also turn on the "validateExistingTransaction" flag in PTM
    so that the inner logic transaction will reject participation if its TransactionStatus is not compatible to the outer TransactionStatus.

    3. Resource, Connection, Session, Transaction and Resource Manager (RM)
    Resource can mean any valuable data; but in PlatformTransactionManager it is either a connection (for JDBC and JMS etc) or session (for Hibernate and JMS etc).

    Both connection and session represent a communication link with the some backend resource manager (RM for short). In the database world, both termscan be used interchangeably. Although Hibernate uses session, it uses a connection behind the scene.
     
    Transaction represents a unit of work. We mentioned in the above Section 1 that Spring's abstract transaction covers an underlying native transaction.
    For JDBC and Hibernate, you create local transactions using connections / sessions and because they have one-to-one relationship, you can sometimes interchange the 2 terms. In other words, if the inner logic transaction participants in the outer physical transaction, it must reuse the the same connection / session.
    For JTA, you creates global transactions using transaction managers; multiple connections can be enlisted in global transactions as transaction branches. One thing that is very different from JDBC and Hibernate is that you can conduct DML changes using different connections to the same backend RM instance in the same global transaction (This is because when a connection is enlisted, XID is created to represent this branch both in the JTA and the RM. Even you use a different connection, the RM can still identify the same transaction using the passed-in XID).

    RM managers resources (transaction) on behalf of you. It is commonly used in JTA/XA world. For most of us, databases are common RM.

    4. Transaction and Session -- Which Comes First?
    In the foo() and bar() of FooBarService, you first create a connection / session, then starts a transaction. Before you close the connection / session, you first commit / rollback the transaction.
    But in the foobar() of
    FooBarService, you first starts a transaction, then create a connection / session. Before you commit / rollback the transaction, you first close the connection / session.
    In FooBarServiceSpring, all you have is just @transactional and you are not allowed to open or close any connection / session explicitly. So how can Spring hide the difference between JDBC, Hibernate and JTA and whois taking care of connections / session anyway?
    Data access experience tells us that we always need a connection / session and its lifecycle methods (open, close etc) are just boilerplate and resource will leak if you forget to call the close method (actually this happens very often). But we must define transaction requirements by ourselves. For example, only you know that the bar() in
    FooBarServiceSpring requires REQUIRE_NEW propagation based on your business rules.

    So Spring is taking care of connections / sessions behind the scene. You just need to specify you transaction requirement using @transactional. The next section tell you more details. 
    5. Transaction and Session -- Resource Synchronization with Transaction
    Because you always explicitly specify your transaction requirements, Spring synchronizes the needed resource (connection or session) with the transaction at both transaction start time and ending time or at the transaction scope boundaries.
     

    For JDBC and Hibernate, Spring binds the connection / session (hence also local transaction) to the thread so that it is always available on the execution call stack. 
    Spring PTM automatically either retrieves an existing connection / session from the thread (if an outer transaction is already there) or creates a new connection / session and binds it to the thread before starting a transaction.
    When transaction is right before being committed or rolled back, Spring PTM also automatically closes the connection / session and unbinds it from the thread.


    For JTA, Spring gets the UserTransaction from the standard JNDI name, so the global transaction is also available on the execution call stack.
    When you retrieves a connection / session, it is automatically enlisted in the global transaction (Spring doesn't help here; the underlying XADataSource usually does the magic).

    When the JTA transaction is right before being committed or rolled back, Spring PTM once again automatically closes the connection / session .

    6. Nested Transaction and Transaction Suspend / Resume
    • For nested transaction, both  the outer and inner transaction are live in your thread while when a transaction is suspended, your thread work on another new transaction;
    • Nested transactions are dependent i.e. only the outer transaction is physical and can commit the change; but you can rollback the inner logic transaction.
      The suspended transaction and the new transaction are two physical transactions and are independent i.e. they can both commit or rollback independently;
    • Suspending tranasction is deadlock-prone because the backend RM still holds the resource locks on the suspended transaction even your JTA has switched to another new transaction. You should be very careful even your application also locks some resources in the suspended transaction that are needed by the new transaction;
    • Nested transactions are only supported by JDBC with the savepoint feature. Because XA doesn't support nested transactions, neither JTA can (This seems to be ironic because most databases support savepoint);
    • Suspending a JDBC transaction in Spring means first unbinds it from the thread, then creates a new connection and binds to the thread. For JTA, Spring just delegates the suspend/resume to the underlying UserTransaction in addition to its own bookkeeping for resource synchronizations.
    7. Propagation.SUPPORTS and No Transaction
    No transaction means your data changes are committed on each individual SQL statement level (it is autocomit for JDBC) instead of grouping several SQL statements in a transaction.

    Propagation.SUPPORTS also commits data changes on each individual SQL statement level if there is no existing transaction. But if you set your TransactionSynchronization in PTM to ALWAYS, it also supports resource synchronization on an empty transaction scope (empty transaction scope means no underlying native transaction).
    To make it concrete, the same JDBC connection or Hibernate Session will bind to the thread inside the empty transaction scope for reuse in the execution call stack until the scope ends.  Be aware that the connection is of course in autocommit mode.

    Please remember that if you empty transaction scope has active resource synchronization, DON'T nested Propagation.REQUIRED or Propagation.REQUIRED_NEW in it because resources synchronizations for the 2 transaction scopes will conflict (unfortunately you have to read the PMT source code in order  to get to the bottom).



    4 comments:

    1. Great article. Very well explained.

      Thanks.

      ReplyDelete
    2. I really appreciate information shared above. It’s of great help. If someone want to learn Online (Virtual) instructor lead live training in WEBSPHERES TX TRAINING, kindly contact us http://www.maxmunus.com/contact
      MaxMunus Offer World Class Virtual Instructor led training on TECHNOLOGY. We have industry expert trainer. We provide Training Material and Software Support. MaxMunus has successfully conducted 100000+ trainings in India, USA, UK, Australlia, Switzerland, Qatar, Saudi Arabia, Bangladesh, Bahrain and UAE etc.
      For Demo Contact us.
      Sangita Mohanty
      MaxMunus
      E-mail: sangita@maxmunus.com
      Skype id: training_maxmunus
      Ph:(0) 9738075708 / 080 - 41103383
      http://www.maxmunus.com/

      ReplyDelete