74

I'm new to Spring and I'm wondering if its possible to use numerous transaction managers in the same application?

I have two data access layers - one for both of the databases. I'm wondering, how do you go about using one transaction managers for one layer and different transaction manager for the other layer. I don't need to perform transactions across both databases - yet. But I do need perform transactions on each database individually. I've created an image to help outline my problem:

alt text

Here is my application context configuration:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <context:component-scan base-package="cheetah.repositories" />
    <tx:annotation-driven />

    <bean id="entityManagerFactory"
        class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
        <property name="persistenceUnitName" value="accounts" />
    </bean>

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

    <bean id="transactionManager"
        class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>

</beans>

Here is an example that uses this configuration:

@Repository
public class JpaAccountRepository implements AccountRepository {

    @PersistenceContext(unitName = "cheetahAccounts")
    private EntityManager accountManager;

    @Override
    @Transactional
    public Account findById(long id) {

        Account account = accountManager.find(Account.class, id);
        return account;
    }
}

So for the account repository, I want to use an entity manager factory with the persistence unit set to accounts. However, with my BusinessData Repository, I want to use an entity manager factory with a different persistence unit. Since I can only define one transaction manager bean, how can I go about using different transaction managers for the different repositories?

Thanks for any help.

informatik01
  • 16,038
  • 10
  • 74
  • 104
Brian DiCasa
  • 9,369
  • 18
  • 65
  • 97

2 Answers2

97

Where you use a @Transactional annotation, you can specify the transaction manager to use by adding an attribute set to a bean name or qualifier. For example, if your application context defines multiple transaction managers with qualifiers:

<bean id="transactionManager1"
    class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory1" />
    <qualifier value="account"/>
</bean>

<bean id="transactionManager2"
    class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory2" />
    <qualifier value="businessData"/>
</bean>

You can use the qualifier to specify the transaction manager to use:

public class TransactionalService {

    @Transactional("account")
    public void setSomethingInAccount() { ... }

    @Transactional("businessData")
    public void doSomethingInBusinessData() { ... }
}
Chin Huang
  • 12,912
  • 4
  • 46
  • 47
  • What about if there is a method that reads data from the database where you dont need to use the @Transactional annotation for database reads. – ziggy May 24 '12 at 11:58
  • 3
    Thanks for your answer! but you lost this `` – Dozer Jun 17 '14 at 05:34
  • 3
    what will happen if that `@Transactional` without qualifier of which transaction manger to use – http8086 Dec 09 '19 at 11:50
5

This Spring Jira entry discusses the issue a bit:

https://jira.spring.io/browse/SPR-3955

I think it could be one transaction manager per connection if you're not using two-phase commit. You just need to create two transaction managers and inject them with the appropriate connection.

But I must ask the question: why do you think you need two transaction managers? You can have more than one database connection. The DAOs that use the connections can and should be instantiated by different services, each of which can be annotated with their own transactional settings. One manager can accommodate both. Why do you think you need two?

Peter Rader
  • 237
  • 3
  • 14
duffymo
  • 305,152
  • 44
  • 369
  • 561
  • 24
    I thought I needed two transaction managers because the transaction manager specifies the entity manager factory, which in turn specifies the persistence unit to be used. – Brian DiCasa Dec 12 '10 at 18:26
  • 1
    Can you give any examples where 1 transaction manager can handle transactions in multiple databases having their own datasources? – saran3h Oct 25 '21 at 10:16
  • Examples? That's how transaction managers work. They manage two-phase commits, which involve multiple databases. – duffymo Oct 25 '21 at 11:38