Here is the method I wish to implement:
And it's interface-based transactional method: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; }
Here are the hibernate mapping files:Code:@Transactional(propagation=Propagation.REQUIRED, rollBackFor=Exception.class) public abstract Item createRecordsAndItem(String[] names, String itemName);
Here are the spring bean configuration 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>
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:<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>
Based on the logs, when the method encounters the Exception, JtaTransactionManager initiates the rollback and properly marks the transaction as rollback-only: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); }
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.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)
I can't figure out how to fix this transaction rollback problem.


Reply With Quote
