本文介绍了Spring 中具有多个事务管理器的多个数据源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

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

I did not have a problem when I used a single transaction manager for dataSource2, even though dataSource3 was 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:

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

因为定义了多个事务管理器,所以我限定了他们自己的价值观.如您所见,dataSource3 正在使用 setter 方法注入:

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

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() { }
}

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

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

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$1.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$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.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$1.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?

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

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

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

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

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

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

推荐答案

你的配置有几个问题:

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

bean 名称 不同,但属性名称 不同.您应该将其更改为:

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

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

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

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

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

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

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

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

第三:@Qualifier("dataSource").请注意,它不是带有限定符和 bean 名称的投诉(注意:它是 dataSource1)

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

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

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.

这篇关于Spring 中具有多个事务管理器的多个数据源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-04 06:57