Dummy Me Dummy Me - 7 months ago 48
Java Question

Spring @Transactional behaviour with only one transactional data source

I have an app that accesses two databases. Obviously, if I need to have a transaction span both DBs, I need to use something like two phase commit. But I don't need this king of guarantee right now, I don't need everything to be transactional. Things can crash or succeed independently, the app can handle it and not end-up in an inconsistent state.

Right now I have a setup like this one (removed interfaces, code, etc to make it as simple as possible to explain):

Two data sources:

<bean id="firstDS" class="org.springframework.jndi.JndiObjectFactoryBean">
<qualifier value="firstDS" />
<property name="jndiName" value="java:comp/env/jdbc/firstDS" />
</bean>

<bean id="secondDS" class="org.springframework.jndi.JndiObjectFactoryBean">
<qualifier value="secondDS" />
<property name="jndiName" value="java:comp/env/jdbc/secondDS" />
</bean>


One transaction manager for only one of the data sources:

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

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="firstDS" />
</bean>


A service:

@Service
@Transactional(propagation = Propagation.REQUIRED)
public class Service {

@Autowired
private FirstDao firstDao;

@Autowired
private SecondDao secondDao;

public void updateStuff() {
firstDao.updateStuff();
secondDao.updateStuff();
}
}


and two DAOs:

@Transactional(propagation = Propagation.MANDATORY)
public class FirstDao {

@Autowired
@Qualifier("firstDS")
private DataSource dataSource;

public void updateStuff() {
// updates stuff in the first database using dataSource
}
}

@Transactional(propagation = Propagation.MANDATORY)
public class SecondDao {

@Autowired
@Qualifier("secondDS")
private DataSource dataSource;

public void updateStuff() {
// updates stuff in the second database using dataSource
}
}


Now, this runs without problems (or at least none that I could observe) but my questions are:


  • is this safe or does it have any side effects?

  • How does the second DAO behave since it is transactional but the transaction is for another database?

  • Should I remove the @Transactional annotation from the second DAO? Will it then behave just like any other method call?



I've read the reference and various posts online but I still am not sure about the behavior.

Answer

Is it safe ?

No. SecondDao.updateStuff will be executed without logical transaction. It means that every single query executed during SecondDao.updateStuff will be executed in a separate physical transaction in auto-commit mode. In other words : SecondDao.updateStuff is not transactional (and may even use one separate connection for every query... which can lead to performance problems).

What can you do ?

First declare a transactionManager for your secondDS :

<tx:annotation-driven/>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="firstDS" />
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="secondDS" />
</bean>

Then specify that SecondDAO use transactionManger2 :

@Transactional(value="transactionManager2",propagation = Propagation.REQUIRED)
public class SecondDao {

    @Autowired
    @Qualifier("secondDS")
    private DataSource dataSource;

    public void updateStuff() {
        // updates stuff in the second database using dataSource
    }
}