香草 JDBC 更新未在 spring 单元测试中回滚
Vanilla JDBC update not rolled back in spring unit test
我有一个 spring 单元测试,用于插入两个不同表的 DAO 方法。测试完成后,其中一个插入会按预期回滚,但另一个不会!我真的不知道发生了什么。我已经多次调试测试,以便我可以看到(未提交的)更改何时出现在数据库中,但只有一个消失。
我能看到的唯一区别是,在一种情况下,插入是使用原始 JDBC 完成的,而在第二种情况下,插入是使用 Sping 的 JdbcTemplate 完成的。但是不应该都在同一个事务中然后回滚吗?
这是我的测试 class:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/test-context.xml"})
@Rollback
@Transactional(transactionManager = "txManager")
public class MyDaoIntegrationTest {
@Autowired
private DataSource dataSource;
@Autowired
private MyDao myDao;
@Test
public void createMyObject_test() throws Exception {
MyObject myObject = new MyObject();
myDao.createMyObject(myObject, 123L);
}
}
我的测试-context.xml 看起来像这样:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="myDao" class="my.package.myDao">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="url" value="${unit.test.db.url}"/>
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="username" value="${unit.test.db.user}"/>
<property name="password" value="${unit.test.db.pass}"/>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
要测试的方法看起来很粗略(删除了连接关闭时的噪音等)所以:
public void createMyObject(MyObject vo, long refId) throws SQLException {
Connection cn = dataSource.getConnection();
PreparedStatement ps = cn.prepareStatement("insert into MY_OBJECT (COL1, COL, ...)) values (?,?,...)");
ps.setInt(1, vo.getCol1());
ps.setInt(2, vo.getCol2());
...
ps.executeUpdate();
MyObjectEventVO event = new MyObjectEventVO();
createMyObjectEvent(event);
}
public void createMyObjectEvent(MyObjectEventVO vo) throws DataAccessException {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String updateSql =
"insert into MY_OBJECT_EVENT(COL1, COL2, ...) "
+ "values (?, ?, ...)";
Object[] params = {vo.getCol1(), vo.getCol2(), ... };
int[] types = {Types.INTEGER, Types.VARCHAR, ...};
jdbcTemplate.update(updateSql, params, types);
}
更新
我已经尝试注释掉对 createMyObjectEvent(event) 的调用,以便只发生一次插入。最终结果是一样的:第一个插入没有回滚。所以第一次插入的事务好像有问题
更新 2
我重构了第一个插入也使用 JdbcTemplate 然后一切正常!所以这个问题可以改写为:如何让普通 JDBC 代码在 spring 单元测试中回滚?
@Rollback 需要 spring 托管事务才能工作。
要参与 spring 托管事务,最简单的方法是使用 JDBCTemplate or DataSourceUtils。
两者都包含使用 spring 托管事务所需的代码。
我有一个 spring 单元测试,用于插入两个不同表的 DAO 方法。测试完成后,其中一个插入会按预期回滚,但另一个不会!我真的不知道发生了什么。我已经多次调试测试,以便我可以看到(未提交的)更改何时出现在数据库中,但只有一个消失。
我能看到的唯一区别是,在一种情况下,插入是使用原始 JDBC 完成的,而在第二种情况下,插入是使用 Sping 的 JdbcTemplate 完成的。但是不应该都在同一个事务中然后回滚吗?
这是我的测试 class:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:spring/test-context.xml"})
@Rollback
@Transactional(transactionManager = "txManager")
public class MyDaoIntegrationTest {
@Autowired
private DataSource dataSource;
@Autowired
private MyDao myDao;
@Test
public void createMyObject_test() throws Exception {
MyObject myObject = new MyObject();
myDao.createMyObject(myObject, 123L);
}
}
我的测试-context.xml 看起来像这样:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="myDao" class="my.package.myDao">
<constructor-arg name="dataSource" ref="dataSource"/>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="url" value="${unit.test.db.url}"/>
<property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
<property name="username" value="${unit.test.db.user}"/>
<property name="password" value="${unit.test.db.pass}"/>
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
要测试的方法看起来很粗略(删除了连接关闭时的噪音等)所以:
public void createMyObject(MyObject vo, long refId) throws SQLException {
Connection cn = dataSource.getConnection();
PreparedStatement ps = cn.prepareStatement("insert into MY_OBJECT (COL1, COL, ...)) values (?,?,...)");
ps.setInt(1, vo.getCol1());
ps.setInt(2, vo.getCol2());
...
ps.executeUpdate();
MyObjectEventVO event = new MyObjectEventVO();
createMyObjectEvent(event);
}
public void createMyObjectEvent(MyObjectEventVO vo) throws DataAccessException {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
String updateSql =
"insert into MY_OBJECT_EVENT(COL1, COL2, ...) "
+ "values (?, ?, ...)";
Object[] params = {vo.getCol1(), vo.getCol2(), ... };
int[] types = {Types.INTEGER, Types.VARCHAR, ...};
jdbcTemplate.update(updateSql, params, types);
}
更新 我已经尝试注释掉对 createMyObjectEvent(event) 的调用,以便只发生一次插入。最终结果是一样的:第一个插入没有回滚。所以第一次插入的事务好像有问题
更新 2 我重构了第一个插入也使用 JdbcTemplate 然后一切正常!所以这个问题可以改写为:如何让普通 JDBC 代码在 spring 单元测试中回滚?
@Rollback 需要 spring 托管事务才能工作。 要参与 spring 托管事务,最简单的方法是使用 JDBCTemplate or DataSourceUtils。 两者都包含使用 spring 托管事务所需的代码。