Historisierung & Versionierung mit JPA

Zusammenfassung

Die meisten Java Anwendungen haben ein Domain-Modell. Dieses ist mit JPA-Entities abgebildet. Manche oder alle Entitäten besitzen eine Gültigkeit (von, bis) und werden so historisiert (Historisierung). Zudem werden einzelne Objekte während der Anwendungslaufzeit verändert, und jede jemals vorhanden Objektversion muss zu jeder Zeit wieder herstellbar sein (Versionierung). Hier wird ein allgemeingültiger Lösungsansatz beschrieben.

Versionierung

Sobald Hibernate im Projekt verwendet wird, kann JBoss Envers in den Classpath aufgenommen und die zu versionierenden Entitäten annotiert werden. Fertig. Zusätzlich muss noch das Datenbankschema angepasst werden.

Wie funktioniert Envers?

Zu jeder JPA-Entität gibt es im Normalfall eine Datenbanktabelle – soweit so klar. Mit JBoss Envers gibt es zu der Entität noch eine zweite Datenbanktabelle (Revisionstabelle), die über die gleichen Spalten wie die erste verfügt aber zusätzlich noch zwei weitere Spalten hat (REV und REVTYPE). In REV steht eine global eindeutige Revisionsnummer (Zahl beginnend bei 1, monoton steigend). In REVTYPE steht einer der Werte {add, mod, del}.

Wird nun eine Instanz einer Entität und damit ein Datensatz in der ersten Tabelle über den EntityManager / Hibernate’s Session verändert, sei es Hinzufügen (EntityManager#persist()), Ändern (EntityManager#update()) oder Löschen (EntityManager#delete()), dann spiegelt sich die Änderung nicht nur in der zur Entität gehörenden Datenbanktabelle wieder, sondern auch in einem zusätzlichen Eintrag in der Revisionstabelle. Für jede Änderung an einer Instanz gibt es in der Revisionstabelle einen neuen Eintrag. Bei einem EntityManager#update() werden in den neuen Eintrag der Revisionstabelle nur die geänderten Attribute aufgenommen, der Rest bleibt leer. Bei EntityManager#persist() und EntityManager#delete() werden alle Attribute der Entität übernommen. In der Spalte REVTYPE steht jeweiliges der Typ der Änderungsoperation.

Es versteht sich von selbst, dass in der Revisionstabelle keine Foreign-Key-Beziehungen und NOT NULL Constraints aktiviert sein dürfen.

JBoss Envers bietet mit AuditQueries die Möglichkeit, die verschiedenen Revisionen zu einer Instanz einer Entität abzufragen.

Voilà.

Historisierung

Instanzen einer Entität haben zeitlich gesehen, Bereiche in denen sie gültig sind, sogenannte Zeitscheiben. To be continued.