java - Spring事务没有正确回滚

标签 java spring transactions

@RunWith(SpringRunner.class)
@CustomTestContext
public class TransactionalTest {

    @Autowired
    private UserRepository userRepository;

    private Log logger = LogFactory.getLog(getClass());

    public void doSomething() {
        try {
            User user = new User();
            user.setUsername("test"); // username has unique constraint
            userRepository.save(user);
        } catch (DataIntegrityViolationException e) {
            logger.info("Wrong");
        }
    }

    @Test
    @Transactional
    public void doTest() {
        doSomething();
        doSomething();
    }

    @Test
    @Transactional
    public void doTest2() {
        doSomething();
        doSomething();
    }
}

doTest在第二次doSomething调用中遇到DataIntegrityViolationException时,第一个doSomething调用没有回滚,导致用户名为“test”的用户仍然存在,并导致doTest2的两次doSomethong调用抛出DataIntegrityViolationException

我期望的是,当事务方法遇到异常时,它将回滚到第一个带有 @Transactional 注释的方法运行之前的状态,在上面的情况下,它根本没有向数据库插入任何新用户,因为默认传播值为 REQUIRE

我将 JpaTransactionManager 日志记录级别设置为 DEBUG 并得到如下控制台:

DEBUG o.s.o.jpa.JpaTransactionManager              - Creating new transaction with name [com.ray.spring.test.TransactionalTest.doTest]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
DEBUG o.s.o.jpa.JpaTransactionManager              - Opened new EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@2ceca2ef]
INFO  o.s.t.c.t.TransactionContext                 - Began transaction (1) for test context [DefaultTestContext@3b42121d testClass = TransactionalTest, testInstance = com.ray.spring.test.TransactionalTest@7a2ab862, testMethod = doTest@TransactionalTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@41fa769c testClass = TransactionalTest, locations = '{file:src/main/resources/applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{file:src/main/resources/application.properties}', propertySourceProperties = '{mysql.Url = jdbc:mysql://localhost:3306/springtest?characterEncoding=UTF-8&serverTimezone=UTC}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@343e225a]; rollback [true]
DEBUG o.s.o.jpa.JpaTransactionManager              - Found thread-bound EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating in existing transaction
DEBUG org.hibernate.SQL                            - insert into User (password, username) values (?, ?)
DEBUG o.s.o.jpa.JpaTransactionManager              - Found thread-bound EntityManager [SessionImpl(PersistenceContext[entityKeys=[EntityKey[com.ray.spring.model.User#1]],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating in existing transaction
DEBUG org.hibernate.SQL                            - insert into User (password, username) values (?, ?)
WARN  o.h.e.j.spi.SqlExceptionHelper               - SQL Error: 1062, SQLState: 23000
ERROR o.h.e.j.spi.SqlExceptionHelper               - Duplicate entry 'test' for key 'UK_jreodf78a7pl5qidfh43axdfb'
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating transaction failed - marking existing transaction as rollback-only
DEBUG o.s.o.jpa.JpaTransactionManager              - Setting JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[EntityKey[com.ray.spring.model.User#1]],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] rollback-only
INFO  c.r.s.test.TransactionalTest                 - Wrong
DEBUG o.s.o.jpa.JpaTransactionManager              - Initiating transaction rollback
DEBUG o.s.o.jpa.JpaTransactionManager              - Rolling back JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[EntityKey[com.ray.spring.model.User#1]],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])]
DEBUG o.s.o.jpa.JpaTransactionManager              - Closing JPA EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] after transaction
INFO  o.s.t.c.t.TransactionContext                 - Rolled back transaction for test context [DefaultTestContext@3b42121d testClass = TransactionalTest, testInstance = com.ray.spring.test.TransactionalTest@7a2ab862, testMethod = doTest@TransactionalTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@41fa769c testClass = TransactionalTest, locations = '{file:src/main/resources/applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{file:src/main/resources/application.properties}', propertySourceProperties = '{mysql.Url = jdbc:mysql://localhost:3306/springtest?characterEncoding=UTF-8&serverTimezone=UTC}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].
// doTest2() below
DEBUG o.s.o.jpa.JpaTransactionManager              - Creating new transaction with name [com.ray.spring.test.TransactionalTest.doTest2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
DEBUG o.s.o.jpa.JpaTransactionManager              - Opened new EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@17229821]
INFO  o.s.t.c.t.TransactionContext                 - Began transaction (1) for test context [DefaultTestContext@3b42121d testClass = TransactionalTest, testInstance = com.ray.spring.test.TransactionalTest@e829999, testMethod = doTest2@TransactionalTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@41fa769c testClass = TransactionalTest, locations = '{file:src/main/resources/applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{file:src/main/resources/application.properties}', propertySourceProperties = '{mysql.Url = jdbc:mysql://localhost:3306/springtest?characterEncoding=UTF-8&serverTimezone=UTC}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@343e225a]; rollback [true]
DEBUG o.s.o.jpa.JpaTransactionManager              - Found thread-bound EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating in existing transaction
DEBUG org.hibernate.SQL                            - insert into User (password, username) values (?, ?)
WARN  o.h.e.j.spi.SqlExceptionHelper               - SQL Error: 1062, SQLState: 23000
ERROR o.h.e.j.spi.SqlExceptionHelper               - Duplicate entry 'test' for key 'UK_jreodf78a7pl5qidfh43axdfb'
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating transaction failed - marking existing transaction as rollback-only
DEBUG o.s.o.jpa.JpaTransactionManager              - Setting JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] rollback-only
INFO  c.r.s.test.TransactionalTest                 - Wrong
DEBUG o.s.o.jpa.JpaTransactionManager              - Found thread-bound EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating in existing transaction
DEBUG org.hibernate.SQL                            - insert into User (password, username) values (?, ?)
WARN  o.h.e.j.spi.SqlExceptionHelper               - SQL Error: 1062, SQLState: 23000
ERROR o.h.e.j.spi.SqlExceptionHelper               - Duplicate entry 'test' for key 'UK_jreodf78a7pl5qidfh43axdfb'
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating transaction failed - marking existing transaction as rollback-only
DEBUG o.s.o.jpa.JpaTransactionManager              - Setting JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] rollback-only
INFO  c.r.s.test.TransactionalTest                 - Wrong
DEBUG o.s.o.jpa.JpaTransactionManager              - Initiating transaction rollback
DEBUG o.s.o.jpa.JpaTransactionManager              - Rolling back JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])]
DEBUG o.s.o.jpa.JpaTransactionManager              - Closing JPA EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] after transaction
INFO  o.s.t.c.t.TransactionContext                 - Rolled back transaction for test context [DefaultTestContext@3b42121d testClass = TransactionalTest, testInstance = com.ray.spring.test.TransactionalTest@e829999, testMethod = doTest2@TransactionalTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@41fa769c testClass = TransactionalTest, locations = '{file:src/main/resources/applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{file:src/main/resources/application.properties}', propertySourceProperties = '{mysql.Url = jdbc:mysql://localhost:3306/springtest?characterEncoding=UTF-8&serverTimezone=UTC}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].

似乎确实回滚了,但不影响第一个 doSomething 调用。

更新:

我编辑代码如下:

@Transactional
public void doSomething() {
    logger.info("First save");
    userRepository.save(new User("test", "test"));
    logger.info("Second save");
    userRepository.save(new User("test", "test"));
}

@Test
@Transactional
public void doTest() {
    doSomething();
}

@Test
@Transactional
public void doTest2() {
    doSomething();
}

我已将 @transactional 添加到 doSomething() 并让异常从 doSomething 抛出,但得到了相同的结果:

DEBUG o.s.o.jpa.JpaTransactionManager              - Creating new transaction with name [com.ray.spring.test.TransactionalTest.doTest]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
DEBUG o.s.o.jpa.JpaTransactionManager              - Opened new EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@42d6c12d]
INFO  o.s.t.c.t.TransactionContext                 - Began transaction (1) for test context [DefaultTestContext@7a2ab862 testClass = TransactionalTest, testInstance = com.ray.spring.test.TransactionalTest@33188612, testMethod = doTest@TransactionalTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@40113163 testClass = TransactionalTest, locations = '{file:src/main/resources/applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{file:src/main/resources/application.properties}', propertySourceProperties = '{mysql.Url = jdbc:mysql://localhost:3306/springtest?characterEncoding=UTF-8&serverTimezone=UTC}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@1a07bf6]; rollback [true]
INFO  c.r.s.test.TransactionalTest                 - First save
DEBUG o.s.o.jpa.JpaTransactionManager              - Found thread-bound EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating in existing transaction
DEBUG org.hibernate.SQL                            - insert into User (password, username) values (?, ?)
INFO  c.r.s.test.TransactionalTest                 - Second save
DEBUG o.s.o.jpa.JpaTransactionManager              - Found thread-bound EntityManager [SessionImpl(PersistenceContext[entityKeys=[EntityKey[com.ray.spring.model.User#1]],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating in existing transaction
DEBUG org.hibernate.SQL                            - insert into User (password, username) values (?, ?)
WARN  o.h.e.j.spi.SqlExceptionHelper               - SQL Error: 1062, SQLState: 23000
ERROR o.h.e.j.spi.SqlExceptionHelper               - Duplicate entry 'test' for key 'UK_jreodf78a7pl5qidfh43axdfb'
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating transaction failed - marking existing transaction as rollback-only
DEBUG o.s.o.jpa.JpaTransactionManager              - Setting JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[EntityKey[com.ray.spring.model.User#1]],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] rollback-only
DEBUG o.s.o.jpa.JpaTransactionManager              - Initiating transaction rollback
DEBUG o.s.o.jpa.JpaTransactionManager              - Rolling back JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[EntityKey[com.ray.spring.model.User#1]],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])]
DEBUG o.s.o.jpa.JpaTransactionManager              - Closing JPA EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] after transaction
INFO  o.s.t.c.t.TransactionContext                 - Rolled back transaction for test context [DefaultTestContext@7a2ab862 testClass = TransactionalTest, testInstance = com.ray.spring.test.TransactionalTest@33188612, testMethod = doTest@TransactionalTest, testException = org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [UK_jreodf78a7pl5qidfh43axdfb]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement, mergedContextConfiguration = [MergedContextConfiguration@40113163 testClass = TransactionalTest, locations = '{file:src/main/resources/applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{file:src/main/resources/application.properties}', propertySourceProperties = '{mysql.Url = jdbc:mysql://localhost:3306/springtest?characterEncoding=UTF-8&serverTimezone=UTC}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].
\\ doTest2() below
DEBUG o.s.o.jpa.JpaTransactionManager              - Creating new transaction with name [com.ray.spring.test.TransactionalTest.doTest2]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
DEBUG o.s.o.jpa.JpaTransactionManager              - Opened new EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@22d9ca63]
INFO  o.s.t.c.t.TransactionContext                 - Began transaction (1) for test context [DefaultTestContext@7a2ab862 testClass = TransactionalTest, testInstance = com.ray.spring.test.TransactionalTest@484149eb, testMethod = doTest2@TransactionalTest, testException = [null], mergedContextConfiguration = [MergedContextConfiguration@40113163 testClass = TransactionalTest, locations = '{file:src/main/resources/applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{file:src/main/resources/application.properties}', propertySourceProperties = '{mysql.Url = jdbc:mysql://localhost:3306/springtest?characterEncoding=UTF-8&serverTimezone=UTC}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]]; transaction manager [org.springframework.orm.jpa.JpaTransactionManager@1a07bf6]; rollback [true]
INFO  c.r.s.test.TransactionalTest                 - First save
DEBUG o.s.o.jpa.JpaTransactionManager              - Found thread-bound EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] for JPA transaction
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating in existing transaction
DEBUG org.hibernate.SQL                            - insert into User (password, username) values (?, ?)
WARN  o.h.e.j.spi.SqlExceptionHelper               - SQL Error: 1062, SQLState: 23000
ERROR o.h.e.j.spi.SqlExceptionHelper               - Duplicate entry 'test' for key 'UK_jreodf78a7pl5qidfh43axdfb'
DEBUG o.s.o.jpa.JpaTransactionManager              - Participating transaction failed - marking existing transaction as rollback-only
DEBUG o.s.o.jpa.JpaTransactionManager              - Setting JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] rollback-only
DEBUG o.s.o.jpa.JpaTransactionManager              - Initiating transaction rollback
DEBUG o.s.o.jpa.JpaTransactionManager              - Rolling back JPA transaction on EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])]
DEBUG o.s.o.jpa.JpaTransactionManager              - Closing JPA EntityManager [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=ExecutableList{size=0} updates=ExecutableList{size=0} deletions=ExecutableList{size=0} orphanRemovals=ExecutableList{size=0} collectionCreations=ExecutableList{size=0} collectionRemovals=ExecutableList{size=0} collectionUpdates=ExecutableList{size=0} collectionQueuedOps=ExecutableList{size=0} unresolvedInsertDependencies=null])] after transaction
INFO  o.s.t.c.t.TransactionContext                 - Rolled back transaction for test context [DefaultTestContext@7a2ab862 testClass = TransactionalTest, testInstance = com.ray.spring.test.TransactionalTest@484149eb, testMethod = doTest2@TransactionalTest, testException = org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [UK_jreodf78a7pl5qidfh43axdfb]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement, mergedContextConfiguration = [MergedContextConfiguration@40113163 testClass = TransactionalTest, locations = '{file:src/main/resources/applicationContext.xml}', classes = '{}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{file:src/main/resources/application.properties}', propertySourceProperties = '{mysql.Url = jdbc:mysql://localhost:3306/springtest?characterEncoding=UTF-8&serverTimezone=UTC}', contextCustomizers = set[[empty]], contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]].

它有行参与事务失败 - 将现有事务标记为仅回滚,但doTest2在第一次userRepository.save()尝试时仍然失败,意味着名称为“test”的用户已经存在。

这是否意味着当同一事务内发生某些异常时,第一个save无法回滚?

CustomTestContxt.java

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@ContextConfiguration("file:src/main/resources/applicationContext.xml")
@TestPropertySource(
        locations = {"file:src/main/resources/application.properties"},
        properties = {"mysql.Url = jdbc:mysql://localhost:3306/springtest?characterEncoding=UTF-8&serverTimezone=UTC"})
public @interface CustomTestContext {
}

更新2:

@Test
@Transactional
@Rollback
public void doTest() {
    entityManager.persist(new User("test", "test"));
}

@Test
@Tranactional
public void doTest2() {
    logger.info(entityManager.find(User.class, 1L));
}

它记录:

INFO  c.r.s.test.TransactionalTest          - User{id=1, username='test', password='test', roles=[]}

最佳答案

doSomething() 正在捕获异常。

只有在@Transactional注解的方法中抛出RuntimeException时,事务才会回滚。

如果您想保留代码原样,则必须在 doSomething() 中手动回滚事务

TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

第二个选项是将 doSomething() 标记为事务性的,然后不捕获事务。那么事务处理必须在测试方法中完成。

关于java - Spring事务没有正确回滚,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48151514/

相关文章:

java - 在 apache Camel 中使用 guice

java - 为什么原型(prototype)作用域与 Stateful bean 一起使用?

java - Spring 3.2 : @WebAppConfiguration breaks existing @Transactional tests

java - 运行另一个 firefox.exe,而不是默认使用 Remote WebDriver 实现的 firefox.exe

java - 如果从 JAR 启动,Jetty + Jersey 嵌入式服务器将无法工作

mysql - Spring 启动: Found shared references to a collection error

java - 非法尝试将代理与两个打开的 session 相关联

spring - 如何对 spring @Transactional 是否存在进行单元测试?

java - 将自由图表插入面板

java - Spring REST 使用 JSON 大写与小写