Grails quarzo di lavoro non ha alcuna sessione di Hibernate dopo l'aggiornamento, causando LazyInitializationException
-
13-09-2019 - |
Domanda
Ho aggiornato un'applicazione Grails 1.0.4 a 1.1.1. Dopo l'aggiornamento, sto ricevendo più volte eccezioni durante l'esecuzione i miei lavori di quarzo (usando quarzo plug-0.4.1). Il plugin è usato per programmare manualmente lavori utilizzando semplici e Cron trigger tramite un servizio (codice parafrasato sotto):
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)
}
...
}
Il mio lavoro BatchJob è configurato come segue:
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
}
}
Ecco una definizione abbreviata di Batch.groovy (dominio):
class Batch
{
BatchStatus batchStatus // relationship
}
Tuttavia, quando schedulerService.schedule()
viene richiamato con uno esistente, Batch risparmiato, ricevo la seguente eccezione:
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>
...
Ho provato le seguenti azioni per risolvere questo problema, ma nessuno lavorato:
- ho specificato
static fetchMode = [batchStatus: 'eager']
sulla mia classe di dominio Batch - Ho usato
static mapping = { columns { batchStatus lazy:false }}
sulla mia classe di dominio Batch - Ho provato con
batch.attach()
dopo aver chiamatoBatch.get()
nel lavoro
Non posso usare BatchJob.triggerNow()
in questo caso, perché questo è solo uno di un paio di esempi - gli altri sono ancora in programma dal servizio, ma potrebbe essere programmato come un lavoro cron o in altro modo. Devo dire che ho fatto aggiornare il plugin quarzo e durante l'aggiornamento Grails; la precedente versione di quarzo era 0.4.1-SNAPSHOT (in contrasto con la versione aggiornata, basta 0.4.1).
Come faccio a ottenere le sessioni di Hibernate per funzionare correttamente in questi posti di lavoro quarzo manualmente-triggered?
Ho anche inviato questa domanda alla mailing list graal-user, come per una questione più di nicchia come questo, la lista sembra di suscitare un po 'più di risposta. Io aggiornare a questa domanda con una risposta se uno viene fuori di lì. Ecco un link .
Soluzione
Scopri jira questione 165 ( http://jira.codehaus.org/browse/GRAILSPLUGINS -165 ) ci sono anche indizi nel plugin quarzo (che come si può controllare) Questo codice è stato utilizzato con il plugin JMS, che sembra funzionare bene.
try
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();
}
}
}
Spero che questo aiuti. Ha funzionato per me.
Altri suggerimenti
Con l'ultima versione graal (Grails 2.0.0) e le versioni precedenti forse, si può semplicemente avvolgere la chiamata con questo metodo di supporto:
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
}
}
}
Penso che si può chiamare il metodo attach()
aggiungere sessione per l'oggetto passando per il processo pianificato.
job.attach()