2011-08-25 18 views
5

我想在grails应用程序中创建特定类型的后台处理设置。在grails中的后台线程中调用Hibernate

  • 固定大小线程池只存在
  • 单个会话由每个线程维持该批次作业的持续时间
  • 每个作业在单独的事务运行

我想开始工作如下:

int poolSize = 10 
ThreadFactory factory = new MyThreadFactory (Executors.defaultThreadFactory()) 
ExecutorService pool = Executors.newFixedThreadPool (poolSize, factory) 

(1..100).each { i -> 
    pool.submit { 
    try { 
     MyDomainClass.withTransaction { 
     doSomeWork(i) 
     } 
    } catch (Exception e) { 
     log.error "error in job ${i}", e 
    } 
    } 
} 

MyThreadFactory创建具有一个hibernate会话线程附为线程的持续时间。

class MyThreadFactory implements ThreadFactory { 

    ThreadFactory delegate 
    PersistenceContextInterceptor persistenceInterceptor 

    MyThreadFactory (ThreadFactory delegate) { 
    this.delegate = delegate 
    ApplicationContext applicationContext = ApplicationHolder.getApplication().getMainContext() 
    persistenceInterceptor = applicationContext.getBean("persistenceInterceptor"); 
    } 

    Thread newThread (Runnable work) { 
    return delegate.newThread { 
     persistenceInterceptor.init() 
     try { 
     work.run() 
     } finally { 
     persistenceInterceptor.flush() 
     persistenceInterceptor.destroy() 
     } 
    } 
    } 
} 

它似乎工作,但我会在第一次运行批处理作业时出现以下错误。 (后续作业运行无事故)

groovy.lang.MissingMethodException: No signature of method: static MyDomainClass.save() is applicable for argument types: (java.util.LinkedHashMap) values: [[flush:false]] 
Possible solutions: save(), save(java.util.Map), save(java.lang.Boolean), wait(), any(), wait(long) 
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
at ... 

我试图与MyDomainClass.withNewSession {}更换persitanceInterceptor,没有效果。

似乎GORM方法没有被注入到我的域类中。

任何人都可以看到我做错了什么,以及为什么再次运行批处理作业允许它成功?

@fixitagain为完整的工作采取这种形式:

doSomeWork = { id -> 
    MyDomainClass a = MyDomainClass.findById (id) 
    a.value = lotsOfWork() 
    a.save() 
} 

我相信丢失的存储是一个红色的鲱鱼,因为我想在包裹交易操作,然后得到一个错误说' DomainClass.withTransaction(Closure)'未定义。

看起来可能存在第一个作业无法运行的竞态条件,但所有后续作业成功运行后(东西?)已完成启动。

+0

他在Grails生命周期的哪个阶段开始工作?这听起来像是在域名被动态方法等装饰之前发生的。 –

+0

作业正在从控制器动作(现在)触发,所以在完成域的装饰之后就已经完成了。我应该注意到,如果我从主线程调用所有作业,则不会出现问题。 – Akusete

+0

好吧,对我来说,GORM是“附加”的,因为它提出了保存(Map)作为可能的解决方案。你可以把你正在使用的保存行吗? BTW flush默认为false,这意味着它将在Hibernate会话结束时刷新... – fixitagain

回答

1

而不是尝试创建自己的线程,可能建议使用Grails的executor插件。它将必要的hibernate会话注入到您创建的线程中,并且它可以相对于它使用的执行程序,线程数量等进行配置。我在生产中使用Quartz作业和其他场景,并且它工作得很好。

Grails Executor Plugin 如果您对使用它有所保留,可以在编写自己的线程策略之前查看其代码。

+0

我看了一下执行程序插件,它也使用persistenceInterceptor在新线程中创建会话。目前我可能更感兴趣的是确保在使用插件之前完全理解grails/hibernate是如何工作的。不过谢谢你提到它。 – Akusete

+0

第二次查看执行程序插件后,它将始终为每个作业创建一个新的会话。一般来说,这是有道理的,但不适用于我之后的特定任务。我试图使用完全相同的机制创建会话作为插件。 – Akusete

+0

为什么不使用Quartz插件? http://grails.org/plugin/quartz – felipenasc

0

我无法从代码或命名约定中得出结论,但是您确定要调用save类的实例吗?

+0

我认为这是一个'怪癖'与错误消息,我敢反对调用save()域类实例。另外,我认为如果这是问题,它不能解释为什么当我第二次运行它时,工作会成功完成。调用a.save()之前调用 – Akusete

+0

,a = a.merge()有帮助吗? – bluesman

0

缺少的方法异常意味着你调用保存在类,而不是实例。

编辑:GORM已经应用了其他方法,正如您在建议的解决方案名称中所看到的。

相关问题