【问题标题】:Vanilla JDBC update not rolled back in spring unit test原始 JDBC 更新未在春季单元测试中回滚
【发布时间】:2017-05-05 09:07:10
【问题描述】:

我有一个用于插入两个不同表的 DAO 方法的弹簧单元测试。测试完成后,其中一个插入按预期回滚,但不是另一个!我真的无法弄清楚发生了什么。我已经调试了几次测试,以便我可以看到(未提交的)更改何时出现在数据库中,但只有一个消失了。

我能看到的唯一区别是在一种情况下插入是使用原始 JDBC 完成的,而在第二种情况下是使用 Sping 的 JdbcTemplate 完成的。但是两者不应该在同一个事务中然后回滚吗?

这是我的测试类:

@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);
    }
}

我的 test-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 单元测试中回滚?

【问题讨论】:

  • createMyObjectEvent 是否有可能有自己的事务?例如。 myDao 是这样注释的吗?
  • MyDao 上没有注释。单元测试之外的所有配置都来自 XML。
  • 第二种方法为什么要用新的JdbcTemplate?
  • 因为很久以前的代码就是这样写的……

标签: java spring unit-testing junit spring-test


【解决方案1】:

@Rollback 需要 Spring 托管事务才能工作。 参与 Spring 托管事务最简单的方法是使用JDBCTemplate 或DataSourceUtils。 两者都包含使用 spring 托管事务所需的代码。

【讨论】:

  • 你是对的!原始 jdbc 连接不参与事务,除非我使用 DataSourceUtils.getConnection(dataSource) 创建它。这就是 JdbcTemplate 内部使用的内容。另见stackoverflow.com/questions/4048340/…
猜你喜欢
  • 1970-01-01
  • 2012-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-13
  • 2021-08-31
  • 1970-01-01
相关资源
最近更新 更多