【问题标题】:Rollback after each unit test每次单元测试后回滚
【发布时间】:2026-01-14 13:00:01
【问题描述】:

我使用 Spring、Hibernate 和 MySQL 开发应用程序。我为我的一张表创建了 DAO。现在,我想测试 DAO 的方法(保存、查找等)。问题是这些方法会影响数据库数据,所以我想在特定方法执行后回滚所有更改。我尝试通过在@TransactionConfiguration 中设置defaultRollback=true 来做到这一点,但它不起作用。下面我粘贴最重要的代码片段。有谁知道如何在每种方法之后强制回滚?

我在 MySQL 中的表正在使用 InnoDB 引擎。 事实上,经过测试执行控制台包含这样的信息: INFO: Rolled back transaction after test execution for test context ... 但已提交数据库中的更改。

UsersDAOTest

@ContextConfiguration(locations={"classpath:applicationContext.xml"})
@RunWith(SpringJUnit4ClassRunner.class)
@TransactionConfiguration(transactionManager="transactionManager", defaultRollback=true)
@Transactional
public class UsersDAOTest {

    @Autowired
    UsersHibernateDAO usersDAO;

    @Test
    public void test1() {
        List<Users> results = usersDAO.findAll();
        Assert.assertEquals(0, results.size());
    }

    @Test
    public void test2() {
        Users user = new Users("mchrobok", "12345678901234567890123456789012");
        usersDAO.saveOrUpdate(user);
        List<Users> results = usersDAO.findAll();
        Assert.assertEquals(1, results.size());     
    }   
}    

休眠(hibernate.cfg.xml)

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>    
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">create-drop</property>

        <mapping class="pl.fp.microblog.domain.Users"/>
    </session-factory>
</hibernate-configuration>

Spring 配置(applicationContext.xml)

<beans>
     <context:component-scan base-package="pl.fp.microblog" />
     <tx:annotation-driven />

     <beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"    destroy-method="close">
         <beans:property name="driverClassName" value="com.mysql.jdbc.Driver"/>
         <beans:property name="url" value="jdbc:mysql://localhost:3306/Microblog"/>
         <beans:property name="username" value="root"/>
         <beans:property name="password" value="root"/>
     </beans:bean>

     <beans:bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
     <beans:property name="dataSource" ref="dataSource" />
         <beans:property name="configLocation">
             <beans:value>
                 hibernate.cfg.xml
         </beans:value>
         </beans:property>
     </beans:bean>

     <beans:bean id="transactionManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
         <beans:property name="sessionFactory" ref="sessionFactory" />
     </beans:bean>

     <beans:bean name="usersDAO" class="pl.fp.microblog.dao.UsersHibernateDAO">
         <beans:property name="sessionFactory" ref="sessionFactory" />
     </beans:bean>
</beans:beans>

编辑

UsersDAO(是通用的,但没关系)

public class GenericHibernateDAO<T> implements GenericDAO<T> {
    private SessionFactory sessionFactory;
    private Class<?> persistClass;

    public GenericHibernateDAO() {
        ParameterizedType type = ((ParameterizedType)getClass().getGenericSuperclass());
        persistClass = ((Class<?>) type.getActualTypeArguments()[0]);
    }

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    @Override
    public List<T> findAll() {
        Criteria criteria = sessionFactory.openSession().createCriteria(persistClass);
        return criteria.list();
    }

    @Override
    public void saveOrUpdate(T entity) {
        sessionFactory.openSession().saveOrUpdate(entity);
    }
}

【问题讨论】:

  • 这显然是一种错误的测试方式。您不应该在 PRODUCTION 环境中测试更改然后将它们回滚,而应使用 DEV、TEST 等较低的环境。与数据库和所有其他系统层
  • 这个数据库是我的测试环境。但我的问题不同。如果在每种方法之后提交更改,它会对其他方法产生影响(据我所知,订单执行是随机的)。这就是我想在每种方法之后回滚的原因。
  • 如果是你的测试环境,你为什么要回滚呢?如果您想恢复到初始状态,在运行测试之前拍摄快照然后在完成后重新应用它不是更简单吗?
  • defaultRollback 默认为 true
  • @foampile 我在乎,因为执行顺序是随机的。 Test1() 读取所有用户,结果应该为 0。但如果 test2() 将在 test1() 之前运行,那么 test1() 将失败,因为所有更改都已提交。

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


【解决方案1】:

您正在休眠代码中打开一个新会话,您应该改用 getCurrentSession()。

您的代码中的问题是您的 DAO 类试图自己管理事务。在现代 Spring 应用程序中,事务管理应该在服务层中执行。

所以这意味着通常你的服务方法应该用@Transactional注释。

【讨论】: