【问题标题】:How to configure mutliple transaction managers with Spring + DBUnit + JUnit如何使用 Spring + DBUnit + JUnit 配置多个事务管理器
【发布时间】:2012-05-18 11:29:56
【问题描述】:

简而言之

我的命令行 Java 应用程序在不使用 XA 的情况下将数据从一个数据源复制到另一个数据源。我已经配置了两个单独的数据源,并且想要一个可以回滚两个数据源上的数据的 JUnit 测试。我使用 DBUnit 将数据加载到“源”数据库中,但我无法让它回滚。我可以让“目标”数据源回滚。

我的代码

鉴于此配置...

<tx:annotation-driven />

<!-- note the default transactionManager name on this one -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource"     ref="dataSourceA" />
</bean>

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

还有这段代码……

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:resources/spring-context.xml",
                                "classpath:resources/spring-db.xml"})  
@Transactional
@TransactionConfiguration(transactionManager = "transactionManagerTarget", defaultRollback = true) 
public class MyIntegrationTest {

    @Autowired
    private MyService service;

    @Autowired
    @Qualifier("dataSourceA")
    private DataSource dataSourceA;

    private IDataSet loadedDataSet;

    /**
     * Required by DbUnit
     */
    @Before
    public void setUp() throws Exception {
        SybaseInsertIdentityOperation.TRUNCATE_TABLE.execute(getConnection(), getDataSet());
        SybaseInsertIdentityOperation.INSERT.execute(getConnection(), getDataSet());
    }

    /**
     * Required by DbUnit
     */
    protected IDataSet getDataSet() throws Exception {
        loadedDataSet = DbUnitHelper.getDataSetFromFile(getConnection(), "TestData.xml");
        return loadedDataSet;
    }

    /**
     * Required by DbUnit
     */
    protected IDatabaseConnection getConnection() throws Exception{
        return new DatabaseConnection(dataSourceA.getConnection());
    }   

    @Test
    public void testSomething() {

        // service.doCopyStuff();

    }

}

在我看来,问题在于@TransactionConfiguration 仅声明了启用回滚的目标数据源。 DBUnit 被明确地传递给dataSourceA,并且正在选择名为transactionManager(我不确定如何)的默认事务管理器,它没有被告知回滚。

问题

如何让两个事务管理器回滚?

当我的数据源不支持 XA 事务时,我可以使用单个事务管理器吗?

注意:应用程序在生产环境中运行时不需要 dataSourceA 上的事务管理器,因为它只是只读的。此问题仅适用于我的测试类。

【问题讨论】:

    标签: spring junit dbunit spring-transactions


    【解决方案1】:

    在事务管理器定义中使用&lt;qualifier&gt; 元素。

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSourceA" />
        <qualifier value="transactionManager" />
    </bean>
    
    <bean id="transactionManagerTarget" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSourceB" />
        <qualifier value="transactionManagerTarget" />
    </bean>
    

    然后你可以直接在@Transactional注解中引用你要使用的那个,即

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations={"classpath:resources/spring-context.xml",
                                    "classpath:resources/spring-db.xml"})  
    @Transactional("transactionManagerTarget")
    @TransactionConfiguration(defaultRollback = true) 
    public class MyIntegrationTest {
    ...
    

    【讨论】:

    • 我对为什么这不起作用的理解是@Before@Test 方法将在同一个事务中执行,我无法指定多个@Transactional 注释。在您的回答中,我看不出它如何告诉transactionManager 回滚。我错过了什么吗?不过使用&lt;qualifier&gt; 的好处是。
    • 我在我的测试类中尝试了@Transactional 的各种用法,但不幸的是,我怀疑@Test 方法启动了一个事务,而@Before 无法为DBUnit 启动另一个事务,因此它继续使用transactionManagerTarget 运行因此不会回滚 DBUnit 数据。我想我尝试了所有可以使用注释的方法,但没有运气。
    【解决方案2】:

    我在使用开源 TM Atomikos 的 JUnit 测试中使用了 XA 事务和回滚。一个不错的特性是 Atomikos 允许使用未启用 XA 的数据源参与 XA 事务。查看此链接以获取示例:http://www.atomikos.com/Documentation/NonXaDataSource

    另一方面,如果 XA 是解决您的 JUnit 问题的一个不错的解决方案,那就另当别论了。您的测试主要关注数据库实现(Sybase)还是更多地关注 Java 逻辑?我通常为 JUnit 测试设置嵌入式数据库,如 Apache Derby 或 HQSQL。然后我不必太在意清理工作,因为 GC 会处理它:)

    【讨论】:

    • 感谢您的回复。 DBUnit 仅用于加载测试数据,因为该数据源已经由另一个服务在生产中填充。所以我在生产中没有 XA 问题,因为我只写入一个目标数据源。我喜欢您将 HSQL 用于我的“源”数据源的想法。我只需要弄清楚如何存储我的模式以供 HSQL 加载并使其与 Sybase 模式保持一致。
    • 任何 JTA TM 对我都没有好处,因为我的两个数据源都不是 XA。大多数 JTA TM 允许您涉及一个非 XA 数据源,但不再涉及
    • 一个非常长的镜头,但什么.. 跳过 HQSQL,使用 Apache Derby 作为数据源之一(源)。您不能将架构存储在 test/resources/source_db_setup.sql 中吗? Derby 已启用 XA。然后你可以使用 sybase 作为你的非 xa 源。我认为您应该有其他选择,不过,我目前没有任何好主意。
    【解决方案3】:

    一种可能的解决方法是引入一个注释为@Transactional("transactionManagerTarget") 的辅助bean,并将您的测试注释为@Transactional("transactionManager"),同时使用defaultRollback = true 进行配置。然后,您的测试将不得不调用助手 bean,而后者又会调用您的服务 bean 进行测试。这应该会导致围绕您的服务的事务回滚,然后是围绕 DBUnit 的事务。

    虽然有点乱。

    其他可能的方法:

    • 使用内存数据库(例如 H2)代替生产数据库 - 您可以将其配置为在需要时删除其所有数据。
    • 允许 DBUnit 提交,并在您的拆卸方法中有一个补偿事务来清除数据。

    【讨论】:

    • 我尝试分解出一个辅助 bean,但这无助于解决问题。当有多个事务管理器时,DBUnit 不会回滚。我将接受您使用 HSQL 作为前进方向的答案,但是在我的情况下,我仍然卡住了,因为我使用 Sybase 在此数据源上创建临时表,并且语法与 HSQL 不兼容。
    猜你喜欢
    • 2014-05-22
    • 2014-02-22
    • 2017-07-30
    • 1970-01-01
    • 1970-01-01
    • 2010-10-18
    • 1970-01-01
    • 2022-01-27
    • 1970-01-01
    相关资源
    最近更新 更多