Java Spring 中具有多个事务管理器的多个数据源

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23038261/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 20:13:00  来源:igfitidea点击:

Multiple DataSources with Multiple Transaction Managers in Spring

javaspringtransactionsdatasource

提问by Malvon

I have three DataSource's (JDBC) defined in the Junit application context file. Two of them need to be transactionally managed; I do not have to chain any of the methods when using these two data sources (they are entirely independent of each other).

DataSource在 Junit 应用程序上下文文件中定义了三个(JDBC)。其中两个需要事务管理;使用这两个数据源时,我不必链接任何方法(它们完全相互独立)。

I did not have a problem when I used a single transaction manager for dataSource2, even though dataSource3was being utilized but not managed in the corresponding methods. Nonetheless, upon needing to also manage methods from various DAO classes that solely use dataSource3, I added the second transaction -- txManager2. The context file contains the following:

当我使用单个事务管理器时,我没有问题dataSource2,即使dataSource3在相应的方法中被使用但没有管理。尽管如此,由于还需要管理来自单独使用 的各种 DAO 类的方法dataSource3,我添加了第二个事务 - txManager2。上下文文件包含以下内容:

<context:component-scan base-package="my.pkg" />

<bean id="dataSource1"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
    <property name="url" value="jdbc:oracle:thin:@host1:1521:dbsid1" />
<property name="username" value="username1" />
<property name="password" value="password1" />
</bean>

<bean id="dataSource2"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
    <property name="url" value="jdbc:oracle:thin:@host2:1521:dbsid2" />
<property name="username" value="username2" />
<property name="password" value="password2" />
</bean>

<bean id="dataSource3"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
    <property name="url" value="jdbc:oracle:thin:@host3:1521:dbsid3" />
<property name="username" value="username3" />
<property name="password" value="password3" />
</bean>

<tx:annotation-driven/>

<bean id="txManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <qualifier value="txManager1"/>
<property name="dataSource2" ref="dataSource2"/>
</bean>

<bean id="txManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <qualifier value="txManager2"/>
<property name="dataSource3" ref="dataSource3"/>
</bean>

Since multiple transaction managers are being defined, I qualifiedthem with their own values. As you can see, dataSource3is being injected using a setter method:

由于定义了多个事务管理器,我用它们自己的值来限定它们。如您所见,dataSource3正在使用 setter 方法注入:

package my.pkg;

@Component
public class MyDAO {

    private DataSource dataSource3;

    // Read only from datasource
    @Autowired
    @Qualifier("dataSource")
    public void setDataSource(DataSource ds) {
        template = new NamedParameterJdbcTemplate(ds);
    }

    // Performs reads/updates/inserts from datasource2
    @Autowired
    @Qualifier("dataSource2")
    public void setDataSource2(DataSource ds) {
        iTemplate = new NamedParameterJdbcTemplate(ds);
    }

    // Performs reads/updates/inserts from datasource3
    @Autowired
    @Qualifier("dataSource3")
    public void setDataSource3(DataSource ds) {
        dataSource3 = ds;
        uTemplate = new NamedParameterJdbcTemplate(ds);
    }

    @Transactional("txManager1")    
    public String insertProcs() { }

    @Transactional("txManager2")    
    public String updateProcs() { }
}

However, upon running the Junit unit test, I get the following:

但是,在运行Junit 单元测试时,我得到以下信息:

java.lang.IllegalStateException: Failed to load ApplicationContext
    at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:308)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:321)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:211)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access
<tx:annotation-driven transaction-manager="txManager1"/>

<bean id="txManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <qualifier value="txManager1"/>
    <property name="dataSource" ref="dataSource2"/>
</bean>

<bean id="txManager2" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <qualifier value="txManager2"/>
    <property name="dataSource" ref="dataSource3"/>
</bean>
0(ParentRunner.java:42) at org.junit.runners.ParentRunner.evaluate(ParentRunner.java:184) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:49) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'txManager2' defined in class path resource [test-context.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property 'dataSource3' of bean class [org.springframework.jdbc.datasource.DataSourceTransactionManager]: Bean property 'dataSource3' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter? at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1363) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1085) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:516) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:455) at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:293) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:290) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:192) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:84) at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:1) at org.springframework.test.context.TestContext.loadApplicationContext(TestContext.java:280) at org.springframework.test.context.TestContext.getApplicationContext(TestContext.java:304) ... 24 more Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'dataSource3' of bean class [org.springframework.jdbc.datasource.DataSourceTransactionManager]: Bean property 'dataSource3' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter? at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:1052) at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:921) at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:76) at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:58) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1360)

Any idea where the source of the problem is?

知道问题的根源在哪里吗?

Update

更新

Based on the answer accepted, the following changes were made to get the intended functionality:

根据接受的答案,进行了以下更改以获得预期的功能:

    // Read only from datasource
    @Autowired
    @Qualifier("dataSource1")
    public void setDataSource1(DataSource ds) {
        template = new NamedParameterJdbcTemplate(ds);
    }

@Qualifier("dataSource")should have been corrected (typo on my part):

@Qualifier("dataSource")应该更正(我的错别字):

<property name="dataSource2" ref="dataSource2"/>
<property name="dataSource3" ref="dataSource3"/>

采纳答案by Ori Dar

There are few problems with your configuration:

您的配置有几个问题:

<property name="dataSource" ref="dataSource2"/>
<property name="dataSource" ref="dataSource3"/>

The bean namesare different, but property namesare not. You should change this to:

bean的名字虽然不同,但属性名称都没有。您应该将其更改为:

##代码##

E.g.: in setter properties for txManager1 and txManager2 beans respectively.

例如:分别在 txManager1 和 txManager2 bean 的 setter 属性中。

Second: @Qualifier("dataSource2")and @Qualifier("dataSource3")are wrong since your are qualifying the TX managers beans, and not the data-source beans.

第二:@Qualifier("dataSource2")并且@Qualifier("dataSource3")是错误的,因为您正在限定 TX 管理器 bean,而不是数据源 bean。

You can use @Resource("dataSource2")and @Resource("dataSource3")instead, or qualify the data-source beans as well.

您可以使用@Resource("dataSource2")and@Resource("dataSource3")代替,或者也可以限定数据源 bean。

Third: @Qualifier("dataSource"). Notice it's not complaint either with a qualifier and a bean's name (note: it's dataSource1)

第三:@Qualifier("dataSource")。请注意,这不是对限定符和 bean 名称的抱怨(注意:它是dataSource1

As a last thing, you have defined tx:annotation-driven. It expects a transaction manager whose bean name is transactionManager. You have failed to give it's bean definition as well, so this will fail either.

最后,您已经定义了tx:annotation-driven. 它需要一个 bean 名称为transactionManager的事务管理器。你也没有给出它的 bean 定义,所以这也会失败。