我在 Grails 项目( grails 2.2.1 )上遇到了 quartz 问题(插件 :quartz2:2.1.6.2 但我什至使用插件 :quartz:1.0-RC7 也进行了测试,但问题没有改变)。
我有这样的工作
class MyJob {
def concurrent = false
def execute(context){
try {
//....
// works with domains .....
myDomain.save(flush: true)
// works with domains .....
//....
sessionFactory.currentSession.flush()
} catch (org.springframework.dao.OptimisticLockingFailureException olfe) {
println "Job failed by database exception "
} catch ( org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException ole){
println "Job failed by database exception "
} catch ( org.hibernate.HibernateException hibe ){
println "Job failed by database exception "
}
}
}
}
有时在 execute 方法中会出现 StaleObjectStateException。这对我的逻辑来说没问题,我正在使用 grails 乐观锁定,并且这个异常每周只发生一次。
问题是当这个异常发生时,Job 会再次停止触发。
我曾尝试将方法代码包装在 try catch 中并在内部刷新 hibernate session 以捕获异常但没有运气。我的任何捕获都没有捕获异常。
在线查看我发现这是一个 old grails quartz bug但它已修复,在任何情况下使用 try{}catch 都必须绕过该错误。{}
附言
作业是通过这种类型的调用从 bootstrab 调度的
MyJob.schedule( 10000L )
停止调度的异常是
[194949896] core.ErrorLogger Unable to notify JobListener(s) of Job that was executed: (error will be ignored). trigger= DEFAULT.MT_3tbn6lewgiqa3 job= DEFAULT.MyJob
org.quartz.SchedulerException: JobListener 'persistenceContextJobListener' threw exception: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [MyDomain#42] [See nested exception: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [MyDomain#42]]
at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1939)
at org.quartz.core.JobRunShell.notifyJobListenersComplete(JobRunShell.java:361)
at org.quartz.core.JobRunShell.run(JobRunShell.java:235)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:557)
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [MyDomain#42]
at grails.plugin.quartz2.PersistenceContextJobListener.jobWasExecuted(PersistenceContextJobListener.groovy:46)
at org.quartz.core.QuartzScheduler.notifyJobListenersWasExecuted(QuartzScheduler.java:1937)
... 3 more
.....
events.PatchedDefaultFlushEventListener Could not synchronize database state with session
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [MyJob#42]
at MyJob.execute(MyJob.groovy:354)
at grails.plugin.quartz2.GrailsArtefactJob.execute(GrailsArtefactJob.java:57)
at org.quartz.core.JobRunShell.run(JobRunShell.java:213)
at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:557)
最佳答案
我很抱歉恢复旧帖子,但我们最近在旧版 Grails 应用程序(Grails 2.2.3)中遇到了这个问题,并且在 execute 方法中刷新 session 并不总是能解决问题,因此我将概述我们为修复该问题所做的工作问题。
在我们的例子中,有时异常会发生在 execute 方法的上下文之外,即使我们在 execute 方法中显式刷新 session 也是如此。更具体地说,异常是在 Quartz2 插件的 PersistenceContextJobListener 代码中抛出的,该代码在 execute 方法完成执行后刷新 session 。因此,在查看 Quartz2 插件代码后,我们意识到我们需要覆盖默认的 PersistenceContextJobListener 来包装作业执行方法并刷新 session 。
首先,请注意 PersistenceContextJobListener 的 jobWasExecuted 回调方法中没有异常处理。
https://github.com/9ci/grails-quartz2/blob/master/src/groovy/grails/plugin/quartz2/PersistenceContextJobListener.groovy#L44
您真正需要做的就是实现您自己的作业监听器并将 jobWasExecuted 代码包装在 try/catch 中。有关如何执行此操作的示例,请参阅以下代码片段。
https://gist.github.com/jmiranda/45084eb32f07f6e3d1934547cd4fbb9f
https://gist.github.com/jmiranda/5148f0a67afc8950bad950793e9c2303
我们以原始 Quartz 插件的 SessionBinderJobListener 为例(错误,我们或多或少复制了它)。
https://github.com/grails-plugins/grails-quartz/blob/master/src/main/groovy/grails/plugins/quartz/listeners/SessionBinderJobListener.java
无论如何,这应该使您能够防止由于未捕获的 StaleObjectStateException 而完全停止触发的作业。
关于hibernate - Quartz 作业因 StaleObjectStateException 异常而停止执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16394437/