Description
Will Darby opened SPR-14457 and commented
I am porting from Spring 3.2+Hibernate 4.1 to Spring 4.3+Hibernate 5.2. There appears to be an issue with Aspect ordering between aspects detected using annotations and those added manually with Advised.addAdvisor(). More specifically, advisors added with Advised.addAdvisor() are added after those discovered using annotations unless 'beforeExistingAdvisors' is added, in which case it is before all others.
The problem arises when using annotation-based transactions, i.e.,
<tx:annotation-driven transaction-manager="transactionManager" order="10"/>
in combination with org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor.
The PersistenceExceptionTranslationPostProcessor adds the Aspect using Advised.addAdvisor() after the BeanFactoryTransactionAttributeSourceAdvisor. The result is that when the a transaction completes and flushes cached operations to the database, exceptions that arise are not processed by the PersistenceExceptionTranslator since it has already completed. The result is that Hibernate exceptions are not translated into Spring exceptions for database operations that occur during commit.
This was working correctly in the previous versions (Spring 3.2+Hibernate 4.1). I have tried adding 'order' to the PersistenceExceptionTranslationPostProcessor, but it is has no effect.
Below are two stack traces illustrating the issue.
The first shows that the PersistenceExceptionTranslator is invoked within the TransactionInterceptor:
PersistenceExceptionTranslationInterceptor.invoke(MethodInvocation) line: 136 ReflectiveMethodInvocation.proceed() line: 179 TransactionInterceptor$1.proceedWithInvocation() line: 99 TransactionInterceptor(TransactionAspectSupport).invokeWithinTransaction(Method, Class, InvocationCallback) line: 281
TransactionInterceptor.invoke(MethodInvocation) line: 96
ReflectiveMethodInvocation.proceed() line: 179
AspectJAfterThrowingAdvice.invoke(MethodInvocation) line: 62
ReflectiveMethodInvocation.proceed() line: 168
ExposeInvocationInterceptor.invoke(MethodInvocation) line: 92 ReflectiveMethodInvocation.proceed() line: 179
JdkDynamicAopProxy.invoke(Object, Method, Object[]) line: 213 $Proxy48.storeAnalysisRule(String, AnalysisRule) line: not available
AnalysisRuleTest.duplicateSaveError() line: 71
The second shows that the exception is occurring during the flush() operation, after the PersistenceExceptionTranslator has completed:
Stacktrace was: javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not execute batch at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:147) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:155) at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:162) at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1403) at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:473) at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3133) at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2370) at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:467) at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:146) at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.access$100(JdbcResourceLocalTransactionCoordinatorImpl.java:38) at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:220) at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:68) at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:581) at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:761) at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:485) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:291) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:168) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy48.storeAnalysisRule(Unknown Source) at test.com.leeriver.trading.account.AnalysisRuleTest.duplicateSaveError(AnalysisRuleTest.java:71) Caused by: org.hibernate.exception.ConstraintViolationException: could not execute batch at org.hibernate.exception.internal.SQLStateConversionDelegate.convert(SQLStateConversionDelegate.java:112) at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111) at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:119) at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.doExecuteBatch(BatchingBatch.java:97) at org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl.execute(AbstractBatchImpl.java:147) at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.executeBatch(JdbcCoordinatorImpl.java:206) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:611) at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456) at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1397) ... 53 more Caused by: java.sql.BatchUpdateException: integrity constraint violation: unique constraint or index violation; USER_RULE_IDX table: USERANALYSISRULE at org.hsqldb.jdbc.JDBCPreparedStatement.executeBatch(Unknown Source) at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297) at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297) at org.apache.commons.dbcp.DelegatingStatement.executeBatch(DelegatingStatement.java:297) at org.hibernate.engine.jdbc.batch.internal.BatchingBatch.performExecution(BatchingBatch.java:110) ... 61 more
Affects: 4.3.1
Issue Links:
- Hibernate5 LocalSessionFactoryBean does not translate javax.persistence exceptions [SPR-14455] #19024 Hibernate5 LocalSessionFactoryBean does not translate javax.persistence exceptions
- Remove outdated JpaExceptionTranslatorAspect [SPR-15047] #19613 Remove outdated JpaExceptionTranslatorAspect