machinery
machinery

Reputation: 3945

Spring + Hibernate: using transactional and non-transactional datasources together

In my Spring 2.5.6 + Hibernate app I need to read/write data to/from multiple databases with different schemas. The app is on Tomcat, so for the time being I'd prefer not to have to use JTA so that I don't need to migrate to a full-blown application server.

So I decided to have just one transactional datasource. I can live with the others not being transactional.

But somehow I cannot get it to work. Could you have a look to give me a clue as to what I might be doing wrong?

This is my applicationContext.xml:

 <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <!-- ..configuration.. -->
</bean>

<bean id="nonTransactionalSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="nonTransactionalDataSource" />
    <!-- ..configuration.. -->
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <!-- ..configuration.. -->
</bean>

<bean id="nonTransactionalDataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <!-- ..configuration.. -->
</bean>

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean id="daoHolder" class="com.app.services.TransactionTest$DaoHolder"/>

<tx:annotation-driven transaction-manager="txManager"/>

<bean id="transactionalDao" class="com.app.services.TransactionalDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="nonTransactionalDao" class="com.app.services.NonTransactionalDaoImpl">
        <property name="sessionFactory" ref="nonTransactionalSessionFactory" />
</bean>

As you can see the above is just a definition of two session factories, each using its own datasource. The transaction manager is using only one of these session factories and annotation-driven tx-management is configured.

Here's my Unit Test in which I'm trying to test transactional behaviour:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"/applicationContext.xml"})
public class TransactionTest {

@Autowired
private DaoHolder daoHolder;

@Test
public void testTransactions() throws Exception {

    try {
        daoHolder.runTransactionalMethod();
    } catch (Exception exception) {
        System.out.println("Exception caught");
    }
}

public static class DaoHolder {

    @Autowired
    private TransactionalDao transactionalDao;
    @Autowired
    private NonTransactionalDao nonTransactionalDao;

    @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, rollbackFor={Exception.class})
    private void runTransactionalMethod() throws Exception {
        transactionalDao.insertRow();
        nonTransactionalDao.insertRow();
        throw new Exception();
    }

}

As a result of the above test I'm expecting to have a new row inserted into the non transactional datasource, because the changes on the transactional datasource should get rolled back, because there's an exception thrown. However, after this test passes, I see rows inserted into both datasources.

EDIT: I've moved a bit further. I've made DaoHolder into an interface, moved the above logic into a class implementing the interface (DaoHolderImpl) and marked also the class (not just the method) with @Transactional. I've also added this class as a Spring Bean. So now Spring handles my transactions. But this time, when throwing the Exception, the new row is rolled back from both datasources, not just the transactional one. This is still not what I was expecting :/

Does anyone see what I'm doing wrong? Thanks in advance,

Peter

Upvotes: 2

Views: 1396

Answers (1)

Sebastien Lorber
Sebastien Lorber

Reputation: 92140

What about putting breakpoints at the "throw exception" line so that you can see if one or both databases have that line?

It doesn't fix your strange rollback not appening problem but may tell you if your non transactional datasource works well.

You'd rather enable spring/hibernate logging to see what happens about the transactions...

Upvotes: 1

Related Questions