5

简而言之

我的命令行 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 上的事务管理器,因为它只是只读的。此问题仅适用于我的测试课程。

4

3 回答 3

1

使用<qualifier>事务管理器定义中的元素。

<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 {
...
于 2012-05-18T12:49:13.717 回答
1

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

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

于 2012-05-18T21:48:47.483 回答
0

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

不过,这有点乱。

其他可能的方法:

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