GORM funziona bene fuori dalla scatola purché non ci siano lotti con più di 10.000 oggetti. Senza ottimizzazione affronterai i problemi outOfMemory.Grails sospensione sessione in batch
La soluzione più comune è quella di svuotare() e chiara() la sessione ogni n (EGN = 500) oggetti:
Session session = sessionFactory.currentSession
Transaction tx = session.beginTransaction();
def propertyInstanceMap = org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP
Date yesterday = new Date() - 1
Criteria c = session.createCriteria(Foo.class)
c.add(Restrictions.lt('lastUpdated',yesterday))
ScrollableResults rawObjects = c.scroll(ScrollMode.FORWARD_ONLY)
int count=0;
while (rawObjects.next()) {
def rawOject = rawObjects.get(0);
fooService.doSomething()
int batchSize = 500
if (++count % batchSize == 0) {
//flush a batch of updates and release memory:
try{
session.flush();
}catch(Exception e){
log.error(session)
log.error(" error: " + e.message)
throw e
}
session.clear();
propertyInstanceMap.get().clear()
}
}
session.flush()
session.clear()
tx.commit()
Ma ci sono alcuni problemi che non posso risolvere:
- Se utilizzo currentSession, il controller non riesce a causa della sessione è vuota
- Se utilizzo sessionFactory.openSession(), la sessione corrente viene comunque utilizzata all'interno di FooService. Per causa posso usare la notazione session.save (oggetto). Ma questo significa che devo modificare fooService.doSomething() e duplicare il codice per un'operazione singola (notazione di graal comuni come fooObject.save()) e operazione batch (session.save (fooObject()) .. notation).
- Se utilizzo Foo.withSession {session->} o Foo.withNewSession {session->}, gli oggetti di Foo Class vengono cancellati da session.clear() come previsto. Tutti gli altri oggetti non vengono cancellati(), ciò che porta alla perdita di memoria.
- Di causa posso usare sfratto (oggetto) per cancellare manualmente la sessione. Ma è quasi impossibile ottenere tutti gli oggetti rilevanti, a causa dell'autofetching delle assunzioni.
Quindi non ho idea di come risolvere i miei problemi senza rendere il FooService.doSomething() più complesso. Sto cercando qualcosa come withSession {} per tutti i domini. O per salvare la sessione all'inizio (Session tmp = currentSession) e fare qualcosa come sessionFactory.setCurrentSession (tmp). Entrambi non esiste!
Qualsiasi idea è benvenuta!
Questo sembra un lavoro che dovrebbe essere svolto interamente in un metodo di servizio. Se all'interno del metodo di servizio, si utilizza 'currentSession', il controller funziona ancora? – doelleri
Sono d'accordo con @doelleri. I servizi sono il posto giusto per farlo. Inoltre, ricorda che per impostazione predefinita sono transazionali, se vuoi gestire manualmente lo stato, usa 'Domain.withTransaction' e imposta' statical transaction = false' o lascia che il servizio si occupi del commit/rollback. –
Il codice che ho postato lì è già all'interno di un metodo di servizio. Sì, potrei usare il contesto di transazione del metodo di servizio, ma non risolverà il mio problema. @doelleri - La risposta alla tua domanda è: il controller frena, in modo che l'utente non possa fare più nulla nell'applicazione tranne che chiudere il browser.(vedi problema 1) – Waldemar