Spring JDBC测试中的事务回滚

14

我试图在使用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>

我做错了什么?

此外,我的注释是否太多了? 我能使它更简洁一些吗?

8个回答

20
如果您不使用@TestExecutionListeners注释来明确配置测试执行监听器,在默认情况下,Spring会配置DependencyInjectionTestExecutionListenerDirtiesContextTestExecutionListenerTransactionalTestExecutionListenerTransactionalTestExecutionListener提供具有默认回滚语义的事务性测试执行。 通过在测试类上显式声明@TestExecutionListeners并从侦听器列表中省略TransactionalTestExecutionListener,您可以禁用事务支持。
您还必须在类或方法级别上添加@Transactional注释。
您还必须使用DataSourceUtils来获取由DataSourceTransactionManager管理的事务性连接。

1
我移除了@TestExecutionListeners并添加了@Transactional。然后,我必须在应用程序上下文中添加一个transactionManager bean(DataSourceTransactionManager)。事务没有回滚,所以我添加了@TransactionConfiguration(defaultRollback=true)。事务仍然没有回滚,所以我在测试方法中添加了@Rollback。只有这样,我才看到Spring日志显示它正在回滚事务,但更新仍然被持久化到数据库中。可能与SQL Server及其驱动程序有关吗? - Synesso
1
将DataSourceUtils添加到我的答案中。 - Chin Huang
啊,我没有收到你的最后一条评论通知。与此同时,我已经改变了我的上下文,将数据源包装在TransactionAwareDataSourceProxy中。那个方法行得通。所以,这里有两种做同样事情的方法。现在看看我能够去掉哪些7个注释并且仍然让它正常工作。 - Synesso

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

}


2

添加此注释,测试用例中将不会回滚:

 @TransactionConfiguration(defaultRollback=false)

我的注释看起来像这样:

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

1

可能是因为测试方法没有 @Transactional 注解吗?


1
我添加了 @Transactional,但没有任何效果。我还添加了 @Rollback,但仍然没有影响。 - Synesso

0
在Spring中使用@Transactional注解时,您必须将以下行添加到Spring配置文件中:
<tx:annotation-driven transaction-manager="transactionManager"/>

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

IBM 网站上的源


1
我记不得问题实际上是什么了,但感谢提供额外的信息。 - Synesso

0

附加信息:

针对此行:

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

transaction-manager属性的默认值为"transactionManager"。 只有当transactionManager的bean id/name不是'transactionManager'时,才需要设置此属性。 因此,您只需要设置:

<tx:annotation-driven />

0
你需要在类级别上添加 @Transactional。就像这样:
@TransactionConfiguration(transactionManager = "txManager",defaultRollback = true)
@Transactional

这里的txManager是来自应用上下文的事务管理器实例或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" />

0

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

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


    @Test
    public void testSaveRecords()
    { ... }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接