4

在我的Grails项目中,我有一个石英(plugin:quartz2:2.1.6.2,但我测试过插件:quartz:1.0-RC7,但问题没有改变)(grails 2.2.1)。Quartz作业停止在StaleObjectStateException异常处执行

我有这样的执行方法本

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 occour工作。对于我的逻辑来说,这是可以的,我使用grails乐观锁,并且这种例外每周只发生一次。

问题是当这个异常发生时,Job停止再次发射。

我试着在try catch和flush hibernate session内部包装方法代码来捕获异常但没有财富。我的任何捕捉都没有捕捉到这个例外。

在网上查找我发现这是一个old grails quartz bug但它是固定的,无论如何使用try {} catch必须绕过该错误。

P.S. 的作业从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) 
+0

在你的代码示例你不要再追“org.hibernate.StaleObjectStateException”,所以我想这是正常的工作失败... – moskiteau 2013-05-14 13:06:31

+0

@moskiteau org.hibernate.StaleObjectStateException是org.hibernate.HibernateException – 2013-05-14 13:16:31

回答

1

对于恢复旧帖子我表示歉意,但我们最近在旧版Grails应用程序(Grails 2.2.3)中遇到了此问题,并且在执行方法中刷新了会话并不总是能解决问题,因此我将概述我们确实解决了这个问题。

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

首先,请注意,PersistenceContextJobListener的jobWasExecuted回调方法中没有异常处理。

https://github.com/9ci/grails-quartz2/blob/master/src/groovy/grails/plugin/quartz2/PersistenceContextJobListener.groovy#L44

你真正需要做的是实现自己的工作听众在一个try/catch包裹jobWasExecuted代码。有关如何执行此操作的示例,请参阅以下代码片段。

https://gist.github.com/jmiranda/45084eb32f07f6e3d1934547cd4fbb9f https://gist.github.com/jmiranda/5148f0a67afc8950bad950793e9c2303

我们使用的原始石英插件的SessionBinderJobListener为例(ERR,我们或多或少复制它)。

https://github.com/grails-plugins/grails-quartz/blob/master/src/main/groovy/grails/plugins/quartz/listeners/SessionBinderJobListener.java

无论如何,这应该让你到你在哪里防止触发工作从完全停止的地步,由于未捕获StaleObjectStateException。

+0

伟大的工作Justin。我不再在这个项目上工作,我无法测试解决方案,但对我来说似乎很好。 – 2016-04-29 07:40:09

0

不知道你的例子如何准确的,但你要知道,常规正在打包异常。这意味着即使在代码抛出StaleObjectStateException时,也可以用简单的RuntimeException封装它,这在上面没有提到。 myDomain.save(flush: true)方法(直接工作或从其他服务执行)有多深?

0

我碰到类似的问题,石英作业运行在一个没有绑定hibernate会话的线程上,我可以通过抓住一个新的会话然后强制flush()和a明确()。如果你没有清除&清除作业最终将重新使用以前的工作线程之一,并尝试写出相同的对象(不能回想起来,如果它必须是同一类或同一类的任何对象) ,但会有在绑定到该线程的会话的未提交副本,这将反过来导致StaleObjectException异常:

这里是我的代码如下所示:

def sessionFactory 

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

    myDomain.save(flush: true) 

    session.flush() 
    session.clear() 
} 

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

+0

Thanl你miedzial。用齐平和清晰的尝试。问题不在于我的项目假设的StaleObjectException。问题是,当StaleObjectException出现时,我无法捕捉它,石英停止执行其他调用 – 2013-06-24 08:00:06