You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring/Hibernate中自定义异常转换器未触发问题求助

问题:自定义LocalSessionFactoryBean的异常转换器未触发ORA-08177转换

你已经实现了自定义的MyLocalSessionFactoryBean,重写了translateExceptionIfPossible方法来处理Oracle的事务序列化异常(ORA-08177),代码如下:

class MyLocalSessionFactoryBean extends LocalSessionFactoryBean {
    @Override
    public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
        if (ex instanceof HibernateJdbcException) {
            // Oracle序列化异常的SQL状态为[72000],错误码为[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);
    }
}

并且已经将该Bean设为primary,DAO也标注了@Repository,但事务提交阶段抛出ORA-08177时,自定义方法从未被调用,异常直接传播,导致Spring Retry无法触发重试。

附异常栈:

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

问题分析与解决方案

先给你理清楚为什么原来的方法没生效:从异常栈可以看到,ORA-08177是在事务提交阶段抛出的,异常最终由HibernateTransactionManager.doCommit方法处理,它调用的是SessionFactoryUtils.convertHibernateAccessException来转换异常,而不是你重写的LocalSessionFactoryBean.translateExceptionIfPossible——这个方法仅在Spring的HibernateTemplate或者DAO直接操作Session抛出异常时才会被触发,提交阶段的异常走的是另一条处理链路。

下面给你两种可行的解决办法:

方案一:自定义HibernateTransactionManager,重写异常转换逻辑

直接在事务管理器层面处理ORA-08177的转换,这样就能覆盖提交阶段的异常处理:

public class CustomHibernateTransactionManager extends HibernateTransactionManager {
    @Override
    protected DataAccessException convertHibernateAccessException(HibernateException ex) {
        // 检查是否是目标异常
        if (ex instanceof HibernateJdbcException) {
            SQLException sqle = ((HibernateJdbcException) ex).getSQLException();
            if ("72000".equals(sqle.getSQLState()) && sqle.getErrorCode() == 8177) {
                return new CannotSerializeTransactionException("无法序列化事务访问", ex);
            }
        }
        // 其他异常交给父类处理
        return super.convertHibernateAccessException(ex);
    }
}

然后在你的配置中(XML或者Java配置类),把原来的HibernateTransactionManager替换成这个自定义类:

如果是XML配置:

<bean id="transactionManager" class="com.yourpackage.CustomHibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

如果是Java配置类:

@Bean
public PlatformTransactionManager transactionManager(SessionFactory sessionFactory) {
    CustomHibernateTransactionManager txManager = new CustomHibernateTransactionManager();
    txManager.setSessionFactory(sessionFactory);
    return txManager;
}

方案二:自定义Hibernate的SQLExceptionConverter

让Hibernate本身把ORA-08177转换成CannotSerializeTransactionException,这样Spring的SessionFactoryUtils会自动识别这个异常类型,无需修改事务管理器:

public class OracleSQLExceptionConverter extends StandardSQLExceptionConverter {
    @Override
    public JDBCException convert(SQLException sqlException, String message, String sql) {
        // 匹配Oracle序列化异常
        if ("72000".equals(sqlException.getSQLState()) && sqlException.getErrorCode() == 8177) {
            return new CannotSerializeTransactionException(message, sqlException, sql);
        }
        // 其他异常交给父类处理
        return super.convert(sqlException, message, sql);
    }
}

然后配置Hibernate使用这个转换器,在LocalSessionFactoryBean的Hibernate属性中添加:

如果是XML配置:

<bean id="sessionFactory" class="com.yourpackage.MyLocalSessionFactoryBean">
    <!-- 其他配置... -->
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.jdbc.sql_exception_converter">com.yourpackage.OracleSQLExceptionConverter</prop>
            <!-- 其他Hibernate属性 -->
        </props>
    </property>
</bean>

如果是Java配置类:

@Bean
public LocalSessionFactoryBean sessionFactory() {
    MyLocalSessionFactoryBean sessionFactory = new MyLocalSessionFactoryBean();
    // 配置数据源、实体扫描等...
    Properties hibernateProps = new Properties();
    hibernateProps.put("hibernate.jdbc.sql_exception_converter", OracleSQLExceptionConverter.class.getName());
    // 其他Hibernate属性设置
    sessionFactory.setHibernateProperties(hibernateProps);
    return sessionFactory;
}

这两种方案都能让ORA-08177被转换成CannotSerializeTransactionException,Spring Retry就能识别这个异常并触发重试逻辑了。


内容的提问来源于stack exchange,提问作者usr-local-ΕΨΗΕΛΩΝ

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:10:19