سؤال

I am creating a sample application to test two-phase commit (2PC). I have taken the code bits used here from the Internet. I am using Spring, Hibernate and Atomikos with MySQL as the backend. I am using two databases and deliberately making the call to the second database fail to check whether the first database call gets rolled back. Sadly it doesn't seem to work. Can some one point me to some links with some sample code?

Following is my configuration:
The Hibernate session factories:

<bean id="sessionFactory1" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource">
        <ref bean="dataSource1"/>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.connection.isolation">3</prop>    
            <prop key="hibernate.current_session_context_class">jta</prop>    
            <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop>    
            <prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop> 
            <prop key="hibernate.connection.release_mode">on_close</prop>
        </props>
    </property>

    <property name="mappingResources">
        <list>
            <value>/hibernate/Stock.hbm.xml</value>
        </list>
    </property>
</bean>

<bean id="sessionFactory2" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource">
        <ref bean="dataSource2"/>
    </property>
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.connection.isolation">3</prop>    
            <prop key="hibernate.current_session_context_class">jta</prop>    
            <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop>    
            <prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop> 
            <prop key="hibernate.connection.release_mode">on_close</prop>
        </props>
    </property>

    <property name="mappingResources">
        <list>
            <value>/hibernate/Stock1.hbm.xml</value>
        </list>
    </property>
</bean>

The dataSource configuration:

<bean id="dataSource1" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">        
    <!-- set an arbitrary but unique name for the datasource -->       
    <property name="uniqueResourceName"><value>XADBMS1</value></property>        
    <!-- set the underlying driver class to use, in this example case we use MySql  -->       
    <property name="xaDataSourceClassName">          
        <value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>       
    </property>       
    <property name="xaProperties">           
    <!--   set the driver-specific XADataSource properties  (check your driver docs for more info)           -->                 
        <props>                         
            <prop key="user">${jdbc.username}</prop>                         
            <prop key="password">${jdbc.password}</prop>                         
            <prop key="URL" >${jdbc.url1}</prop>                 
        </props>      
    </property>           
    <!-- how many connections in the pool? -->       
    <property name="poolSize" value="3"/>    
</bean>

<bean id="dataSource2" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">        
    <!-- set an arbitrary but unique name for the datasource -->       
    <property name="uniqueResourceName"><value>XADBMS2</value></property>        
    <!-- set the underlying driver class to use, in this example case we use MySql  -->       
    <property name="xaDataSourceClassName">          
        <value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value>       
    </property>       
    <property name="xaProperties">           
    <!--   set the driver-specific XADataSource properties  (check your driver docs for more info)           -->                 
        <props>                         
            <prop key="user">${jdbc.username}</prop>                         
            <prop key="password">${jdbc.password}</prop>                         
            <prop key="URL" >${jdbc.url2}</prop>                 
        </props>      
    </property>           
    <!-- how many connections in the pool? -->       
    <property name="poolSize" value="3"/>    
</bean>

The Spring JTA configuration:

 <bean id="AtomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">     
<!-- when close is called, should we force transactions to terminate or not?     -->   
   <property name="forceShutdown" value="false" /> 
</bean>  
<!--Also use Atomikos UserTransactionImp, needed to configure Spring   --> 
<bean id="AtomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">     
    <property name="transactionTimeout" value="300" /> 
</bean>

<!--Configure the Spring framework to use JTA transactions from Atomikos  --> 
<bean id="JtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">    
    <property name="transactionManager" ref="AtomikosTransactionManager" />    
    <property name="userTransaction" ref="AtomikosUserTransaction" /> 
</bean>  

I have two DAOImpl into which I inject the two sessionFactories defined above. Following is the call from the Java code:

public static void main( String[] args )
{
    ApplicationContext appContext = 
            new ClassPathXmlApplicationContext("spring/config/appContext.xml");

    StockBo stockBo = (StockBo)appContext.getBean("stockBo1");
    StockBo stockBo2 = (StockBo)appContext.getBean("stockBo2");

    /** insert **/
    Stock stock = new Stock();
    stock.setStockCode("7668");
    stock.setStockName("HAIO");
    stockBo.save(stock);

    Stock stock1 = new Stock();
    //stock1.setStockCode("1668"); **Commented to fail the second db insert**
    stock1.setStockName("AAIO");
    stockBo2.save(stock1);
}

Any pointers would be of great help.

Thanks

هل كانت مفيدة؟

المحلول

To get the behaviour you expect you need to perform your DAO calls inside the same transaction, for example, as follows:

final StockBo stockBo = (StockBo)appContext.getBean("stockBo1");
final StockBo stockBo2 = (StockBo)appContext.getBean("stockBo2");
TransactionTemplate tx = new TransactionTemplate(appContext.getBean(PlatformTransactionManager.class);

tx.execute(new TransactionCallback<Void>() {
    public Void doInTransaction(TransactionStatus ts) {
        /** insert **/
        Stock stock = new Stock();
        stock.setStockCode("7668");
        stock.setStockName("HAIO");
        stockBo.save(stock);

        Stock stock1 = new Stock();
        //stock1.setStockCode("1668"); **Commented to fail the second db insert**
        stock1.setStockName("AAIO");
        stockBo2.save(stock1);

       return null;
    }
});

Also, I think you don't need the following lines since you configure Hibernate with Spring:

<prop key="hibernate.current_session_context_class">jta</prop>
<prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop> 
<prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop>
<prop key="hibernate.connection.release_mode">on_close</prop>  

See also:

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top