Announcement Announcement Module
Collapse
No announcement yet.
Spring / Hibernate / Atomikos using two transactions instead of one so rollback fails Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Spring / Hibernate / Atomikos using two transactions instead of one so rollback fails

    I think I almost have 2 phase commit working now using Atomikos. I can see the problem in my tm.out log. The problem is that the two database writes I do to database A and database B are done using two different transactions. The problem looks like is:

    1) A transaction is opened for database A;
    2) A write is done to database A;
    3) Voting occurs and is successful;
    4) Transaction is committed;
    5) Transaction is terminated;

    6) A NEW transaction is opened for database B;
    7) A write is attempted to database B but fails;
    8) Voting occurs and returns as failed
    9) A rollback occurs on database B only;
    10) The transaction is terminated.

    I would be grateful if someone could tell me how to get it working using a single transaction so that I can do a rollback on database A if the write to database B fails.

    My spring-config is:

    Code:
    <bean id="dataSourceProsoc" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
    	<property name="uniqueResourceName"><value>XADBMSProsoc</value></property>
    	<property name="xaDataSourceClassName"><value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value></property>
    	<property name="xaProperties">
    	    <props>
    		<prop key="databaseName">prosoc</prop>
    		<prop key="serverName">localhost</prop>
    		<prop key="port">3306</prop>
    		<prop key="user">prosoc</prop>
    		<prop key="password">prosoc-</prop>
    		<prop key="url">jdbc:mysql://localhost:3306/prosoc</prop>
    	    </props>
    	</property>
    	<property name="minPoolSize"><value>1</value></property>
    </bean>
    
    <bean id="dataSourceProsocForum" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
    	<property name="uniqueResourceName"><value>XADBMSProsocForum</value></property>
    	<property name="xaDataSourceClassName"><value>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</value></property>
    	<property name="xaProperties">
    	    <props>
    		<prop key="databaseName">prosoc</prop>
    		<prop key="serverName">localhost</prop>
    		<prop key="port">3306</prop>
    		<prop key="user">prosoc</prop>
    		<prop key="password">prosoc-</prop>
    		<prop key="url">jdbc:mysql://localhost:3306/prosoc_forum</prop>
    	    </props>
    	</property>
    	<property name="minPoolSize"><value>1</value></property>
    </bean>
    
    <bean id="sessionFactoryProsoc" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    	<property name="useTransactionAwareDataSource" value="true"/>
    	<property name="dataSource"><ref bean="dataSourceProsoc"/></property>
    	<property name="mappingResources">
    	   <list>
    	       <value>uk/co/prodia/prosoc/user/User.hbm.xml</value>
    	   </list>
    	</property>
    	<property name="hibernateProperties">
    	   <props>
    	       <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</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>
    	       <prop key="hibernate.show_sql">true</prop>
    	   </props>
    	</property>
    </bean>
    
    <bean id="sessionFactoryProsocForum" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    	<property name="useTransactionAwareDataSource" value="true"/>
    	<property name="dataSource"><ref bean="dataSourceProsocForum"/></property>
    	<property name="mappingResources">
    	   <list>
    	       <value>uk/co/prodia/prosoc/forum/mvnforum/MVNForumMember.hbm.xml</value>
    	   </list>
    	</property>
    	<property name="hibernateProperties">
    	   <props>
    	       <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</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>
    	       <prop key="hibernate.show_sql">true</prop>
    	   </props>
    	</property>
    </bean>
    
    <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
    	<property name="forceShutdown"><value>true</value></property>
    	<property name="startupTransactionService" value="true"/>
    </bean>
    
    <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
    	roperty name="transactionTimeout"><value>300</value></property>
    </bean>
    
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    	<property name="transactionManager"><ref bean="atomikosTransactionManager"/></property>
    	<property name="userTransaction"><ref bean="atomikosUserTransaction"/></property>
    </bean>
    
    <bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
    	<property name="sessionFactory"><ref bean="sessionFactoryProsoc"/></property>
    </bean>
    
    <bean id="user" class="uk.co.prodia.prosoc.persistence.hibernate.DAOUserImpl">
    	<property name="sessionFactory" ref="sessionFactoryProsoc"/>
    </bean>
    
    <bean id="mvnForumMember" class="uk.co.prodia.prosoc.forum.mvnforum.persistance.hibernate.DAOMVNForumMemberImpl">
    	<property name="sessionFactory" ref="sessionFactoryProsocForum"/>
    </bean>
    and my jta.properties is:

    Code:
    com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
    com.atomikos.icatch.console_file_name = tm.out
    com.atomikos.icatch.log_base_name = tmlog
    com.atomikos.icatch.tm_unique_name = com.atomikos.spring.jdbc.tm
    com.atomikos.icatch.console_log_level = DEBUG 
    com.atomikos.icatch.output_dir = /mnt/storage/work/svn/prosoc/trunk/prosoc/web/WEB-INF/logs
    Thanks for any assistance anyone may be able to offer on this.

  • #2
    Success but is there a better way?

    Having read the documentation for spring a bit more I saw a part that mentioned that the connection will close at the end of the method call. Originally my Java code was:

    Code:
    public class ActionUpdateEmail extends BaseActionSupport {
    
        public String execute() {
    
            // Call to database A
            IfaceDAOUser ifaceDAOUser = (IfaceDAOUser) Config.getSpringBean("prosocUser");
            ifaceDAOUser.updateUserEmail(getServletRequest() , email);
                    
            // Call to database B
            IfaceForumUser ifaceForumUser = (IfaceForumUser)      Config.getSpringBean("prosocForumUser");
            ifaceForumUser.updateUserEmail(prosocUser.getUsername() , email);
    
        }
    }
    If I move the call to database B into the method

    Code:
    ifaceDAOUser.updateUserEmail(getServletRequest() , email);
    then I get the rollback as I would like. Is it possible to leave my code as it was as that seems like a neater way of doing things or is the new way the standard way of getting XA transactions to work?

    I have tried to make the ActionUpdateEmail class (shown above) @Transactional and added an entry for it in the spring-beans file but that didn't work. I also have the sessionFactories set with:

    Code:
    <prop key="hibernate.connection.release_mode">on_close</prop>
    Thanks for any suggestions.

    Comment


    • #3
      Ackkk, not quite working

      Well, I thought I had it working but just to make sure I went back to using non-XA datasources and I find that I get exactly the same behavior. A failed write to database B and the update to database A does not get committed. Maybe someone can clarify my thinking:

      1) I am doing two updates to databases A+B;
      2) The updates are both called from within the same transactional method;
      3) The method will not commit unless they both work as hibernate will rollback database A when the write to database B fails

      With the way I have my code set up it doesn't seem to matter if I use XA or not.

      Questions
      -----------------------
      1) Is the advantage of using XA via Atomikos that the database will be left in a consistent state?
      2) Could anyone give me an example of how, without XA (Atomikos) datasources the DB could be left in an inconsistent state?
      3) Is the structure of my code wrong?

      Comment


      • #4
        If your ActionUpdateEmail class is not created by Spring, @Transactional is ignored.

        Comment


        • #5
          Thank you for the reply, I think you are right.

          I had pretty much decided to go with the solution I had found but then also found a struts2-spring-plugin which I was not previously using. My ActionUpdateEmail is a Struts2 action class and I am now getting:

          Code:
          Action class [springManagedProsocActionUpdateEmail] not found
          error and am not sure why. I have:

          1) Added the struts2-spring-plugin-2.0.11.1.jar to my lib folder;
          2) Added the following to my spring-config file:

          Code:
          <bean id="springManagedProsocActionUpdateEmail" class="uk.co.prodia.prosoc.struts2.action.ActionUpdateEmail">
                  <property name="sessionFactory" ref="sessionFactoryProsocForum"/>
          </bean>
          3) Added the following to my struts.xml file:

          Code:
          <constant name="struts.objectFactory" value="org.apache.struts2.spring.StrutsSpringObjectFactory" />
          4) Modified the action class attribute to point to the id of the bean in the spring-config file so that it looks like the following:

          Code:
          <action name="update-email!*" class="springManagedProsocActionUpdateEmail" method="{1}">
              <results in here ... ...>
          </action>
          I am not sure what else to do as I have followed the guide given at:

          http://struts.apache.org/2.x/docs/spring-plugin.html

          but I am sure that you suggestion is correct.

          Comment


          • #6
            I have fixed the problem and now have 2PC working correctly. In case anyone find it useful here is the thread that describes what I did:

            http://www.nabble.com/Struts2-and-sp...d18814302.html

            Comment


            • #7
              Hi all,
              I'm also trying to use Atomikos+Spring+JDBC, using 2 phase commit.
              Does anyone know how to encrypt com.atomikos.jdbc.AtomikosDataSourceBean password, which define in xmls.

              Thanks,
              filexxia

              Comment


              • #8
                encryption

                Hi,

                Atomikos does not offer out-of-the-box encryption. Are there any industry standards or best practices for this?

                Best

                Comment


                • #9
                  Hi,

                  I have two different databases.
                  1.Oracle
                  2. HSQLDB

                  I am inseting one row in each database, Second database insert fails. So I want to rollback the entire transaction and first database sould not be committed.

                  But in my code first database insert is getting committed.

                  Here is my spring configuration
                  Code:
                  <?xml version="1.0" encoding="UTF-8"?>
                  <beans xmlns="http://www.springframework.org/schema/beans"
                  	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                  	xmlns:tx="http://www.springframework.org/schema/tx"
                  	xmlns:aop="http://www.springframework.org/schema/aop"
                  	xmlns:oxm="http://www.springframework.org/schema/oxm"
                  	xsi:schemaLocation="http://www.springframework.org/schema/beans 
                  	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
                  	http://www.springframework.org/schema/oxm 
                  	http://www.springframework.org/schema/oxm/spring-oxm-1.5.xsd 
                  	http://www.springframework.org/schema/aop 
                  	http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
                  	http://www.springframework.org/schema/tx 
                      http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
                  
                  	<aop:aspectj-autoproxy proxy-target-class="true" />
                  	<bean id="oracleDataSource"
                  		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
                  		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
                  		<property name="url"
                  			value="jdbc:oracle:thin:@172.168.1.1:1521:xe" />
                  		<property name="username" value="xxx" />
                  		<property name="password" value="xxx" />
                  	</bean>
                  	<bean id="hsqldbDataSource"
                  		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
                  		<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
                  		<property name="url" value="jdbc:hsqldb:hsql://localhost/" />
                  		<property name="username" value="sa" />
                  		<property name="password" value="" />
                  	</bean>
                  	<bean id="testAspect" class="com.demo.aop.TestAspect"></bean>
                  	<bean id="XATester" class="com.demo.xa.XATesterClass">
                  		<property name="oracleDatasource" ref="oracleDataSource"></property>
                  		<property name="hsqldbDatasource" ref="hsqldbDataSource"></property>
                  		<property name="transactionManager" ref="transactionManager"></property>
                  	</bean>
                  	<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
                  		init-method="init" destroy-method="close">
                  		<property name="forceShutdown">
                  			<value>true</value>
                  		</property>
                  	</bean>
                  
                  	<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
                  	</bean>
                  	<tx:annotation-driven transaction-manager="transactionManager" />
                  
                  	<bean id="transactionManager"
                  		class="org.springframework.transaction.jta.JtaTransactionManager">
                  		<property name="transactionManager">
                  			<ref bean="atomikosTransactionManager" />
                  		</property>
                  		<property name="userTransaction">
                  			<ref bean="atomikosUserTransaction" />
                  		</property>
                  	</bean>
                  	
                  	<bean id="caroline" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
                      <property name="transactionManager"><ref bean="transactionManager" /></property>
                      <property name="target"><ref bean="XATester"  /></property>
                      <property name="transactionAttributes">
                          <props>
                              <prop key="*">PROPAGATION_REQUIRED, -Exception</prop>
                          </props>
                      </property>
                  	</bean>
                  	<aop:config proxy-target-class="true">
                  		<aop:aspect ref="testAspect">
                  			<aop:pointcut id="myCut" expression="execution(public * *(..))" />
                  			<aop:around pointcut-ref="myCut" method="log" />
                  		</aop:aspect>
                  	</aop:config>
                  
                  </beans>

                  This is how i am doing database insert.

                  Code:
                  /**
                   * 
                   */
                  package com.demo.xa;
                  
                  import java.util.HashMap;
                  import java.util.List;
                  import java.util.Map;
                  
                  import javax.sql.DataSource;
                  
                  import org.springframework.jdbc.core.simple.SimpleJdbcInsert;
                  import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
                  import org.springframework.transaction.PlatformTransactionManager;
                  import org.springframework.transaction.TransactionDefinition;
                  import org.springframework.transaction.TransactionStatus;
                  import org.springframework.transaction.annotation.Transactional;
                  import org.springframework.transaction.support.DefaultTransactionDefinition;
                  
                  /**
                   * @author dp.prabu
                   * 
                   */
                  public class XATesterClass implements TesterInterface {
                  	private DataSource oracleDatasource;
                  	private DataSource hsqldbDatasource;
                  	private PlatformTransactionManager transactionManager;
                  	
                  
                  	/**
                  	 * @return the oracleDatasource
                  	 */
                  	public DataSource getOracleDatasource() {
                  		return oracleDatasource;
                  	}
                  
                  	/**
                  	 * @param oracleDatasource the oracleDatasource to set
                  	 */
                  	public void setOracleDatasource(DataSource oracleDatasource) {
                  		this.oracleDatasource = oracleDatasource;
                  	}
                  
                  	/**
                  	 * @return the hsqldbDatasource
                  	 */
                  	public DataSource getHsqldbDatasource() {
                  		return hsqldbDatasource;
                  	}
                  
                  	/**
                  	 * @param hsqldbDatasource the hsqldbDatasource to set
                  	 */
                  	public void setHsqldbDatasource(DataSource hsqldbDatasource) {
                  		this.hsqldbDatasource = hsqldbDatasource;
                  	}
                  
                  	
                  	/**
                  	 * @return the transactionManager
                  	 */
                  	public PlatformTransactionManager getTransactionManager() {
                  		return transactionManager;
                  	}
                  
                  	/**
                  	 * @param transactionManager the transactionManager to set
                  	 */
                  	public void setTransactionManager(PlatformTransactionManager transactionManager) {
                  		this.transactionManager = transactionManager;
                  	}
                  	public void testMethod() {
                  		try {	
                  				SimpleJdbcInsert insertRecordHsqldb = new SimpleJdbcInsert(hsqldbDatasource).withTableName("temp");
                  				Map<String,Object> parametersHsqldb = new HashMap<String,Object>();
                  				parametersHsqldb.put("code", "3");
                  				parametersHsqldb.put("name", "Prabu");
                  				insertRecordHsqldb.execute(parametersHsqldb);
                  				SimpleJdbcInsert insertRecord = new SimpleJdbcInsert(oracleDatasource).withTableName("EMP");
                  				Map<String,Object> parameters = new HashMap<String,Object>();
                  				parameters.put("id", "46");
                  				parameters.put("name", "Prabu");
                  				insertRecord.execute(parameters);
                  				SimpleJdbcTemplate template2 = new SimpleJdbcTemplate(this.oracleDatasource);
                  				String sql1 = "SELECT * FROM EMP";
                  				List<Map<String, Object>> rows1=template2.queryForList(sql1);
                  				System.out.println(rows1.size());
                  				SimpleJdbcTemplate template = new SimpleJdbcTemplate(this.hsqldbDatasource);
                  				String sql = "SELECT * FROM TEMP";
                  				List<Map<String, Object>> rows=template.queryForList(sql);
                  				System.out.println(rows.size());
                  		} catch (Exception e) {
                  			System.out.println(e.getMessage());
                  		}
                  	}
                  }


                  Please advise. I don know what is wrong with this code.

                  Comment

                  Working...
                  X