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

Spring+Hibernate+PostgreSQL定时任务事务回滚异常排查求助

定时任务抛出Transaction was marked for rollback only异常的根本原因分析

使用Spring、Hibernate、PostgreSQL技术栈,执行定时任务时出现如下异常:

2024-04-02 11:05:34.035 [acquiring_push_notify_scheduler-10] ERROR o.s.s.s.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task.
org.springframework.orm.jpa.JpaSystemException: Transaction was marked for rollback only; cannot commit; nested exception is org.hibernate.TransactionException: Transaction was marked for rollback only; cannot commit
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:333)
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:521)
        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:504)
        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.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
        at com.sun.proxy.$Proxy868.validateExpiredProfiles(Unknown Source)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
        at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
        at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
        at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
Caused by: org.hibernate.TransactionException: Transaction was marked for rollback only; cannot commit
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.commit(JdbcResourceLocalTransactionCoordinatorImpl.java:217)
        at org.hibernate.engine.transaction.internal.TransactionImpl.commit(TransactionImpl.java:68)
        at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:517)
        ... 22 common frames omitted

对应的业务方法如下:

@Override
@Transactional("mainTx")
public void validateExpiredProfiles() {
    List<ProfileData> profiles = verifiedCashierDao.getVerifiedCashiersWithExpiredProfile(LocalDateTime.now().plusDays(NOTIFY_DAY_BEFORE));
    LOGGER.info("validateExpiredProfiles: Size of expired profiles {}", profiles.size());

    for (ProfileData profile : profiles) {
        try{
            agentDao.changeCashierIdentification(profile.getCashierId(), IdentificationStatusEnum.PROFILE_DATA_EXPIRED);
        }
        catch (Exception e){
            LOGGER.error("Error on validateExpiredProfiles {} cause {}", profile.getPhone(), e);
        }
    }
}

Spring XML上下文配置中的定时任务定义为:

<task:scheduled ref="verifiedCashierService" method="validateExpiredProfiles" cron="0 0/5 * * * *" />

根本原因分析

  • 子方法抛出RuntimeException触发事务回滚标记:agentDao.changeCashierIdentification内部可能抛出了RuntimeException(比如数据库约束冲突、Hibernate持久化异常等),尽管你在循环里捕获了Exception,但Spring事务拦截器会监控整个事务上下文——只要事务执行过程中出现未被事务边界捕获的RuntimeException,就会将当前事务标记为rollback-only。当外层事务尝试提交时,就会触发该异常。
  • 事务传播特性导致的上下文污染:如果agentDao.changeCashierIdentification自身也标注了@Transactional,且传播特性为默认的REQUIRED,那么它会加入外层的定时任务事务。当该方法内部抛出异常,即使被外层try-catch捕获,事务上下文已经被标记为回滚,最终外层事务提交必然失败。
  • 数据库隐性异常触发回滚:比如PostgreSQL的死锁、连接超时、数据唯一性约束违反等,这类异常会被Hibernate包装成RuntimeException抛出,触发事务的回滚标记,导致后续提交失败。

内容的提问来源于stack exchange,提问作者Nurzamat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 18:17:10