У задания Grails Quartz нет сеанса Hibernate после обновления, что вызывает исключение LazyInitializationException
Я обновил приложение Grails 1.0.4 до 1.1.1. После обновления я постоянно получаю исключения при выполнении моих заданий Quartz (используя плагин Quartz 0.4.1). Плагин используется для ручного планирования заданий с использованием Simple и Cron Trigger через службу (перефразированный код ниже):
class SchedulerService implements InitializingBean
{
static scope = 'singleton'
...
def schedule(def batch) {
JobDetail job = new JobDetail(uniqueId, groupName, BatchJob.class, false, false, true)
job.jobDataMap.put("batchId", batch.id)
SimpleTrigger trigger = new SimpleTrigger(triggerId, triggerGroup, 0)
SchedulerFactory factory = new SchedulerFactory()
factory.initialize(properties)
Scheduler scheduler = factory.getScheduler()
scheduler.scheduleJob(job, trigger)
}
...
}
Моя задание BatchJob настроено следующим образом:
class BatchJob implements Job, InterruptableJob
{
static triggers = {}
void execute(JobExecutionContext context) {
def batch = Batch.get(context.jobDetail.jobDataMap.getLongValue("batchId"))
// the next line is "line 49" from the stack trace below
def foo = batch.batchStatus.description
}
}
Вот сокращенное определение Batch.groovy (домен):
class Batch
{
BatchStatus batchStatus // relationship
}
Однако когда schedulerService.schedule()
вызывается с существующим, сохраненным пакетом, я получаю следующее исключение:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86)
at org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil.unwrapProxy(GrailsHibernateUtil.java:311)
at org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil$unwrapProxy.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:40)
...
<b>at BatchJob.execute(BatchJob.groovy:49)</b>
...
Я попытался следующие действия, чтобы исправить это, но ни один не работал:
- Я указал
static fetchMode = [batchStatus: 'eager']
в моем классе домена Batch - Я использовал
static mapping = { columns { batchStatus lazy:false }}
в моем классе домена Batch - Я пытался использовать
batch.attach()
после звонкаBatch.get()
в работе
Я не могу использовать BatchJob.triggerNow()
в этом случае, потому что это только один из пары примеров - другие все еще запланированы службой, но могут быть запланированы как задание cron или как-то иначе. Я должен отметить, что я обновил плагин Quartz также и при обновлении Grails; предыдущая версия Quartz была 0.4.1-SNAPSHOT (в отличие от обновленной версии, только 0.4.1).
Как заставить сеансы Hibernate правильно работать в этих кварцевых заданиях, запускаемых вручную?
Я также отправил этот вопрос в список рассылки пользователя grails, так как для более нишевой проблемы, подобной этой, список, кажется, вызывает немного больший отклик. Я обновлю этот вопрос ответом, если кто-то выйдет оттуда. Вот ссылка.
3 ответа
Ознакомьтесь с выпуском jira 165 ( http://jira.codehaus.org/browse/GRAILSPLUGINS-165). В Quartz Plugin также есть подсказки (которые вы можете проверить). Этот код был использован с плагином JMS, который, кажется, хорошо работать.
пытаться
import org.hibernate.FlushMode
import org.hibernate.Session
import org.springframework.orm.hibernate3.SessionFactoryUtils
import org.springframework.orm.hibernate3.SessionHolder
class BatchJob implements Job, InterruptableJob
{
static triggers = {}
void execute(JobExecutionContext context) {
Session session = null;
try {
session = SessionFactoryUtils.getSession(sessionFactory, false);
}
// If not already bound the Create and Bind it!
catch (java.lang.IllegalStateException ex) {
session = SessionFactoryUtils.getSession(sessionFactory, true);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
session.setFlushMode(FlushMode.AUTO);
if( log.isDebugEnabled()) log.debug("Hibernate Session is bounded to Job thread");
// Your Code!
def batch = Batch.get(context.jobDetail.jobDataMap.getLongValue("batchId"))
// the next line is "line 49" from the stack trace below
def foo = batch.batchStatus.description
try {
SessionHolder sessionHolder = (SessionHolder)
TransactionSynchronizationManager.unbindResource(sessionFactory);
if(!FlushMode.MANUAL.equals(sessionHolder.getSession().getFlushMode())) {
sessionHolder.getSession().flush();
}
SessionFactoryUtils.closeSession(sessionHolder.getSession());
if( log.isDebugEnabled()) log.debug("Hibernate Session is unbounded from Job thread and closed");
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}
Надеюсь это поможет. Это сработало для меня.
С последней версией grails (Grails 2.0.0) и, возможно, более ранними версиями, вы можете просто обернуть ваш вызов с помощью этого вспомогательного метода:
class BatchJob implements Job, InterruptableJob
{
static triggers = {}
void execute(JobExecutionContext context) {
Batch.withSession { sess ->
def batch = Batch.get(context.jobDetail.jobDataMap.getLongValue("batchId"))
def foo = batch.batchStatus.description
}
}
}
Я думаю, что вы можете позвонить attach()
способ добавить сеанс к объекту, передаваемому в запланированное задание.
job.attach()