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

  • JtaTransactionManager's transaction rollback

    Here is the method I wish to implement:

    Code:
    @Override
    public Item createRecordsAndItem(String[] names, String itemName) {
    	Item item = (Item) getBeanFactory().getBean("item");
    	item.setName(itemName);
    	getItemDao().insert(item);
    	
    	List<Record> records = new ArrayList<Record>();
    	
    	for (String name : names) {
    		Record record = (Record) getBeanFactory().getBean("record");
    		record.setName(name);
    		record.setItem(item);
    		getRecordDao().insert(record);
    		records.add(record);
    	}
    	
    	return item;
    }
    And it's interface-based transactional method:

    Code:
    @Transactional(propagation=Propagation.REQUIRED,
    				rollBackFor=Exception.class)
    public abstract Item createRecordsAndItem(String[] names, String itemName);
    Here are the hibernate mapping files:

    Code:
    <hibernate-mapping package="ph.vmobile.test.txnmgmt.entity">
    	<class name="DefaultRecord" table="ENTITY" entity-name="record"
    		abstract="false" lazy="true">
    		<id name="id" type="long">
    			<generator class="sequence">
    				<param name="sequence">rec_seq</param>
    			</generator>
    		</id>
    		<property name="name" type="java.lang.String" length="32" unique="true"/>
    		<many-to-one="item" entity-name="item" lazy="false"/>
    	</class>
    </hibernate-mapping>
    
    
    <hibernate-mapping package="ph.vmobile.test.txnmgmt.entity">
    	<class name="DefaultItem" table="ITEMS" entity-name="item"
    		abstract="false" lazy="true">
    		<id name="id" type="long">
    			<generator class="sequence">
    				<param name="sequence">item_seq</param>
    			</generator>
    		</id>
    		<property name="name" type="java.lang.String" length="32" unique="true"/>
    	</class>
    </hibernate-mapping>
    Here are the spring bean configuration files:
    Code:
    <bean name="jotm" class="org.objectweb.jotm.Jotm" scope="singleton">
    	<constructor-arg index="0" value="true"/>
    	<constructor-arg index="1" value="false"/>
    </bean>
    
    <bean name="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" scope="singleton">
    	<property name="userTransaction">
    		<bean factory-bean="jotm" factory-method="getUserTransaction"/>
    	</property>
    </bean>
    
    <tx:annotation-driven order="1"/>
    
    <bean name="service" class="ph.vmobile.test.txnmgmt.service.DefaultService" scope="prototype">
    	<property name="recordDao" ref="recordDao"/>
    	<property name="itemDao" ref="itemDao"/>
    </bean>
    
    <bean name="recordDao" class="ph.vmobile.test.txnmgmt.dao.RecordSHDao" scope="prototype">
    	<property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    
    <bean name="itemDao" class="ph.vmobile.test.txnmgmt.dao.ItemSHDao" scope="prototype">
    	<property name="sessionFactory" ref="sessionFactory"/>
    </bean>
    
    <bean name="sessionFactory"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
    scope="singleton">
    	<property name="dataSource">
    		<bean class="com.mchange.v2.c3p0.ComboPooledDataSource"
    			destroy-method="close">
    				<property name="driverClass"
    					value="org.postgresql.Driver" />
    				<property name="initialPoolSize"
    					value="2" />
    				<property name="minPoolSize"
    					value="$2" />
    				<property name="maxPoolSize"
    					value="$20" />
    				<property name="maxIdleTime"
    					value="30" />
    				<property name="autoCommitOnClose" value="false" />
    				<property name="user"
    					value="${ph.vmobile.test.jdbc.user}" />
    				<property name="password"
    					value="${ph.vmobile.test.jdbc.password}" />
    				<property name="jdbcUrl"
    					value="${ph.vmobile.test.jdbc.url}" />
    			</bean>
    		</property>
    		<property name="hibernateProperties">
    			<props>
    				<prop key="hibernate.dialect">org.hibernate.dialect.PostgreSQLDialect</prop>
    				<prop key="hibernate.hbm2ddl.auto">create</prop>
    				<prop key="hibernate.show_sql">true</prop>
    				<prop key="hibernate.format_sql">true</prop>
    				<prop key="hibernate.use_sql_comments">true</prop>
    				<prop key="hibernate.generate_statistics">false</prop>
    			</props>
    		</property>
    		<property name="mappingResources">
    			<list>
    				<value>
    					ph/vmobile/test/txnmgmt/entity/Record.hbm.xml
    				</value>
    				<value>
    					ph/vmobile/test/txnmgmt/entity/Item.hbm.xml
    				</value>
    			</list>
    		</property>
    	</bean>
    So I made a unit test that will use the method: successfully inserting an Item and successfully inserting several Records until I fail it on purpose by inserting a Record with a duplicate name.

    Code:
    @Test
    public void testCreateRecordsAndItem() {
    	logger.info("Create item then records...");
    	Service service = (Service) beanFactory.getBean("service");
    	Integer initialCount = service.count();
    	
    	Item item = service.createRecordsAndItem(new String[]{
    			"B1", "B2", "B3", "B4", "B5", 
    			"B6", "B7", "B8", "B9", "B1"},	// Last one is really a duplicate
    			"sampleItem");
    	
    	Integer newCount = service.count();
    	assertEquals(initialCount, newCount);
    	
    	assertNull(item);
    }
    Based on the logs, when the method encounters the Exception, JtaTransactionManager initiates the rollback and properly marks the transaction as rollback-only:

    Code:
    6843 [main] DEBUG org.springframework.transaction.jta.JtaTransactionManager  - Initiating transaction rollback after commit exception
    org.springframework.dao.DataIntegrityViolationException: ...
    ... 6843 [main] DEBUG org.objectweb.jotm.jta  - Current.getStatus()
    6843 [main] DEBUG org.objectweb.jotm.jta  - threadTx.get= java.lang.ThreadLocal@156e5ed
    6843 [main] DEBUG org.objectweb.jotm.jta  - Transaction ret= bb14:38:0:01b8e6e2d9d86bc2ac...075001:
    6843 [main] DEBUG org.objectweb.jotm.jta  - TransactionImpl.getStatus()
    6843 [main] DEBUG org.objectweb.jotm.jta  - Current.rollback()
    6843 [main] DEBUG org.objectweb.jotm.jta  - threadTx.get= java.lang.ThreadLocal@156e5ed
    6843 [main] DEBUG org.objectweb.jotm.jta  - Transaction ret= bb14:38:0:01b8e6e2d9d86bc2ac...075001:
    6843 [main] DEBUG org.objectweb.jotm.jta  - threadTx.set= null
    6843 [main] DEBUG org.objectweb.jotm.jta  - TransactionImpl.rollback(tx= bb14:38:0:01b8e6e2d9d86bc2ac...075001:)
    6843 [main] DEBUG org.objectweb.jotm.jta  - unset timer for tx (timer=org.objectweb.jotm.TimerEvent@127461b, tx=bb14:38:0:01b8e6e2d9d86bc2ac...075001:)
    6843 [main] DEBUG org.objectweb.jotm.jta  - TimerEvent(bb14:38:0:01b8e6e2d9d86bc2ac...075001:).unset
    6843 [main] DEBUG org.objectweb.jotm.jta  - remove tx from xid (xid=bb14:38:0:01b8e6e2d9d86bc2ac...075001:)
    6843 [main] DEBUG org.objectweb.jotm.jta  - reset timeout= 60
    6843 [main] DEBUG org.springframework.orm.hibernate3.SessionFactoryUtils  - Closing Hibernate Session
    6843 [main] DEBUG com.mchange.v2.resourcepool.BasicResourcePool  - trace com.mchange.v2.resourcepool.BasicResourcePool@1e152f4 [managed: 2, unused: 1, excluded: 0] (e.g. com.mchange.v2.c3p0.impl.NewPooledConnection@1bf7b23)
    Supposedly, the whole transaction is rolledback including all the Records and the Item. But when I check the tables, Item with name sampleItem is still inserted.

    I can't figure out how to fix this transaction rollback problem.
    Last edited by erhapal; Nov 5th, 2010, 03:07 AM.

  • #2
    Seriously? Nobody can help me with this one?

    Comment

    Working...
    X