4

我在我的 Grails 项目( grails 2.2.1 )上遇到了石英问题(插件 :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 "
        }
    }

}

}

有时在执行方法中会出现 StaleObjectStateException。这对我的逻辑来说没问题,我正在使用 grails 乐观锁定,并且这个异常每周只发生一次。

问题是当此异常发生时,作业停止再次触发。

我已经尝试将方法代码共同包装在 try catch 中并在内部刷新休眠会话以捕获异常但没有运气。我的任何捕获都没有捕获异常。

在网上查找,我发现这是一个旧的 grails 石英错误,但已修复,无论如何使用 try{}catch 必须绕过该错误。

PS 该作业是通过这种类型的调用从引导程序调度的

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)
4

3 回答 3

1

我很抱歉恢复旧帖子,但我们最近在旧版 Grails 应用程序(Grails 2.2.3)中遇到了这个问题,并且在执行方法中刷新会话并不总能解决问题,所以我将概述我们为修复该问题所做的工作问题。

在我们的例子中,有时异常会在执行方法的上下文之外发生,即使我们在执行方法中显式刷新会话也是如此。更具体地说,异常是在 Quartz2 插件的 PersistenceContextJobListener 代码中引发的,该代码在执行方法完成执行后刷新会话。因此,在查看 Quartz2 插件代码后,我们意识到我们需要覆盖默认的 PersistenceContextJobListener,它包装了作业执行方法并刷新会话。

首先,请注意 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 而完全停止。

于 2016-04-28T15:23:07.427 回答
0

我遇到了类似的问题,石英作业在没有绑定休眠会话的线程上运行,我能够通过抓取一个新会话然后强制刷新()和清除()来绕过它。如果您不刷新和清除作业,最终将重新使用以前的作业线程之一并尝试写出相同的对象(不记得它是否必须是相同的对象或同一类的任何对象) ,但在绑定到该线程的会话中会有一个未提交的副本,这反过来会导致 StaleObjectException:

这是我的代码的样子:

def sessionFactory

def execute() {
    def session = SessionFactoryUtils.getSession(sessionFactory,true)

    myDomain.save(flush: true)

    session.flush()
    session.clear()
}

您可能只需要在示例代码中执行 flush() 和 clear() 即可获得相同的结果。

于 2013-06-20T14:16:23.700 回答
0

不知道您的示例有多准确,但您需要知道 groovy 正在包装异常。这意味着即使代码抛出 StaleObjectStateException,您也可以将其包装在上面未捕获的简单 RuntimeException 中。该方法有多深myDomain.save(flush: true)(直接在工作中或从其他服务执行)?

于 2013-05-23T20:09:19.000 回答