Sto creando una semplice webapp Tomcat che utilizza Spring Data e Hibernate. C'è un punto finale che fa molto lavoro, quindi voglio scaricare il lavoro su un thread in background in modo che la richiesta web non si blocchi per 10+ minuti mentre il lavoro è in corso. Così ho scritto un nuovo servizio in un pacchetto di componenti-scan'd:Come faccio a fare correttamente un thread in background quando si usano Spring Data e Hibernate?
@Service
public class BackgroundJobService {
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
public void startJob(Runnable runnable) {
threadPoolTaskExecutor.execute(runnable);
}
}
quindi avere la ThreadPoolTaskExecutor
configurato in primavera:
<bean id="threadPoolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="queueCapacity" value="25" />
</bean>
Questo è tutto grande lavoro. Tuttavia, il problema deriva da Hibernate. All'interno del mio runnable, le query solo a metà lavoro. Posso fare:
MyObject myObject = myObjectRepository.findOne()
myObject.setSomething("something");
myObjectRepository.save(myObject);
Ma se devo campi caricati pigri, viene a mancare:
MyObject myObject = myObjectRepository.findOne()
List<Lazy> lazies = myObject.getLazies();
for(Lazy lazy : lazies) { // Exception
...
}
ottengo il seguente errore:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.stackoverflow.MyObject.lazies, could not initialize proxy - no Session
Quindi sembra che a me (Hibernate newbie) che il nuovo thread non ha una sessione su questi thread fatti in casa, ma Spring Data crea automaticamente nuove sessioni per i thread HTTP Request.
- C'è un modo per avviare manualmente una nuova sessione all'interno della sessione?
- O un modo per dire al pool di thread di farlo per me?
- Qual è la pratica standard per fare questo tipo di lavoro?
Sono stato in grado di lavorare intorno ad esso un po 'facendo tutto da all'interno di un metodo @Transactional
, ma sto imparando in fretta che non è una soluzione molto buona, come questo non mi lascia usare metodi che funzionano va bene per le richieste web.
Grazie.
Parte del problema è che voglio essere in grado di aggiornare lo stato del lavoro mentre è in esecuzione. Quindi, se lo avvolgo tutto in un'unica transazione, lo stato non si aggiorna fino a quando non si commette il commit più esterno. A meno che non manchi qualcosa qui ... che è totalmente possibile :) – Joel
Puoi mostrare la tua classe di esecuzione Runnable e forse il tuo ObjectRepository? Sono anche un po 'curioso di sapere come e dove intendi pubblicare aggiornamenti di stato. Non è così ovvio con le app web basate su http. – alobodzk
In particolare per l'aggiornamento dello stato del processo: utilizzare una transazione nidificata. Assicurati che il tuo provider JPA li supporti. – Virmundi