简而言之
我的命令行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上的事务管理器,因为它只是只读的。此问题仅适用于我的测试类。
答案 0 :(得分: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 {
...
答案 1 :(得分:1)
我使用开源TM Atomikos在JUnit测试中使用了XA事务和回滚。一个很好的功能是Atomikos允许使用非XA启用的数据源参与XA事务。请查看此链接以获取示例:http://www.atomikos.com/Documentation/NonXaDataSource
另一方面,如果XA是一个适合您的JUnit问题的解决方案是另一个故事。您的测试是重点关注数据库实现(Sybase)还是更多关于Java逻辑?我通常设置嵌入式数据库,如Apache Derby或HQSQL,用于JUnit测试。然后我不必太关心清理,因为GC会处理:)
答案 2 :(得分:0)
可能的解决方法是引入一个注释为@Transactional("transactionManagerTarget")
的辅助bean,并将您的测试注释为@Transactional("transactionManager")
,并使用defaultRollback = true
进行配置。然后,您的测试必须调用辅助bean,然后调用您的服务bean进行测试。这应该导致服务周围的事务回滚,然后围绕DBUnit进行事务。
其他可能的方法: