2017-05-05 47 views
1

對於插入兩個不同表中的DAO方法,我有一個彈簧單元測試。測試完成後,其中一個插入按預期回滾,但不是另一個!我真的無法弄清楚發生了什麼。我已經多次調試了這個測試,以便我可以看到數據庫中何時出現(未提交)更改,但只有一個消失。在Spring單元測試中未回滾的Vanilla JDBC更新

我能看到的唯一區別是在一種情況下插入是用原始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); 
    } 
} 

我測試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,然後一切工作正常!所以這個問題可以重新表述爲:如何讓vanilla JDBC代碼在春季單元測試中回滾?

+0

是否有可能createMyObjectEvent有一個自己的事務?例如。這是myDao註釋嗎? –

+0

MyDao上沒有註釋。單元測試之外的所有配置都來自XML。 – inovaovao

+0

爲什麼在第二種方法中使用新的JdbcTemplate? – Nemesis

回答

1

@Rollback需要春季託管交易才能正常工作。 參加春季託管交易最簡單的方法是使用JDBCTemplateDataSourceUtils。 兩者均包含使用彈簧管理事務所需的代碼。

+0

你是對的!除非使用DataSourceUtils.getConnection(dataSource)創建它,否則原始jdbc連接不參與事務。這就是JdbcTemplate內部使用的內容。另見http://stackoverflow.com/questions/4048340/transaction-rollback-on-spring-jdbc-tests – inovaovao