Announcement Announcement Module
Collapse
No announcement yet.
JtaTransactionManager cannot rollback all resources Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JtaTransactionManager cannot rollback all resources

    I use Spring JtaTransactionManager to manager the global transaction, BTM(Bitronix) as the JTA implementation, MySQL as the XA datasource, Hibernate as the JPA implementation.

    I use programmable transactionManager config and config datasource and transaction in Spring applicationContext.xml:
    Code:
    	<bean id="dataSource1" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">
    		<property name="className" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
    		<property name="driverProperties" >
    			<props>
    				<prop key="URL">jdbc:mysql://localhost:3306/Bookings</prop>
    				<prop key="user">root</prop>
    				<prop key="password">ACahlof</prop>
    			</props>
    		</property>
    		<property name="uniqueName" value="jdbc/booking" />
    		<property name="minPoolSize" value="0" />
    		<property name="maxPoolSize" value="3" />
    	</bean> 
    
        <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager"> 
            <property name="transactionManager" ref="bitronixTransactionManager"/> 
            <property name="userTransaction" ref="bitronixTransactionManager"/> 
        </bean> 
    
        <bean id="bitronixTransactionManager" factory-method="getTransactionManager" 
              class="bitronix.tm.TransactionManagerServices" depends-on="dataSource,dataSource1" 
              destroy-method="shutdown"/>
    I config persistence-unit for Hibernate/JPA in another persistence.xml.

    In my testing code, I init the IoC container and get the JtaTransactionManager instance and start the transaction, in the transaction, will insert into database twice.
    Code:
    		ApplicationContext ctx = new FileSystemXmlApplicationContext("applicationContext.xml");
    		((AbstractApplicationContext) ctx).registerShutdownHook();
    		JtaTransactionManager txManager = (JtaTransactionManager) ctx.getBean("txManager");
    		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
    		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    		TransactionStatus status = null;
    
    		BookingBasic bb = new BookingBasic();
    		bb.setBookingNo("booking-001");
    		bb.setCustomer("ABC");
    
    		CargoAspect ca = new CargoAspect();
    		ca.setId(1);
    		ca.setCommodity("girls");
    
    		try {
    			System.out.println("get the transaction status");
    			status = txManager.getTransaction(def);
    
    			EntityManagerFactory emf = Persistence.createEntityManagerFactory("booking-jta");
    			EntityManager em = emf.createEntityManager();
    			System.out.println("Entity Manager is "+em);
    
    			em.persist(ca);
    
    			em.persist(bb);
    			
    			em.close();
    			
                            System.out.println("commit the transaction");
    			txManager.commit(status);
    					}catch(Exception e){
    			System.out.println("transaction commit exception");
    			e.printStackTrace();
    			
    			try{
    				System.out.println("rollback the transaction");
    				txManager.rollback(status);
    			}catch(Exception e1){
    				System.out.println("rollback exception");
    				e1.printStackTrace();
    			}
    			
    		}
    When execute txManager.commit(status), there is exception for second insert em.persist(bb), then only this persistence is rolled back, the former insert em.persist(ca) still commit to database.

    Anyone know what is the problem with my code?
    I want to rollback all the persistence including the former one. Do I have to do additional setting?

    Your help will be greatly appreciated!

  • #2
    Why do you need JTA/distributed transactions? You are using the same EntityManager for both operations. A local JPA transaction manager should be fine.
    Why use programatic transactions? declarative are preferred unless you have a scenario which cannot be handled by declarative transaction managment.
    In case you still want to use programatic transactions, try using TransactionTemplate provided by Spring
    http://static.springsource.org/sprin...-prog-template

    Comment


    • #3
      Thanks for your reply!
      I am new to Spring, I will learn how to use declarative transaction instead of programatic transaction, and use TransactionTemplate for local transaction.

      Comment


      • #4
        To also say you are creating an EMF for a single transaction. This is a very heavy weight operation. This is certainly no way to learn how to efficiently use Spring Framework with JPA, but if you wanted to understand JPA and transaction interaction better, as a proof-of-concept then; knock yourself out.


        With that said, I also think you need to move the emf.close() call until after you have comitted. The order of operations is:

        * Create EMF
        * Ask EMF for new EM instance
        * Begin transaction (ensure you have an active transactional context)
        * Use EM to read/write data.
        * Use data returned from EM (lazy-loading / OpenEntityManagerInView)
        * EM flush()
        * Commit transaction
        * EM close()
        * EMF close()

        It is better to restructure your design as above since that is how an efficient real world application works. The EMF is usually created once and is long running. An EM is usually created once per thread/unit-of-work, an EM is allowed to perform many transactions sequencially.

        It is not clear from your configuration how you have instructed your EMF what transacation management stratagy it is using. By coding the whole example yourself you must now also read your EMF implementations documentation to find out how to hookup Spring's transaction management into it (do not take it as a given this is going to happen automatically).

        If you want things to be automatic and be more realistic of a JPA enabled Spring framework application then you want to look at using your applicationContext.xml and BeanFactory to initialize your EMF, your transactionManager using one of the Spring helper classes/beans that you can find example of usage in Spring documentation (look for JPA section).


        What is probably happening is the first em.persist(ca); is happening inside an "implicit transaction".

        Comment


        • #5
          Thanks Dlmiles.

          Your suggestion is good for me.
          I should get more understanding of JPA and Transaction.

          I am aware of that I have not made good use of JPA and Spring transaction strategy. That makes my code in a mess.

          Thanks a million for your suggestion of the operation steps!

          Comment


          • #6
            Originally posted by dlmiles View Post
            The order of operations is:

            * Create EMF
            * Ask EMF for new EM instance
            * Begin transaction (ensure you have an active transactional context)
            * Use EM to read/write data.
            * Use data returned from EM (lazy-loading / OpenEntityManagerInView)
            * EM flush()
            * Commit transaction
            * EM close()
            * EMF close()
            HI Dlmiles,

            When I tried to get the EM before begin transaction, seems the EM is not bound to the transaction.
            EntityManager em = emf.createEntityManager();
            status = txManager.getTransaction(def);

            em.persist(ca);

            txManager.commit(status);
            em.close();
            I config the transaction in applicationContext.xml as original post, I config the EMF in persistence.xml file
            <persistence-unit name="booking-jta" transaction-type="JTA">
            <provider>org.hibernate.ejb.HibernatePersistence </provider>
            <jta-data-source>jdbc/booking</jta-data-source>
            <properties>
            <property name="hibernate.archive.autodetection" value="class"/>
            <property name="hibernate.current_session_context_class" value="jta" />
            <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.BTMTransactionMan agerLookup" />
            <property name="hibernate.jndi.class" value="bitronix.tm.jndi.BitronixInitialContextFact ory"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
            <property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider" />
            <property name="hibernate.show_sql" value="true" />

            <!-- after first run the application, should comment it, else it will drop and create table each time
            <property name="hibernate.hbm2ddl.auto" value="create" /> -->

            </properties>
            </persistence-unit>
            Should config the EMF in the applicationContext.xml then the EM can be bound to the transaction?

            If get the EM after start transaction, the EM can be bound to the transaction.
            status = txManager.getTransaction(def);
            EntityManager em = emf.createEntityManager();

            Comment


            • #7
              I did discuss this issue as "transacation management stratagy" your EMF is not configured correctly for the environment.

              The setting: <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.BTMTransactionMan agerLookup" />

              Is related to this matter. If you still wish to roll your own integration you need to read the Hibernate manual about this matter and provide a suitable lookup class. What happens then is the EM will automatically integrate with the transaction context seamlessly.

              Comment


              • #8
                Originally posted by dlmiles View Post
                I did discuss this issue as "transacation management stratagy" your EMF is not configured correctly for the environment.

                The setting: <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.BTMTransactionMan agerLookup" />

                Is related to this matter. If you still wish to roll your own integration you need to read the Hibernate manual about this matter and provide a suitable lookup class. What happens then is the EM will automatically integrate with the transaction context seamlessly.
                Thanks for your checking and reply!
                I think I will read the Spring reference manual about the ORM part clearly, and move the EMF configuration to applicationContext.xml. after I test ok, I will post the changes here.

                Comment

                Working...
                X