2010-03-07 16 views
7

Sto utilizzando la modalità di sospensione per aggiornare i prodotti 20K nel mio database.utilizzando Hibernate per caricare prodotti 20K, modificando l'entità e aggiornando in db

A partire da ora inserisco i prodotti 20K, passandoci sopra e modificando alcune proprietà e aggiornando il database.

così:

load products 

foreach products 
    session begintransaction 
    productDao.MakePersistant(p); 
    session commit(); 

A partire da ora le cose sono piuttosto lento rispetto al vostro standard JDBC, cosa posso fare per accelerare le cose?

Sono sicuro che sto facendo qualcosa di sbagliato qui.

+0

Forse significa veramente * N * Hibernate (nel titolo)? – M4N

+0

Non per Java: NHibernate è per .NET. – duffymo

+0

risolto il titolo grazie. – Blankman

risposta

9

Il diritto posto a guardare in la documentazione per questo tipo di trattamento è l'intero Chapter 13. Batch processing.

Qui, ci sono diversi errori evidenti nel vostro approccio attuale:

  • Non si dovrebbe iniziare/commit della transazione per ogni aggiornamento.
  • è consigliabile attivare JDBC dosaggio e impostarlo su un numero ragionevole (10-50):

    hibernate.jdbc.batch_size 20 
    
  • si dovrebbe flush() e poi clear() la sessione a intervalli regolari (ogni n record dove n è pari a il parametro hibernate.jdbc.batch_size) o continuerà a crescere e potrebbe esplodere (con un OutOfMemoryException) ad un certo punto.

Di seguito, l'esempio dato nella sezione 13.2. Batch updates che illustra questo:

Session session = sessionFactory.openSession(); 
Transaction tx = session.beginTransaction(); 

ScrollableResults customers = session.getNamedQuery("GetCustomers") 
    .setCacheMode(CacheMode.IGNORE) 
    .scroll(ScrollMode.FORWARD_ONLY); 
int count=0; 
while (customers.next()) { 
    Customer customer = (Customer) customers.get(0); 
    customer.updateStuff(...); 
    if (++count % 20 == 0) { 
     //flush a batch of updates and release memory: 
     session.flush(); 
     session.clear(); 
    } 
} 

tx.commit(); 
session.close(); 

Si può anche considerare l'utilizzo della StatelessSession.

Un'altra opzione sarebbe quella di utilizzare DML-style operations (in HQL!): UPDATE FROM? EntityName (WHERE where_conditions)?.In questo esempio l'UPDATE HQL:

Session session = sessionFactory.openSession(); 
Transaction tx = session.beginTransaction(); 

String hqlUpdate = "update Customer c set c.name = :newName where c.name = :oldName"; 
// or String hqlUpdate = "update Customer set name = :newName where name = :oldName"; 
int updatedEntities = s.createQuery(hqlUpdate) 
     .setString("newName", newName) 
     .setString("oldName", oldName) 
     .executeUpdate(); 
tx.commit(); 
session.close(); 

Anche in questo caso, fare riferimento alla documentazione per i dati (in particolare come trattare con i valori version o timestamp proprietà utilizzando la parola chiave VERSIONED).

5

Se questa è la pseudo-codice, io consiglierei di spostare la transazione al di fuori del ciclo, o almeno avere un doppio loop se avere tutti i 20K i prodotti in una singola transazione è troppo:

load products 
foreach (batch) 
{ 
    try 
    { 
     session beginTransaction() 
     foreach (product in batch) 
     { 
      product.saveOrUpdate() 
     } 
     session commit() 
    } 
    catch (Exception e) 
    { 
     e.printStackTrace() 
     session.rollback() 
    } 
} 

anche , Ti consiglio di mettere in batch gli UPDATE invece di inviarli singolarmente al database. C'è troppo traffico di rete in questo modo. Raggruppa ciascun blocco in un singolo batch e inviali tutti in una volta.

0

Il modo più rapido per eseguire un aggiornamento batch è convertirlo in una singola istruzione SQL ed eseguirlo come sql raw nella sessione. Qualcosa di simile

update TABLE set (x=y) where w=z; 

In mancanza di questo si può provare a fare al netto dei rapporti e fare gli aggiornamenti in lotti:

start session 
start transaction 

products = session.getNamedQuery("GetProducs") 
    .setCacheMode(CacheMode.IGNORE) 
    .scroll(ScrollMode.FORWARD_ONLY); 
count=0; 
foreach product 
    update product 
    if (++count % 20 == 0) { 
     session.flush(); 
     session.clear(); 
    } 
} 

commit transaction 
close session 

Per ulteriori informazioni cerca nella Hibernate Community Docs

1

concordo con la risposta di cui sopra di guardare al capitolo sulla elaborazione in batch.

Volevo anche aggiungere che è necessario assicurarsi che si carica solo ciò che è neccessary per i cambiamenti che è necessario fare per il prodotto.

Quello che voglio dire è che se il prodotto carica con impazienza un gran numero di altri oggetti che non sono importanti per questa transazione, dovresti considerare di non caricare gli oggetti uniti - accelererà il caricamento dei prodotti e dipende dalla loro persistenza strategia, potrebbe anche farti risparmiare tempo quando riattivi il prodotto.