@EnableTransactionManagement annotation with 2 transaction managers

此生再无相见时 提交于 2019-12-17 17:55:31

问题


I am using @Configuration annotation for configuration of spring instead of xml file. I am configuring 2 datasources with different session factory and different transaction managers. I am stuck with a problem here for @EnableTransactionManagement annotation. I read in its documentation that,

@EnableTransactionManagement is more flexible; it will fall back to a by-type lookup for any PlatformTransactionManager bean in the container. Thus the name can be "txManager", "transactionManager", or "tm": it simply does not matter.

This means whatever name I give to method, it will always search for the method which returns PlatformTransactionManager object while I have 2 transactionmanagers. Now the problem is, when I test this class, it gives me error:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.springframework.transaction.PlatformTransactionManager] is defined: expected single bean but found 2

I even tried to have 2 different Configuration classes but in vain. In xml configuration, this was not the case. I registered my both transaction managers with two <tx:annotation-driven transaction-manager="" /> tag and it worked fine. But not able to do same here with annotations.

What should I do if I want to configure 2 datasources with 2 different transaction managers in Spring annotated configuration class?


回答1:


In your configuration class, use @EnableTransactionManagement annotation.

Define a transaction manager in this class as:

    @Bean(name="txName")
    public HibernateTransactionManager txName() throws IOException{
        HibernateTransactionManager txName= new HibernateTransactionManager();
        txName.setSessionFactory(...);
        txName.setDataSource(...);
        return txName;
   }

There on, in your class/method that executes transactional job(s), annotate as follows:

@Transactional("txName")

or

@Transactional(value = "txName")

This is how you would tie a name qualified transaction manager to wherever you need it. You can now have as many transaction managers as you want and use it accordingly wherever you need.




回答2:


Just in case anyone runs into this problem, I found a solution:

@Configuration
@EnableTransactionManagement
@DependsOn("myTxManager")
@ImportResource("classpath:applicationContext.xml")
public class AppConfig implements TransactionManagementConfigurer {

@Autowired
private PlatformTransactionManager myTxManager;

...

@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
    return this.myTxManager;
}

In this way, you can use a specific txManager defined in an xml configuration.

In case you want to define the txManager used on service-level, you shall remove the @EnableTransactionManagement annotation from the @Configuration class and specify the txManager in the @Transactional annotations, e.g.

@Service
@Transactional(value="myTxManager", readOnly = true)
public class MyServiceImpl implements MyService { ... }



回答3:


From the java doc

For those that wish to establish a more direct relationship between
@EnableTransactionManagement and the exact transaction manager bean to be used, the TransactionManagementConfigurer callback interface may be implemented - notice the implements clause and the @Override-annotated method below:

Your @Configuration class needs to implement TransactionManagementConfigurer interface - implement the annotationDrivenTransactionManager which will return the reference to the transactionManager that should be used.




回答4:


I am not sure why you are using two TransactionManagers . You could consider using the same TransactionManager for multiple datasource via the AbstractRoutingDataSource . Please refer

http://blog.springsource.org/2007/01/23/dynamic-datasource-routing/

for a sample on its usage.




回答5:


Some of the other answers imply that using two transaction managers is in some way wrong; however, Spring's XML configuration allows for using multiple transaction managers as stated in the online documentation (below). Unfortunately, there does not seem to be a way to make the @EnableTransactionManagement annotation work in a similar manner. As a result, I simply use an @ImportResource annotation to load an XML file that includes the <tx:annotation-driven/> line. This allows you to get a Java configuration for most things but still make use of @Transactional with an optional Transaction Manager qualifier.

http://docs.spring.io/spring/docs/3.1.x/spring-framework-reference/html/transaction.html

Most Spring applications only need a single transaction manager, but there may be situations where you want multiple independent transaction managers in a single application. The value attribute of the @Transactional annotation can be used to optionally specify the identity of the PlatformTransactionManager to be used. This can either be the bean name or the qualifier value of the transaction manager bean. For example, using the qualifier notation, the following Java code




回答6:


Try to use chained TransactionalManager

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.transaction.ChainedTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

@Configuration
public class ChainedDBConfig {

    @Bean("chainedTransactionManager")
    public PlatformTransactionManager transactionManager(
            @Qualifier("database1TransactionManager") final PlatformTransactionManager db1PlatformTransactionManager,
            @Qualifier("database2TransactionManager") final PlatformTransactionManager db2PlatformTransactionManager) {

        return new ChainedTransactionManager(db1PlatformTransactionManager, db2PlatformTransactionManager);
    }

}

And place the following annotation on your service class:

@Transactional(transactionManager = "chainedTransactionManager")
public class AggregateMessagesJobIntegrationTest {
   ...
}

You can also use it inside the integration tests:

@RunWith(SpringRunner.class)
@Transactional(transactionManager = "chainedRawAndAggregatedTransactionManager")
@Rollback
public class ExampleIntegrationTest extends AbstractIntegrationTest {
    ....
}

and it will do a rollback for both DB transaction managers.



来源:https://stackoverflow.com/questions/8050183/enabletransactionmanagement-annotation-with-2-transaction-managers

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!