无法提交JPA事务 - RollbackException: 事务被标记为rollbackOnly。

3

首先,我想说我在stackoverflow上看到了所有关于我的问题的主题,但仍无法解决我的问题。

我需要每晚运行定时任务来检查任务是否完成 - 我是这样做的:

@Service
@Transactional
public class CronBackGroundProcess {

@Autowired
private CronJobService cronJobService;

@Scheduled(cron = "15 01 01 ? * *")
public void StartNightJob() {
    CronJobLog log = new CronJobLog();
    int count = 0;
    try {
        log.setStartTime(new Date());
        log.setStatus("Entered StartNightJob Function");
        cronJobService.saveCronJobLog(log);

        List<Task> Tasks = cronJobService.getActive_AND_InArreasTasks();
        log.setStatus("Grabbed List of tasks to Check");
        cronJobService.saveCronJobLog(log);

        for (Task Task : Tasks) {
            cronJobService.StartNightJobProcess(Task, true);
            count++;
        }
    } catch (Exception e) {
        CronJobLog log2 = new CronJobLog();
        log2.setStatus("Error Occurred " + new Date().toString() + e.getMessage());
        cronJobService.saveCronJobLog(log2);
    }

    log.setLoansChecked(count);
    log.setStatus("Finished");
    log.setEndDate(new Date());
    cronJobService.saveCronJobLog(log);
}
}

CronJobService本身是@Transactional,并自动装配了几个@Transactional的服务。

@Service
@Transactional
public class CronJobService {

@Autowired
private ProductService productService;

@Autowired
private RepaymentService repaymentService;

@Autowired
private CronJobLogDAO cronJobLogDAO;


@Autowired
private TransferService transferService;

public String StartNightJobProcess(Account account, boolean makeTransfers) {

    do something....
            }
        }
    }

该过程未发生错误,但在提交所有交易时,我收到以下错误消息:
 org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:524) ~[spring-orm-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:757) ~[spring-tx-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:726) ~[spring-tx-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:478) ~[spring-tx-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:272) ~[spring-tx-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95) ~[spring-tx-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:646) ~[spring-aop-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at ge.shemo.services.core.CronBackGroundProcess$$EnhancerByCGLIB$$30cdcf31.StartNightJob(<generated>) ~[spring-core-4.0.0.RELEASE.jar:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_79]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_79]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_79]
at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_79]
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65) ~[spring-context-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) ~[spring-context-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81) [spring-context-4.0.0.RELEASE.jar:4.0.0.RELEASE]
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) [na:1.7.0_79]
at java.util.concurrent.FutureTask.run(FutureTask.java:262) [na:1.7.0_79]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178) [na:1.7.0_79]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292) [na:1.7.0_79]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [na:1.7.0_79]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [na:1.7.0_79]
at java.lang.Thread.run(Thread.java:745) [na:1.7.0_79]
Caused by: javax.persistence.RollbackException: Transaction marked as rollbackOnly
at org.hibernate.jpa.internal.TransactionImpl.commit(TransactionImpl.java:58) ~[hibernate-entitymanager-5.0.1.Final.jar:5.0.1.Final]
at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:515) ~[spring-orm-4.0.0.RELEASE.jar:4.0.0.RELEASE]
... 22 common frames omitted

我想不出为什么。此外,如果我从@ Controller启动同一函数,则可以正常工作。

@Controller
@RequestMapping("/test")
public class test {

@Autowired
private ClientService clientService;

@Autowired
private CronBackGroundProcess cronBackGroundProcess;

@RequestMapping(value = "/test")
@ResponseBody
public void test() throws Exception {
    try {

        cronBackGroundProcess.StartNightJob();
    } catch (Exception e) {
       String s = "sd";
    }
   }
}

所以我的问题是:为什么这个函数在控制器中可以正常工作 - 提交所有内容,并且在定时任务中不起作用(没有错误地经过了所有流程)?

您的 Transaction marked as rollbackOnly,因为出现了错误,并损坏了连接。请仔细查看日志以找到异常,如果必要,甚至可以启用额外的日志记录(仅在某个地方捕获了该异常但未打印时才需要)。 - Gergely Bacso
4
你的代码有缺陷。你捕获了一个异常,但从未将其传递到外部事务,外部事务仍然认为可以提交,但由于异常已经被标记为回滚,因此无法提交。你应该重新抛出异常,并将catch后面的内容放在finally块中,并确保在新事务中编写(以及作业的开头)。或者从你的"CronBackGroundProcess"中简单地删除"@Transactional"注释。 - M. Deinum
1
从CronBackGroundProcess中删除@Transactional,我能够捕获错误,但错误与堆栈相同。 - Irakli
1
如果可以的话,请在org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex)中设置一个调试断点,然后查看实际异常是什么。 - Pallav Jha
好的 - 所以我找到了问题所在。问题是我正在为已登录的用户和系统使用相同的服务 - 在那里我正在记录谁在进行更改 - 因为系统本身没有用户,所以当我从Spring Security获取当前用户时会收到错误。更改函数,现在一切都正常了。@PallavJha - 谢谢,我能够从completeTra‌​sactionAfterThrowing函数中找到实际错误,就像你提供的那样。感谢所有帮助我的人 :) - Irakli
显示剩余10条评论
2个回答

8
如果可以的话,可以在org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex)中设置一个调试断点,然后查看实际的异常信息。

问题出现在开发环境中,因此断点不可行。 - Sham Fiorin
我添加了一个断点,发现其中一个外键为空... JPA 没有告诉我它缺失了... 我想到了添加断点的主意... :) 实际上,你可以在任何带有远程调试的环境中设置断点 :) - Marcello DeSales

1
您不需要将CronBackGroundProcess标记为@Transactional,因为在StartNightJob()方法中,您无法访问数据库,我猜测您是在CronJobService中执行所有对数据库的访问。

因此,请从CronBackGroundProcess中删除@Transactional,这应该有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接