【问题标题】:Transaction rollback on Spring JDBC testsSpring JDBC 测试的事务回滚
【发布时间】:2010-10-29 01:20:01
【问题描述】:

我试图在使用 Spring-test 时回滚 JDBC 事务但没有成功。当我运行以下 SQL 更新总是提交。

package my.dao.impl;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionConfiguration;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Statement;

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class})
@ContextConfiguration(locations={"classpath:ApplicationContext-test-DAOs.xml"})
@TransactionConfiguration(defaultRollback = true)
public class ConfirmationMatchingDAOImplTest {

    @Autowired
    private DataSource dataSource;

    @Test
    public void shouldInsertSomething() throws Exception {
        final Connection connection = dataSource.getConnection();
        final Statement statement = connection.createStatement();
        statement.executeUpdate("insert into TEST_INSERT values (1, 'hello')");
        statement.close();
        connection.close();
    }
}

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"/>
    <property name="url" value="jdbc:sqlserver://makeitfunky:1490;databaseName=fonzie"/>
    <property name="username" value="ralph"/>
    <property name="password" value="p0n1es_R_kew1"/>
</bean>

我做错了什么?

另外,我是否使用了太多注释?我可以让它更干净一点吗?

【问题讨论】:

    标签: spring testing jdbc transactions rollback


    【解决方案1】:

    如果你没有使用@TestExecutionListeners注解显式配置测试执行监听器,Spring默认配置DependencyInjectionTestExecutionListener、DirtiesContextTestExecutionListener和TransactionalTestExecutionListener。 TransactionalTestExecutionListener 提供具有默认回滚语义的事务测试执行。通过在测试类中显式声明 @TestExecutionListeners 并从侦听器列表中省略 TransactionalTestExecutionListener,您将禁用事务支持。

    您还必须在类或方法级别添加@Transactional 注解。

    您还必须使用DataSourceUtils 来获取由 DataSourceTransactionManager 管理的事务连接。

    【讨论】:

    • 我删除了@TestExecutionListeners 并添加了@Transactional。然后我必须将一个 transactionManager bean 添加到应用程序上下文 (DataSourceTransactionManager)。 Txn 没有回滚,所以我添加了@TransactionConfiguration(defaultRollback=true)。 Txn 仍然没有回滚,所以我在测试方法中添加了@Rollback。直到那时我才看到 Spring 日志它正在回滚 txn,但更新仍然被持久化到数据库中。可能与 SQL Server 及其驱动程序有关?
    • 在我的回答中添加了 DataSourceUtils。
    • 啊,我没有收到您最后评论的通知。同时,我更改了上下文以将数据源包装在 TransactionAwareDataSourceProxy 中。那行得通。所以你去,两种方法来做同样的事情。现在看看我可以摆脱这 7 个注释中的哪一个并且仍然可以工作。
    【解决方案2】:

    如果您使用的是非 xml 方法,这从 3.1 版开始就可以很好地工作

    @Transactional
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {TestDbConfig.class, SomeService.class})
    public class SomeTest {
    
    @Inject
    private SomeService someService;
    
    @PersistenceContext
    private EntityManager em;
    
    @Test
    public void someTest() {}
    

    然后测试配置采用这种形式。注意@EnableTransactionManagement 以及您可以声明全局测试 defaultRollback 的事实。这在大型项目中变得特别有用。

    @Configuration
    @PropertySource(value = "classpath:app.properties")
    @EnableTransactionManagement
    @TransactionConfiguration(defaultRollback = true)
    public class TestDbConfig {
    
    //read the parameters from properties
    @Value("${hibernate.dialect:unset}")
    private String hibernateDialect;
    
    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
        return new PropertySourcesPlaceholderConfigurer();
    }
    
    @Bean
    public PlatformTransactionManager transactionManager() {
        //for example
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactoryBean().getObject());
        return transactionManager;
    }
    
    @Bean
    LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {
        //set the datasource
        //set the JpaVendorAdapter
        //set the packagesToScan
        return some sort of LocalContainerEntityManagerFactoryBean;
    }
    
    @Bean
    DataSource dataSource() {
        return dataSource from jndi or a DriverManagerDataSource();
    }
    

    }

    【讨论】:

      【解决方案3】:

      添加此注解,测试用例不会回滚:

       @TransactionConfiguration(defaultRollback=false)
      

      我的注释是这样的:

      @RunWith(SpringJUnit4ClassRunner.class)
      @ContextConfiguration(locations = { "/../applicationContext.xml" })
      @TransactionConfiguration(defaultRollback=true)
      public class DBCreatorTest {
      

      【讨论】:

        【解决方案4】:

        可能是因为您没有用于测试方法的@Transactional?

        【讨论】:

        • 我添加了@Transactional,但没有效果。我还添加了@Rollback,它仍然没有效果。
        【解决方案5】:

        在 Spring 中使用 @Transactional 注解时,必须在 Spring 配置文件中添加以下行:

        <tx:annotation-driven transaction-manager="transactionManager"/>
        

        transaction-manager 属性包含对 Spring 配置文件中定义的事务管理器 bean 的引用。这段代码告诉 Spring 在应用事务拦截器时使用 @Transaction 注解。没有它,@Transactional 注释将被忽略,导致您的代码中没有使用任何事务。

        Source on IBM website

        【讨论】:

        • 我不记得问题到底出在哪里,但感谢您提供的额外信息。
        【解决方案6】:

        附加信息:

        对于这一行

        <tx:annotation-driven transaction-manager="transactionManager"/>
        

        transaction-manager 属性的默认值为“transactionManager”。 仅当 transactionManager 的 bean id/name 不是“transactionManager”时才需要此属性。 所以你只需要设置:

        <tx:annotation-driven />
        

        【讨论】:

          【解决方案7】:

          您需要在班级级别添加@Transactional。 像这样的:

          @TransactionConfiguration(transactionManager = "txManager",defaultRollback = true)
          @Transactional
          

          这里txManager 是来自application context 的事务管理器的实例或bean id。

          <!-- Transaction Manager -->
              <bean id="txManager"
                    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
                  <property name="sessionFactory" ref="sessionFactory" />
              </bean>
          
              <tx:annotation-driven transaction-manager="txManager" />
          

          【讨论】:

            【解决方案8】:

            在尝试了很多上述组合之后,以下设置对我有用,并且在测试成功后完全回滚。

            @RunWith(SpringJUnit4ClassRunner.class)
            @ContextConfiguration(locations = {"classpath:datasource-context-test.xml"})
            @TransactionConfiguration(defaultRollback = true)
            @Transactional
            public class DataAccessTest 
            {
            
            
                @Test
                public void testSaveRecords()
                { ... }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2011-10-04
              • 1970-01-01
              • 2016-10-07
              • 2017-11-07
              • 1970-01-01
              • 2015-06-19
              • 2018-03-25
              • 1970-01-01
              相关资源
              最近更新 更多