Massendatenverarbeitung mit Hibernate

In Datenbankanwendungen steht man oft vor der Aufgabe, viele Datensätze gleichzeitig zu verarbeiten, sogenannte Bulk-Updates. Beispiele dafür sind das Löschen von 1000 Datensätzen oder das Ändern einer einzigen Spalte in einer großen Datenmenge.

Ein naiver Ansatz wäre, alle Objekte in die Anwendung zu laden, dann die Änderungen durchzuführen und erneut abzuspeichern.

for (...) {
 Entity e = session.load(Entity.class, id);
 session.delete(e);
}

Das funktioniert bei wenigen Datensätzen gut. Vorteil ist auch, daß der Quellcode gut lesbar und damit auch wartbar ist. Sobald man aber mehr als eine Handvoll Objekte gleichzeitig verarbeiten möchte, ist dieser Ansatz nicht mehr performant.

Hauptursachen für die schlechte Performance ist einerseits das Laden der vielen Objekte, was zum einen zeitaufwändig (Netzwerklatenz und Übertragung von der Datenbank, Mapping des Resultsets) und auch sehr speicherintensiv ist. Andererseits verursacht das Defaultverhalten der Hibernate-Session (First-Level-Cache und Dirty Checking) bei einer großen Anzahl von Datensätzen einen erhöhten Management-Aufwand.

Letzteren Fall kann man durch den Einsatz der sogenannten StatelessSession verhindern. Die Stateless Session hält keinen Persistenzkontext und bietet viele der Features der normalen Session nicht an, sondern orientiert sich von den Eigenschaften eher an der darunterliegenden JDBC-Schicht. Von JDBC unterscheidet sie sich aber natürlich durch das objektrelationale Mapping.

Trotzdem wird der Einsatz der StatelessSession bei der Bearbeitung von Massendaten meist noch nicht performant genug sein, da weiterhin eine Vielzahl von Objekten in den Speicher geladen werden muß. Sinnvoller ist es, die Arbeit komplett in die Datebank zu verlagern. Die Datenbank ist für solche Aktionen optimiert und wird Bulk-Updates performant durchführen. Gleichzeitig muß die Anwendung keine Daten in den Speicher laden und auch kein objektrelationales Mapping durchführen. In Hibernate kann man mit HQL die Operationen sogar auf Objektebene durchführen, muß also nicht mit SQL, Tabellen- und Spaltennamen arbeiten.

String hql = "delete from Entity where id between ...";
session.createQuery(hql).execute();
String hql = "from Entity e where e.name in ('Foo', 'Bar', ... )";
session.createQuery(hql).execute();
Short URL for this post: http://wp.me/p4nxik-fl
This entry was posted in Java Persistence and tagged , . Bookmark the permalink.