2010-03-07 3 views
7

Ich verwende Hibernate, um 20K-Produkte in meiner Datenbank zu aktualisieren.Verwenden von Hibernate zum Laden von 20K-Produkten, Ändern der Entität und Aktualisieren auf db

Ab jetzt ziehe ich die 20K Produkte ein, durchschlinge sie und modifiziere einige Eigenschaften und aktualisiere dann die Datenbank.

so:

load products 

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

Ab sofort Dinge ziemlich langsam sind im Vergleich zu Standard jdbc, was kann ich die Dinge zu beschleunigen tun, um nach oben?

Ich bin mir sicher, dass ich hier etwas falsch mache.

+0

Hast du wirklich * N * Hibernate (im Titel) gemeint? – M4N

+0

Nicht für Java - NHibernate ist für .NET. – duffymo

+0

behoben den Titel danke. – Blankman

Antwort

9

am richtigen Ort suchen in der Dokumentation für diese Art der Behandlung ist das ganze Chapter 13. Batch processing.

Hier gibt es einige offensichtliche Fehler in Ihrem aktuellen Ansatz:

  • Sie sollten die Transaktion nicht für jedes Update starten/commit.
  • sollten Sie JDBC Dosierung ermöglichen und es auf eine vernünftige Anzahl eingestellt (10-50):

    hibernate.jdbc.batch_size 20 
    
  • sollten Sie flush() und dann clear() die Sitzung in regelmäßigen Abständen (alle n Datensätze, in denen n gleich der hibernate.jdbc.batch_size Parameter) oder es wird weiter wachsen und kann irgendwann explodieren (mit einem OutOfMemoryException).

Unten das Beispiel im Abschnitt 13.2. Batch updates illustriert dies:

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(); 

Sie auch die StatelessSession mit berücksichtigen kann. Eine andere Option wäre DML-style operations (in HQL!): UPDATE FROM? EntityName (WHERE where_conditions)?.Dies ist die HQL UPDATE Beispiel:

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(); 

Auch hier beziehen sich auf die Dokumentation der Daten (vor allem, wie mit den version oder timestamp Eigenschaftswerte mit dem VERSIONED Schlüsselwort zu behandeln).

5

Wenn diese Pseudo-Code ist, würde ich empfehlen die Transaktion außerhalb der Schleife zu bewegen oder zumindest eine doppelte Schleife, wenn in einer einzigen Transaktion alle 20K Produkte mit zu viel ist:

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

Auch , Ich würde empfehlen, dass Sie Ihre UPDATEs statt einzeln an die Datenbank senden. Es gibt zu viel Netzwerkverkehr auf diese Weise. Bündeln Sie jeden Chunk in einen einzigen Stapel und senden Sie alle auf einmal.

0

Die schnellste Möglichkeit, eine Stapelaktualisierung durchzuführen, besteht darin, sie in eine einzige SQL-Anweisung umzuwandeln und sie als rohe SQL in der Sitzung auszuführen. So etwas wie

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

Gelingt das nicht, können Sie versuchen zu weniger Transaktionen zu machen und tun Updates in Chargen:

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 

Für weitere Informationen sehen Sie die Hibernate Community Docs

1

Ich stimme der obigen Antwort zu, wenn ich das Kapitel zur Stapelverarbeitung betrachte.

Ich wollte auch hinzufügen, dass Sie sicherstellen sollten, dass Sie nur laden, was für die Änderungen notwendig ist, die Sie für das Produkt machen müssen.

Was ich meine ist, wenn das Produkt eifrig eine große Anzahl anderer Objekte lädt, die für diese Transaktion nicht wichtig sind, sollten Sie nicht die verbundenen Objekte laden - es wird das Laden von Produkten beschleunigen und abhängig von ihrer Persistenz Strategie, können Sie auch Zeit sparen, wenn Sie das Produkt wieder hartnäckig machen.

Verwandte Themen