【问题标题】:Prevent inner transaction from rolling back outer transaction防止内部事务回滚外部事务
【发布时间】:2014-07-17 05:57:34
【问题描述】:

我有一个有点复杂的事务,它创建或不创建许多不同类型的实体。如果由于验证错误而无法创建其中一个实体,我想简单地捕获 ConstraintValidationError 并继续(允许在同一事务中创建的任何其他实体成功保存)。

我可以成功执行此操作,但验证错误仍将我的整个事务标记为回滚,这是我不想要的。

我尝试更改导致 ConstraintValidationError 的单一验证方法:

@Transactional(propagation = Propagation.NOT_SUPPORTED)

强制一个新事务,认为它只会标记那个新的内部事务以进行回滚,但事实并非如此。

调用类标有:

@Transactional(noRollbackFor=ConstraintViolationException.class)

但是如果 ConstraintViolationException 在被调用的方法中并且我不希望将其添加到被调用的方法中,这将无济于事。

这样做的正确方法是什么?

(如果需要代码,请告诉我——这个例子有点复杂。)

编辑(2014 年 5 月 26 日):

当我尝试时

progration = Propagation.REQUIRES_NEW

我发现结果没有变化。此特定日志来自一个测试,该测试故意尝试保存具有空描述的“部门”实体对象,这将是无效的:

11:46:43.599 [main] DEBUG org.springframework.transaction.annotation.AnnotationTransactionAttributeSource - Adding transactional method 'DefaultCourseManager.saveDepartment' with attribute: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT; ''
11:46:43.599 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
11:46:43.599 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@3a90c13c] for JPA transaction
11:46:43.599 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Suspending current transaction, creating new transaction with name [edu.ucdavis.dss.dw.site.DefaultCourseManager.saveDepartment]
11:46:43.600 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Opened new EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@67b355c8] for JPA transaction
11:46:43.600 [main] DEBUG org.hibernate.engine.transaction.spi.AbstractTransactionImpl - begin
11:46:43.600 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Obtaining JDBC connection
11:46:43.601 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Obtained JDBC connection
11:46:43.601 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - initial autocommit status: true
11:46:43.601 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - disabling autocommit
11:46:43.601 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@388623ad]
11:46:43.601 [main] DEBUG org.springframework.data.repository.core.support.TransactionalRepositoryProxyPostProcessor$CustomAnnotationTransactionAttributeSource - Adding transactional method 'save' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
11:46:43.602 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'transactionManager'
11:46:43.602 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@67b355c8] for JPA transaction
11:46:43.602 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Participating in existing transaction
11:46:43.606 [main] DEBUG org.hibernate.SQL - select KeyValue from SurrogateKeys where TableName = 'Departments' for update
11:46:43.608 [main] DEBUG org.hibernate.SQL - insert into SurrogateKeys(TableName, KeyValue) values('Departments', ?)
11:46:43.610 [main] DEBUG org.hibernate.SQL - update SurrogateKeys set KeyValue = ? where KeyValue = ? and TableName = 'Departments'
11:46:43.612 [main] DEBUG org.hibernate.SQL - select KeyValue from SurrogateKeys where TableName = 'Departments' for update
11:46:43.612 [main] DEBUG org.hibernate.SQL - update SurrogateKeys set KeyValue = ? where KeyValue = ? and TableName = 'Departments'
11:46:43.613 [main] DEBUG org.hibernate.event.internal.AbstractSaveEventListener - Generated identifier: 1, using strategy: org.hibernate.id.MultipleHiLoPerTableGenerator
11:46:43.613 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Initiating transaction commit
11:46:43.613 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@67b355c8]
11:46:43.613 [main] DEBUG org.hibernate.engine.transaction.spi.AbstractTransactionImpl - committing
11:46:43.613 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Processing flush-time cascades
11:46:43.614 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Dirty checking collections
11:46:43.614 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects
11:46:43.614 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
11:46:43.614 [main] DEBUG org.hibernate.internal.util.EntityPrinter - Listing entities:
11:46:43.614 [main] DEBUG org.hibernate.internal.util.EntityPrinter - edu.ucdavis.dss.dw.entities.Department{courses=null, code=null, name=null, id=1}
11:46:43.620 [main] DEBUG org.hibernate.validator.resourceloading.PlatformResourceBundleLocator - ValidationMessages not found.
11:46:43.624 [main] DEBUG org.hibernate.validator.resourceloading.PlatformResourceBundleLocator - org.hibernate.validator.ValidationMessages found.
11:46:43.637 [main] DEBUG org.hibernate.engine.transaction.spi.AbstractTransactionImpl - rolling back
11:46:43.638 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - rolled JDBC Connection
11:46:43.638 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - re-enabling autocommit
11:46:43.639 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@67b355c8] after transaction
11:46:43.640 [main] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
11:46:43.648 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Releasing JDBC connection
11:46:43.649 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Released JDBC connection
11:46:43.649 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Resuming suspended transaction after completion of inner transaction
11:46:43.651 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Initiating transaction rollback
11:46:43.651 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Rolling back JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@3a90c13c]
11:46:43.651 [main] DEBUG org.hibernate.engine.transaction.spi.AbstractTransactionImpl - rolling back
11:46:43.651 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - rolled JDBC Connection
11:46:43.651 [main] DEBUG org.hibernate.engine.transaction.internal.jdbc.JdbcTransaction - re-enabling autocommit
11:46:43.652 [main] DEBUG org.springframework.orm.jpa.JpaTransactionManager - Closing JPA EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@3a90c13c] after transaction
11:46:43.652 [main] DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Closing JPA EntityManager
11:46:43.652 [main] DEBUG org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl - HHH000420: Closing un-released batch
11:46:43.652 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Releasing JDBC connection
11:46:43.653 [main] DEBUG org.hibernate.engine.jdbc.internal.LogicalConnectionImpl - Released JDBC connection
11:46:43.655 [main] DEBUG com.github.springtestdbunit.DbUnitTestExecutionListener - Skipping @DatabaseTest expectation due to test exception class org.springframework.transaction.TransactionSystemException
11:46:43.656 [main] DEBUG org.dbunit.database.DatabaseDataSourceConnection - close() - start
11:46:43.657 [main] DEBUG org.springframework.test.context.support.DirtiesContextTestExecutionListener - After test method: context [DefaultTestContext@1da2cb77 testClass = BannerManagerTestCase, testInstance = edu.ucdavis.dss.dw.site.BannerManagerTestCase@48f278eb, testMethod = testCatchesNullDepartment@BannerManagerTestCase, testException = org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction, mergedContextConfiguration = [MergedContextConfiguration@2f217633 testClass = BannerManagerTestCase, locations = '{}', classes = '{class edu.ucdavis.dss.dw.config.TestContextConfiguration}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], class dirties context [false], class mode [null], method dirties context [false].
11:46:43.675 [main] DEBUG org.springframework.test.context.support.DirtiesContextTestExecutionListener - After test class: context [DefaultTestContext@1da2cb77 testClass = BannerManagerTestCase, testInstance = [null], testMethod = [null], testException = [null], mergedContextConfiguration = [MergedContextConfiguration@2f217633 testClass = BannerManagerTestCase, locations = '{}', classes = '{class edu.ucdavis.dss.dw.config.TestContextConfiguration}', contextInitializerClasses = '[]', activeProfiles = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], dirtiesContext [false].
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 7.299 sec <<< FAILURE!

Results :

Tests in error: 
  testCatchesNullDepartment(edu.ucdavis.dss.dw.site.BannerManagerTestCase): Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction

如果我不确定我的阅读是否正确——我看到空值部门实体,然后看到它正在回滚的信息,但人们会认为它会在某处表明它决定回滚的原因.

【问题讨论】:

    标签: java hibernate transactions spring-data


    【解决方案1】:

    您可以在新事务中运行有问题的方法。你应该使用Propagation.REQUIRES_NEW,而不是@Transactional(propagation = Propagation.NOT_SUPPORTED)

    @Transactional(propagation = Propagation.REQUIRES_NEW)

    强制创建新事务。 NOT_SUPPORTED 不会导致创建新事务,它只会暂停当前事务。

    将对方法的调用包装在 try/catch 块中。我不确定您在春季环境中会遇到什么异常。在 Java EE 中,它将是 EJBException

    【讨论】:

    • 这似乎并没有改变错误。我已将其设置为 REQUIRES_NEW 并更新了原始问题以包含日志。
    • 你是对的,我在同一个事务中有一个额外的存储库调用,它改变了结果。
    • 酷,很高兴我能帮上忙。
    • 这看起来很完美,但请记住外部事务(失败时)不会回滚内部事务中所做的更改。
    • 是的,我还没有处理相反的情况。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 2020-04-12
    • 2019-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-10
    相关资源
    最近更新 更多