web-dev-qa-db-ger.com

Masseneinsatz oder Update mit Hibernate?

Ich muss ziemlich große Datenmengen aus einer täglichen CSV-Datei verbrauchen. Der CSV enthält rund 120.000 Datensätze. Bei Verwendung des Ruhezustands verlangsamt sich dies zu einer Durchforstung. Im Grunde scheint es, als würde der Ruhezustand vor jedem einzelnen INSERT (oder UPDATE) bei Verwendung von saveOrUpdate () ein SELECT ausführen. Für jede Instanz, die mit saveOrUpdate () beibehalten wird, wird vor dem eigentlichen INSERT oder einem UPDATE ein SELECT ausgegeben. Ich kann verstehen, warum das so ist, aber es ist für die Massenverarbeitung äußerst ineffizient und ich suche nach Alternativen

Ich bin zuversichtlich, dass das Leistungsproblem in der Art und Weise liegt, wie ich den Hibernate-Modus verwende, da ich eine andere Version habe, die mit nativem SQL arbeitet (das die CSV auf die gleiche Weise parst) und deren wörtliche Kreise um diese neue Version herumlaufen.

Gibt es für die eigentliche Frage eine Hibernat-Alternative zur mysqls-Syntax "INSERT ... ON DUPLICATE"?

Wenn ich mich dazu entschließe, native SQL zu verwenden, kann ich native SQL innerhalb einer Winterschlaf-Transaktion ausführen? Das heißt, wird es Commit/Rollbacks unterstützen?

19
JustDanyul

Gemäß einer Antwort auf eine ähnliche Frage können Sie dies tun, indem Sie Hibernate konfigurieren, um Objekte mit einer benutzerdefinierten gespeicherten Prozedur einzufügen , die die upsert - Funktionalität Ihrer Datenbank verwendet. Es ist aber nicht hübsch.

5
Tom Anderson

Es gibt viele mögliche Engpässe bei Massenvorgängen. Die beste Vorgehensweise hängt stark davon ab, wie Ihre Daten aussehen. Schauen Sie sich den Abschnitt Hibernate Manual über die Stapelverarbeitung an.

Stellen Sie mindestens sicher, dass Sie das folgende Muster verwenden (aus dem Handbuch kopiert):

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

for ( int i=0; i<100000; i++ ) {
Customer customer = new Customer(.....);
session.save(customer);
    if ( i % 20 == 0 ) { //20, same as the JDBC batch size
        //flush a batch of inserts and release memory:
        session.flush();
        session.clear();
    }
}

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

Wenn Sie eine unstrukturierte Datei einem sehr komplexen Objektdiagramm zuordnen, müssen Sie möglicherweise kreativer werden. Das grundlegende Prinzip ist jedoch, dass Sie ein Gleichgewicht finden müssen, bei dem Datenblöcke von guter Größe mit jedem Flush/Commit in die Datenbank verschoben und vermieden werden Explodieren der Größe des Cache auf Sitzungsebene.

Wenn Sie keine Ruhezustände benötigen, um Sammlungen oder Kaskadierungen für das korrekte Einfügen Ihrer Daten auszuführen, sollten Sie eine StatelessSession verwenden.

31
jcwayne

Von Hibernate Batch Processing Für das Update habe ich Folgendes verwendet:

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

ScrollableResults employeeCursor = session.createQuery("FROM EMPLOYEE")
                                   .scroll();
int count = 0;

while ( employeeCursor.next() ) {
   Employee employee = (Employee) employeeCursor.get(0);
   employee.updateEmployee();
   seession.update(employee); 
   if ( ++count % 50 == 0 ) {
      session.flush();
      session.clear();
   }
}
tx.commit();
session.close();

Aber für Einfügen würde ich für jcwayne Antwort gehen

3
shareef

Wenn Sie eine Sequenz oder einen nativen Generator verwenden, verwendet Hibernate eine Auswahl, um die ID zu erhalten:

<id name="id" column="ID">
    <generator class="native" />
</id>

Verwenden Sie den hilo oder den seqHiLo Generator:

<id name="id" type="long" column="id">  
    <generator class="seqhilo">
        <param name="sequence">SEQ_NAME</param>
        <param name="max_lo">100</param>
    </generator>
</id>
1
Gabriel

Wenn Sie nur Daten ohne Verarbeitung oder Umwandlung importieren möchten, ist ein Tool wie PostgreSQL COPY der schnellste Weg, Daten zu importieren.

Wenn Sie jedoch Transformation, Datenaggregation, Korrelation/Zusammenführung zwischen vorhandenen und eingehenden Daten durchführen müssen, benötigen Sie eine Stapelverarbeitung auf Anwendungsebene.

In diesem Fall möchten Sie, wie ich in diesem Artikel erklärt habe, regelmäßig ein Flush-Clear-Commit ausführen:

int entityCount = 50;
int batchSize = 25;

EntityManager entityManager = entityManagerFactory()
    .createEntityManager();

EntityTransaction entityTransaction = entityManager
    .getTransaction();

try {
    entityTransaction.begin();

    for (int i = 0; i < entityCount; i++) {
        if (i > 0 && i % batchSize == 0) {
            entityTransaction.commit();
            entityTransaction.begin();

            entityManager.clear();
        }

        Post post = new Post(
            String.format("Post %d", i + 1)
        );

        entityManager.persist(post);
    }

    entityTransaction.commit();
} catch (RuntimeException e) {
    if (entityTransaction.isActive()) {
        entityTransaction.rollback();
    }
    throw e;
} finally {
    entityManager.close();
}

Stellen Sie außerdem sicher, dass Sie auch das JDBC-Batching mithilfe der folgenden Konfigurationseigenschaften aktivieren:

<property
    name="hibernate.jdbc.batch_size"
    value="25"
/>

<property
    name="hibernate.order_inserts"  
    value="true"
/>

<property
    name="hibernate.order_updates"  
    value="true"
/>

Weitere Informationen zu diesen Hibernate-Konfigurationseigenschaften finden Sie unter diesem Artikel .

1
Vlad Mihalcea

Die Option "Extra" dient dazu, die eindeutige Kennung für Ihre Daten zu generieren.

Wechseln Sie zur HiLo-Sequenzgenerierung, und Sie können die Sequenzrundfahrten zur Datenbank um die Nummer der Zuweisungsgröße reduzieren. Bitte beachten Sie, dass es eine Lücke in den Primärschlüsseln gibt, sofern Sie nicht Ihren Sequenzwert für den HiLo-Generator anpassen

0
szucsz