【问题标题】:Custom Exception translator not invoked in Hibernate/Spring在 Hibernate/Spring 中未调用自定义异常转换器
【发布时间】:2018-01-22 13:40:52
【问题描述】:

我编写了代码来处理特定的 Oracle 错误代码。我扩展了 LocalSessionFactoryBean 以实现会话工厂创建的附加逻辑。

class MyLocalSessionFactoryBean extends LocalSessionFactoryBean {

    @Override
    public DataAccessException translateExceptionIfPossible(RuntimeException ex)
    {
        if (ex instanceof HibernateJdbcException)
        {
            // Oracle serialization exception has SQL state [72000]; error code [8177]
            SQLException sqle = ((HibernateJdbcException) ex).getSQLException();
            if (equal(sqle.getSQLState(), "72000") && sqle.getErrorCode() == 8177)
                return new CannotSerializeTransactionException("Cannot serialize transaction access", ex);
        }

        return super.translateExceptionIfPossible(ex);
    }


}

它在 XML 中被声明为主要的 SessionFactory bean 并按其工作。

我已经阅读了this question,但不同的是我使用的是 SessionFactory 而不是 EntityManager。我认为我的设置很好,可以利用持久性异常翻译器。另外,我的 DAO 带有注释 @Repository

不幸的是,每当我从 Oracle 获得事务序列化并且仅从 Oracle 获得时,我都会通过所有堆栈跟踪传播它。我可以调试并且永远不会调用 LSFB 的 translateExceptionIfPossible 方法。

Exception in thread "processRunManager-dequeue" org.springframework.orm.hibernate5.HibernateJdbcException: JDBC exception on Hibernate data access: SQLException for SQL [n/a]; SQL state [72000]; error code [8177]; could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement
                at org.springframework.orm.hibernate5.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:248)
                at org.springframework.orm.hibernate5.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:755)
                at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:590)
                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:518)
                at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
                at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
                at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
                at org.springframework.retry.interceptor.RetryOperationsInterceptor$1.doWithRetry(RetryOperationsInterceptor.java:91)
                at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:287)
                at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:164)
                at org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:118)
                at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:153)
                at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
                at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
                at com.sun.proxy.$Proxy272.pick(Unknown Source)
                at sun.reflect.GeneratedMethodAccessor1806.invoke(Unknown Source)
                at sun.reflect.DelegatingMethodAccessorImpl.__invoke(DelegatingMethodAccessorImpl.java:43)
                at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java)
                at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java)
                at java.lang.reflect.Method.invoke(Method.java:606)
                at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
                at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
                at com.sun.proxy.$Proxy273.pick(Unknown Source)
                at com.acme.Dequeuer.run(ProcessEngineImpl.java:298)
                at java.lang.Thread.run(Thread.java:745)
Caused by: org.hibernate.exception.GenericJDBCException: could not execute statement
                at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:47)
                at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:111)
                at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:97)
                at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:207)
                at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:45)
                at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3184)
                at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3063)
                at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3443)
                at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:145)
                at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:589)
                at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:463)
                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:1295)
                at org.hibernate.internal.SessionImpl.managedFlush(SessionImpl.java:468)
                at org.hibernate.internal.SessionImpl.flushBeforeTransactionCompletion(SessionImpl.java:3159)
                at org.hibernate.internal.SessionImpl.beforeTransactionCompletion(SessionImpl.java:2352)
                at org.hibernate.engine.jdbc.internal.JdbcCoordinatorImpl.beforeTransactionCompletion(JdbcCoordinatorImpl.java:491)
                at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl.beforeCompletionCallback(JdbcResourceLocalTransactionCoordinatorImpl.java:147)
                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:231)
                at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:65)
                at org.springframework.orm.hibernate5.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:582)
                ... 24 more
Caused by: java.sql.SQLException: ORA-08177: impossibile serializzare l'accesso per questa transazione

                at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:447)
                at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
                at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:951)
                at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:513)
                at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:227)
                at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)
                at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:208)
                at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1046)
                at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1336)
                at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3613)
                at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3694)
                at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1354)
                at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeUpdate(NewProxyPreparedStatement.java:384)
                at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
                ... 43 more

可以预见,异常发生在@Transactional 的提交阶段。 JDBC抛出异常到Hibernate是对的,但是应该由Spring使用我的代码翻译,这样Spring@Retry可以再次重放事务。

如何让我的自定义异常翻译器正常工作?

【问题讨论】:

  • 我确认我的代码从未被调用,因为我的断点没有触发,而不是因为异常没有按预期翻译

标签: spring hibernate


【解决方案1】:

找到了罪魁祸首。

此类异常仅在提交阶段引发。我的 DAO 被注解为 @Repository,但不是 @Transactional,因此任何操作都将通过持有 exception translator interceptor 的 DAO 的 @Repository 注解而没有问题。但是,当PlatformTransactionManager 尝试提交时,它没有被PersistenceExceptionTranslationAdvisor 正确修饰,如堆栈跟踪所示。

HibernateTransactionManager 只翻译 HibernateExceptions 和 PersistenceExceptions。

正如我在问题中所说,我确定 Hibernate 不会将带有这些特定 Oracle 错误代码的 SQLException 转换为 HibernateException 的正确子类。

解决方案 1 是使用带有 @Repository 的 DAO 或解决方案 2(我们的方法)来装饰服务,以使用 pessmistic row locks 处理序列化交易

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-29
    • 1970-01-01
    • 2021-03-13
    • 1970-01-01
    • 1970-01-01
    • 2016-07-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多