在Hibernate / Spring

时间:2018-01-22 13:40:53

标签: spring hibernate

我编写了代码来处理特定的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可以再次重放事务。

如何让自定义异常翻译工作?

1 个答案:

答案 0 :(得分:0)

找到了罪魁祸首。

仅在提交阶段抛出此类异常。我的DAO已注释@Repository,但未注明@Transactional,因此任何操作都会通过DAO的@Repository注释保持exception translator interceptor没有问题。但是,当PlatformTransactionManager尝试提交时,如果堆栈跟踪中显示的PersistenceExceptionTranslationAdvisor没有正确修饰它。

HibernateTransactionManager仅翻译HibernateExceptionPersistenceException

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

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