Friday, July 10, 2009

Hibernate session issue during Spring tests

I reckon this is something good to share as it took me a few hours to get the problem resolved.

We are using SpringJUnit4ClassRunner for our DAO tests, the tests passed when they were run in Eclipse, but not in Maven. One of the test classes failed with HibernateSystemException:

org.springframework.orm.hibernate3.HibernateSystemException: Illegal attempt to associate a collection with two open sessions; nested exception is org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions

Google search gave me some clue about the problem had something to do with cascading style, transaction, and duplication but they didn't lead me anywhere. I broke at AbstractPersistentCollection.setCurrentSession to find out that the session/session factory (DAOs are instances of HibernateDaoSupport) assigned to one of the DAOs was different.

A second look at the test class which had:
@Autowired
BillingInvoiceDao $;
BillingOrderDao orderDao;
BillingItemDao itemDao;
MembershipDao membershipDao;

@Override
protected void setUpDataAccessObjects() {
membershipDao = DataAccessObjectHelper.getDataAccessObject(MembershipDao.class);
itemDao = DataAccessObjectHelper.getDataAccessObject(BillingItemDao.class);
orderDao = DataAccessObjectHelper.getDataAccessObject(BillingOrderDao.class);
}

I found out that BillingInvoiceDao was not a proxy instance but the other DAOs are. The log may explain something:
[07-11 09:19:30] DEBUG AutowiredAnnotationBeanPostProcessor [main]: Autowiring by type from bean name 'itest.net.aflexi.cdn.billing.HibernateBillingInvoiceDaoTest' to bean named 'billingInvoiceDao'
[07-11 09:19:30] DEBUG DefaultListableBeanFactory [main]: Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
[07-11 09:19:30] DEBUG DefaultListableBeanFactory [main]: Returning cached instance of singleton bean 'org.springframework.transaction.config.internalTransactionAdvisor'
[07-11 09:19:30] DEBUG AnnotationTransactionAttributeSource [main]: Adding transactional method [testCrud] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
[07-11 09:19:30] DEBUG AnnotationAwareAspectJAutoProxyCreator [main]: Creating implicit proxy for bean 'itest.net.aflexi.cdn.billing.HibernateBillingInvoiceDaoTest' with 0 common interceptors and 1 specific interceptors
[07-11 09:19:30] DEBUG JdkDynamicAopProxy [main]: Creating JDK dynamic proxy: target source is SingletonTargetSource for target object [itest.net.aflexi.cdn.billing.HibernateBillingInvoiceDaoTest@17afcff]
[07-11 09:19:30] DEBUG AnnotationTransactionAttributeSource [main]: Adding transactional method [testCrud] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
[07-11 09:19:30] DEBUG TransactionalTestExecutionListener [main]: Explicit transaction definition [PROPAGATION_REQUIRED,ISOLATION_DEFAULT] found for test context [[TestContext@134b58c testClass = HibernateBillingInvoiceDaoTest, locations = array['classpath:spring/dataAccessTestContext.xml', 'classpath:spring/dataAccessTestContext.billing.xml'], testInstance = itest.net.aflexi.cdn.billing.HibernateBillingInvoiceDaoTest@17afcff, testMethod = testCrud@HibernateBillingInvoiceDaoTest, testException = [null]]]
[07-11 09:19:30] DEBUG TransactionalTestExecutionListener [main]: Retrieved @TransactionConfiguration [@org.springframework.test.context.transaction.TransactionConfiguration(defaultRollback=true, transactionManager=mainTxManager)] for test class [class itest.net.aflexi.cdn.billing.HibernateBillingInvoiceDaoTest]
[07-11 09:19:30] DEBUG TransactionalTestExecutionListener [main]: Retrieved TransactionConfigurationAttributes [[TransactionConfigurationAttributes@ddc524 transactionManagerName = 'mainTxManager', defaultRollback = true]] for class [class itest.net.aflexi.cdn.billing.HibernateBillingInvoiceDaoTest]
[07-11 09:19:30] DEBUG DefaultListableBeanFactory [main]: Returning cached instance of singleton bean 'mainTxManager'
[07-11 09:19:30] DEBUG TransactionalTestExecutionListener [main]: Executing @BeforeTransaction method [public void net.aflexi.cdn.core.test.AbstractDaoTest.setUpBeforeTransaction() throws java.lang.Exception] for test context [[TestContext@134b58c testClass = HibernateBillingInvoiceDaoTest, locations = array['classpath:spring/dataAccessTestContext.xml', 'classpath:spring/dataAccessTestContext.billing.xml'], testInstance = itest.net.aflexi.cdn.billing.HibernateBillingInvoiceDaoTest@17afcff, testMethod = testCrud@HibernateBillingInvoiceDaoTest, testException = [null]]]
[07-11 09:19:30] DEBUG DefaultListableBeanFactory [main]: Returning cached instance of singleton bean 'membershipDao'

That transaction propagation happened after autowiring of beans. The fix of my problem is to @Autowired all DAOs (so that they are consistent) or assign them via the DataAccessObjectHelper as shown in the snippet.

Hope this helps.

No comments: