Junit测试在事务后不回滚

Posted

技术标签:

【中文标题】Junit测试在事务后不回滚【英文标题】:Junit test not rolling back after transaction 【发布时间】:2014-05-04 18:19:59 【问题描述】:

我正在尝试对一些持久性代码进行单元测试,但遇到了数据库挂在先前测试中创建的行上的问题。我也在使用 Hibernate Search,虽然这没有反映在我在这里分享的代码中。

我正在使用 Spring 3.1.x、Hibernate 4.x 和 HSQLDB 2.3.2

日志

    DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
    DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
    Hibernate: insert into UserRole (userRoleId, label) values (default, ?)
    DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[nl.project.model.user.UserRole#1]],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
    DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
    Hibernate: insert into UserRole (userRoleId, label) values (default, ?)

    ***First test****

    DEBUG - AbstractPlatformTransactionManager.processRollback(843) | Initiating transaction rollback
    DEBUG - HibernateTransactionManager.doRollback(496) | Rolling back Hibernate transaction on Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[nl.project.model.user.User#1],EntityKey[nl.project.model.user.UserRole#1],EntityKey[nl.project.model.user.UserRole#2], EntityKey[nl.project.model.user.UserRole#3]],collectionKeys=[CollectionKey[nl.project.model.user.User.roles#1]]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
    DEBUG - HibernateTransactionManager.doBegin(342) | Opened new Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
    DEBUG - HibernateTransactionManager.doBegin(352) | Preparing JDBC Connection of Hibernate Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]
    DEBUG - HibernateTransactionManager.doBegin(413) | Exposing Hibernate transaction as JDBC transaction [com.jolbox.bonecp.ConnectionHandle@73d17d67]
    DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
    DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
    DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
    DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
    DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
    DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
    Hibernate: select this_.userRoleId as userRole1_92_0_, this_.label as label2_92_0_ from UserRole this_
    DEBUG - HibernateTransactionManager.doGetTransaction(290) | Found thread-bound Session [SessionImpl(PersistenceContext[entityKeys=[EntityKey[nl.project.model.user.UserRole#3], EntityKey[nl.project.model.user.UserRole#2], EntityKey[nl.project.model.user.UserRole#1]],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] orphanRemovals=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] collectionQueuedOps=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])] for Hibernate transaction
    DEBUG - AbstractPlatformTransactionManager.handleExistingTransaction(470) | Participating in existing transaction
    Hibernate: insert into UserRole (userRoleId, label) values (default, ?)
    WARN - SqlExceptionHelper.logExceptions(144) | SQL Error: -104, SQLState: 23505
    ERROR - SqlExceptionHelper.logExceptions(146) | integrity constraint violation: unique constraint or index violation; UK_O0VIK8LBO8UYMR8WUDN5T21QX table: USERROLE

代码

@Entity
@Immutable
@Table
@NaturalIdCache(region=CacheRegion.NATURAL)
@Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE,region=CacheRegion.USER)
public class UserRole implements Serializable

    public static final String ROLE_ANONYMOUS="ROLE_ANONYMOUS";
    public static final String ROLE_USER="ROLE_USER";
    public static final String ROLE_PROVENDOR="ROLE_PROVENDOR";

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(nullable=false, updatable=false)
    private Long id;

    @Column(nullable=false,unique=true,updatable=false)
    @NaturalId
    private String label;

    ...
    

代码

    public class CoreTest extends TestCase 

        @Inject
        protected SimpleManager<Long> simpleMgr; 

        public void baseSetup()

            simpleMgr.flush();
                            simpleMgr.clear();

                    //after 1st test this contains all created UserRoles
            List roles = simpleMgr.getAll(UserRole.class);

            UserRole role = new UserRole(UserRole.ROLE_ANONYMOUS);
            simpleMgr.save(role);

            role = new UserRole(UserRole.ROLE_USER);
            simpleMgr.save(role);
        
    

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = 
            "classpath:/spring/applicationContext.xml"
            "classpath:/spring/applicationContext-transaction.xml")
    public class MyManagerTest extends CoreTest

        @Before
        public void methodSetup()

            super.baseSetup();

            role = new UserRole(UserRole.ROLE_PROUSER);
            simpleMgr.save(role);

            simpleMgr.save(userMgr.createUser("marc", "marc@gmail.com"));

            simpleMgr.flush();
        

        @Test
        @Transactional
        public void test1()
            ...
        

        @Test
        @Transactional
        public void test2()
            ....
        

事务管理

    <bean id="dataSource" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource" ref="mainDataSource"/>
    </bean>

    <bean id="mainDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
        <property name="driverClass" value="$jdbc.driverClassName"/>
        [...]
    </bean>
    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:hibernate.cfg.xml"/>
        <property name="hibernateProperties">
            <value>
                hibernate.connection.driver_class=$jdbc.driverClassName
                hibernate.jdbc.batch_size=30
                hibernate.dialect=$hibernate.dialect
                hibernate.connection.autoReconnect=true
                hibernate.connection.autoReconnectForPools=true
                hibernate.connection.autocommit=false
            </value>
        </property>
    </bean>
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

【问题讨论】:

尝试在您的测试中添加@Rollback(true) 注释。 谢谢。没有区别 可能是因为你没有清理你的数据? 我还尝试使用 rollback=true 显式指定事务管理。没有帮助,这是 Spring 设置的默认值。日志表明回滚实际上正在发生,它似乎没有传播到数据库 清理是什么意思?回滚表明不需要显式清理? 【参考方案1】:

尝试在测试类上添加@Transactional 注解。 http://docs.spring.io/spring/docs/2.0.x/api/org/springframework/transaction/annotation/Transactional.html

【讨论】:

是的,这没有帮助【参考方案2】:

尝试添加事务拦截器:

<beans xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="
    ...
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

【讨论】:

这里有,我没有在示例中包含这个【参考方案3】:

扩展 AbstractTransactionalJunit4SpringContextTests 或添加 @TestExecutionListeners(TransactionalTestExecutionListener.class)。

【讨论】:

不确定这是否会有所帮助,因为它只是一个方便类,用于似乎已经存在的东西 我以前遇到过一个问题,因为缺少@TestExecutionListeners(TransactionalTestExecutionListener.class)。 顺便说一下,你扩展了TestCase——你用的是什么版本的junit? (在 Junit4 中不需要扩展任何东西)。【参考方案4】:

所以,我终于通过在 BoneCPDatasource 上指定 defaultAutoCommit=false 来完成这项工作。 AFAIK 这对于这种配置来说太低了,因此可能是错误的。但至少它是有效的。

【讨论】:

以上是关于Junit测试在事务后不回滚的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot,测试后事务不回滚

spring hibernate 事务整合 使用测试类 事务不自动提交的问题!!!

Spring集成测试不回滚

junit测试事务回滚时遇到的问题

在集成测试中每个方法不回滚后 Spring Data JPA 数据库更改

JUnit 测试总是回滚事务