Hibernate: Inserire migliaia di righe senza avere problemi di m

Hibernate: Inserimenti Batch senza OutOfMemoryException G.Morreale
Introduzione:
In questo breve articolo voglio mostravi appena due righe di codice per rendere persistenti migliaia di Entity senza intasare la memoria.
Nel titolo è presente il nome di hibernate.. in quanto è il framework più usato, ma la soluzione è valida anche per altri persistence provider.
Esempio
Supponiamo di voler eseguire il seguente codice di inserimento
Esempiotable t;
for (int i = 0; i < 500000; i++)
{
//Creazione nuovo entity (non passo alcun parametro perchè l'id è autoincrement)
t = new Esempiotable();
//Memorizzazione entity all'interno del persistence Context attraverso em(EntityManager)
em.persist(t);
}
In questa porzione di codice ad ogni creazione dell'entity esso verrà inserito all'interno del persistence context, ciò ad ogni ciclo incrementa il consumo di bytes all'interno dell'heap.
Effettuando un test del precedente codice attraverso il profiler di netbeans è possibile notare come il consumo di memoria massimo durante il ciclo si aggiri intorno ai 250MB per un Entity dotato di soli due campi (Integer e String (peraltro lasciato vuoto))
Se anzichè 500000 entity ne creassimo molti di più otterremmo un Eccezione di tipo OutOfMemoryException
La Soluzione
Qual'è il "contenitore" che si riempie? Il PERSISTENCE CONTEXT!!
Quindi è necessario di tanto in tanto svuotarlo.
Esempiotable t;
for (int i = 0; i < 500000; i++)
{
//Creazione nuovo entity (non passo alcun parametro perchè l'id è autoincrement)
t = new Esempiotable();
//Memorizzazione entity all'interno del persistence Context attraverso em(EntityManager)
em.persist(t);
if ((i % 20) == 0)//Ogni venti inserimenti
{ em.flush();//rendiamo persisteni le modifiche (non necessario se em.flushModeType = COMMIT
em.clear();//ripuliamo il persistence context
}
}
Attraverso il flushing e relativo clear il consumo di memoria nella stessa situazione provata prima non supera i 48MB.
Ciò dimostra che il metodo funziona non solo teoricamente ma anche nella pratica.
nota:
Nel caso sia attiva una cache di secondo livello, essa deve essere disabilitata per le operazioni batch.
Conclusione
La tecnica "flush and clear" funziona anche nel caso di update, in tal caso se si usa hibernate si può optare per lo ScrollableResults in modo da usare i cursori per velocizzare il processo di estrazione dei dati e non intasare la memoria nel caricamento di troppi dati nel persistence context.
Inoltre come consigliato nella guida hibernate, bisogna settare nella configurazione, la seguente proprietà:
hibernate.jdbc_batch_size = 20;
in modo da abilitare il batch processing a livello jdbc.
Riferimenti.

No comments: