Search code examples
javaspringhibernatetransactionsjta

Questions regarding JtaTransactionManager


I am looking for a way to put CRUD of two data source into one transaction by Spring/JTA, but have following questions:

  1. Is it ok that the datasource is is made oforg.apache.commons.dbcp.BasicDataSource instead of JNDI?
  2. There are two parameters to intitiate a JtaTransactionManager:

    public JtaTransactionManager(UserTransaction userTransaction,
                         TransactionManager transactionManager)
    

    From Spring document:

    userTransaction - the JTA UserTransaction to use as direct reference
    transactionManager - the JTA TransactionManager to use as direct reference
    

    If I have a datasource DS_A and another datasource DS_B, which both requires within the same transaction manager, then which should be the UserTransaction and how to define the TransactionManager, as I am just creating the JtaTransactionManager, does it mean another JtaTransactionManager? Also it looks both parameters are not required so how JtaTransactionManager can detect them from:

    <context:annotation-driven/>
    <tx:jta-transaction-manager/> 
    
  3. If everything is well defined in application context, then which transaction manager bean should referred in @Transactional annotation, if there are two datasources?


Solution

    1. Is it ok that the datasource is is made oforg.apache.commons.dbcp.BasicDataSource instead of JNDI?

    JNDI is just a way to access the datasource. To have distributed transactions across databases you need to use transactional data sources. For e.g. microsoft sql server jdbc driver has a transactional data source - com.microsoft.sqlserver.jdbc.SQLServerXADataSource.

    1. If I have a datasource DS_A and another datasource DS_B, which both requires within the same transaction manager, then which should be the UserTransaction and how to define the TransactionManager, as I am just creating the JtaTransactionManager, does it mean another JtaTransactionManager?

    JtaTransactionManager is an implementation for JTA, delegating to a backend JTA provider. So you would need a JTA provider that implements distributed transactions. Bitronix is one such JTA provider. There are various configurations needed

    a. hibernate.properties

    <prop key="hibernate.transaction.factory_class">
    <prop key="hibernate.transaction.manager_lookup_class">
    <property name="useTransactionAwareDataSource" value="true"/>
    

    b. Defining transaction manager and usertransaction beans

        <bean id="BitronixTransactionManager" factory-method="getTransactionManager"
              class="bitronix.tm.TransactionManagerServices" depends-on="bitronixConfiguration"
              destroy-method="shutdown">
        </bean>
    
        <bean id="transactionManager"                           class="org.springframework.transaction.jta.JtaTransactionManager">
            <property name="transactionManager" ref="BitronixTransactionManager"/>
            <property name="userTransaction" ref="BitronixTransactionManager"/>
            <property name="allowCustomIsolationLevels" value="true"/>
        </bean>
    

    c. Transaction Interceptor and annotation support

        <bean id="annotationTransactionSource"
            class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"/>
    
    
        <bean id="transactionInterceptor" 
              class="org.springframework.transaction.interceptor.TransactionInterceptor">
            <property name="transactionManager">
                <ref local="transactionManager"/>
            </property>
            <property name="transactionAttributeSource">
                <ref local="annotationTransactionSource"/>
            </property>
        </bean>
    

    Spring 3.1 also introduced a @EnableTransactionManagement annotation so another way to configure would be

    @Configuration
    @EnableTransactionManagement
    public class PersistenceJPAConfig{
    
       @Bean
       public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
          ...
       }
    
       @Bean
       public PlatformTransactionManager transactionManager(){
          //configure JTA transaction manager
          return transactionManager;
       }
    }
    
    1. If everything is well defined in application context, then which transaction manager bean should referred in @Transactional annotation, if there are two datasources?

    I assume the above explanation should answer this question. There is just one transaction manager managing transactions over the two data sources. These are called distributed transaction and they use a two phase commit protocol. Go through this link for an introductory article on XA transactions.