简体   繁体   English

防止内部事务回滚外部事务

[英]Prevent inner transaction from rolling back outer transaction

I have a somewhat complex transaction which does or does not create many different types of entities. 我有一个比较复杂的事务,它创建或不创建许多不同类型的实体。 Should it fail to create one of those entities due to a validation error, I'd like to simply catch the ConstraintValidationError and move on (allowing any other entities created in the same transaction to save successfully). 如果由于验证错误而无法创建这些实体之一,我想简单地捕获ConstraintValidationError并继续操作(允许在同一事务中创建的任何其他实体成功保存)。

I can do this successfully but the validation error still marks my whole transaction for rollback, which I do not want. 我可以成功完成此操作,但是验证错误仍然将我的整个事务标记为回退,这是我不希望的。

I tried changing the single validating method which causes the ConstraintValidationError to have a: 我尝试更改导致ConstraintValidationError具有以下内容的单个验证方法:

@Transactional(propagation = Propagation.NOT_SUPPORTED)

to force a new transaction, thinking it would mark only that new, inner transaction for rollback, but that doesn't seem to be the case. 强制执行新事务,认为这只会将新的内部事务标记为回滚,但事实并非如此。

The calling class is marked with a: 调用类标记有:

@Transactional(noRollbackFor=ConstraintViolationException.class)

but this doesn't help if the ConstraintViolationException is in the called method and I do not wish to add it to the called method. 但这如果ConstraintViolationException在被调用的方法中并且我不希望将其添加到被调用的方法中,则无济于事。

What's the proper way to do something like this? 做这样的事情的正确方法是什么?

(Let me know if code is required -- the example is somewhat complex.) (让我知道是否需要代码-该示例有些复杂。)

EDIT (5/26/14): 编辑(5/26/14):

When I try 当我尝试

progration = Propagation.REQUIRES_NEW

I see no change in the result. 我看不出结果有任何变化。 This particular log is from a test purposefully trying to save a 'department' entity object with a null description, which would be invalid: 这个特定的日志来自一个测试,该测试有意地尝试使用空描述保存“部门”实体对象,这将是无效的:

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

If I'm not sure if I'm reading that correctly -- I see the null-valued department entity and then the information that it is rolling back but one would think it would indicate somewhere as to why it decided to rollback. 如果不确定我是否正确阅读了-我看到空值部门实体,然后看到它正在回滚的信息,但有人会认为这表明它决定回滚的原因。

You can run the problematic method in a new transaction. 您可以在新事务中运行有问题的方法。 Instead of @Transactional(propagation = Propagation.NOT_SUPPORTED) , you should use Propagation.REQUIRES_NEW : 而不是@Transactional(propagation = Propagation.NOT_SUPPORTED) ,您应该使用Propagation.REQUIRES_NEW

@Transactional(propagation = Propagation.REQUIRES_NEW)

to force the creation of a new transaction. 强制创建新交易。 NOT_SUPPORTED does not lead to the creation of a new transaction, it only suspends the current one. NOT_SUPPORTED不会导致创建新事务,它只会挂起当前事务。

Wrap the call to the method in a try/catch block. 将对方法的调用包装在try / catch块中。 I am not sure what Exception you are going to get in a spring environment. 我不确定在春季环境中会遇到什么异常。 In Java EE, it would be EJBException . 在Java EE中,它将是EJBException

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM