2

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

machinery
  • 3,793
  • 4
  • 41
  • 52

1 Answers1

1

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...

Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419
  • No problem, thanks for the try though :) I've just got it to a point where after throwing an exception I get a rollback (had to add @Transactional to the whole DaoHolder class), but this time the changes from both DAOs seem to have been rolled back because nothing got inserted. This is surprising to me because I was expecting only the transactional dao to get rolled back. Anyway - this is still not the behaviour that I need. – machinery Dec 28 '11 at 14:06
  • Perhaps you should configure the flushmode of your non transactionnal database. It may be possible that the row is inserted into your non transactionnal session but the flush simply didn't happen. Do you see anything in the hibernate SQL logs (show_sql property)? What about your db configuration? Did you set something like autocommit=true or something like that? – Sebastien Lorber Dec 28 '11 at 14:16
  • Well, if I don't throw the Exception I do see both inserts in the hibernate SQL logs. If I throw the Exception I don't see any inserts. Hmm.. in the logs I see this: **Opened new Session [org.hibernate.impl.SessionImpl@1517997] for Hibernate transaction** just once. So I guess my problem is caused by the fact that in reality both of these operations are done on the same Hibernate session. Why is it so and how can I change it? – machinery Dec 28 '11 at 14:34
  • 1
    Sorry i don't really know. But generally Spring binds the session opened by @Transactionnal annotations to the current Thread and thus maybe if you use spring dao support / hibernate template or something like this, it sees that a session is bound to the thread and simply use it... In both your dao's, try to do "getSession()" or something and check with logs if it's the same object – Sebastien Lorber Dec 28 '11 at 14:40
  • Great idea, thanks :) I'll try this right away. Do you know any good resources on spring transaction management? I'd like to know in more detail what exactly happens under the covers when you annotate something with @Transactional. The Spring docs are helpful but they did not provide me with enough info... Thanks again. – machinery Dec 28 '11 at 14:51
  • Checked - in both DAOs getSession() returns DIFFERENT objects. So it appears I've got two sessions. Now I need to make sure one of them is not transactional. – machinery Dec 28 '11 at 15:37
  • Sorry i don't know if there is a well explained doc about all the magic spring does for you... when the magic doesn't work well it's always a pain ;) Btw it would be good to have your dao implementations – Sebastien Lorber Dec 28 '11 at 15:52
  • Hi :) I've rephrased this question to make it more precise and now it's here: http://stackoverflow.com/questions/8700160/spring-hibernatetransactionmanager-handling-multiple-datasources You can find the Dao implementations there as well. Thx – machinery Jan 03 '12 at 07:42