GEDOPLAN
Jakarta EE (Java EE)

Hibernate Envers – Entity Auditing

Jakarta EE (Java EE)
hibernate envers title

Beim Entity Auditing geht es darum Veränderungen an unseren JPA-Objekten festzuhalten um Nachverfolgen zu können wie sich ein Datensatz im Laufe der Zeit verändert hat. Bereits seit der Hibernate Version 3.5 steht „Envers“ im HibernateCore-Module bereit und bietet Out-Of-The-Box eine eben solche Lösung.

Die Funktionsweise von Envers ist schnell erklärt: intern arbeitet Envers mit Listenern die während einem merge / persist / remove  aktiv werden und den Zustand vor der Aktion in einer separaten Tabelle festhalten. Dabei verwendet Envers so genannte Revisions, eine Art Versionsnummer für Entitäten die pro Transaktion generiert wird und anhand der die unterschiedlichen Stände der Entitäten ermittelt werden können.

Unsere hier gezeigten Beispiele zielen auf den Wildfly 10 als Laufzeitumgebung ab der die benötigen Bibliotheken bereits (in Version 5.0.7) mit bringt. Um Envers im Projekt ein setzen reicht demnach eine provided-Abhängigkeit in unserer pom.xml:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-envers</artifactId>
    <version>5.0.7.Final</version>
    <scope>provided</scope>
</dependency>

Um nun ganze Entitäten oder wahlweise nur einzelne Attribute der Historisierung zu unterwerfen reicht es  mittels „@Audited“ Annotation dieses Hibernate mit zu teilen:

Auditing
@Entity
@Table(name = "ORDERTBL")
@Audited
public class Order {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer orderID;

    @OneToMany(mappedBy = "order")
    @NotAudited
    private List<OrderDetail> orderDetails;

Das obige Beispiel zeigt die einfache Verwendung von Hibernate Envers. Mittels „@Audited“ markieren wir unsere Order-Entität für die Historisierung. Jegliche Transaktionen die Änderungen vornimmt führt nun zu einer neuen Revision. Dabei berücksichtigt Envers auch abhängige Objekte und würde diese, wenn nicht wie im obigen Beispiel mittels „@NotAudited“ annotiert, ebenfalls historisieren.

Neben der zu erwartenden Tabelle „ORDERTBL“ die unseren aktuellen Datensatz beinhaltet führt Envers nun eine weitere Tabelle mit dem default-Suffix „_AUD“ ein:

hibernate_envers

Hierbei handelt es sich um die Revisionstabelle für unsere Entität. Diese beinhaltet die Revisionnummer, einen Revisiontyp (ADD,MOD, DEL) und alle Daten der Entität, bzw. der Attribute die zur Historisierung markiert wurden. Eine entsprechende API zum Zugriff auf die Revisionsdaten stellt Envers ebenfalls bereit, dabei ist die Synatx sehr stark an die CriteriaAPI angelehnt.

Selektion der ersten Revision einer Bestellung mit bestimmter ID:

AuditReader auditReader = AuditReaderFactory.get(entityManager);
List<Number> revisions = auditReader.getRevisions(Order.class, order.getOrderID());
Order revOrder = auditReader.find(Order.class, order.getOrderID(), revisions.get(0));
Assert.assertTrue(revOrder.getShipName().equals(oldShipName));

Wer zum Teufel war das?

Eine häufige Anforderung ist ebenfalls das zu einer Revision zusätzliche Daten abgelegt werden sollen, z.B. das aktuelle Datum oder der Benutzer der die Änderung vollzogen hat. Hierzu bietet Envers ebenfalls eine Lösung. Zwei Klassen sind dazu zu implementieren

  • RevisonEntity
    • Entität welche die zusätzlichen Daten für jede Revision bereit hält
  • Revision Listener
    • Ermittlung der Daten
@Entity
@RevisionEntity(RevisionDataListener.class)
public class RevisionData extends DefaultRevisionEntity {

    @Temporal(TemporalType.TIMESTAMP)
    private Date changeDate;

    private String username;

    //Getter und Setter

}
public class RevisionDataListener implements RevisionListener {

    @Override
    public void newRevision(Object o) {
        RevisionData revData = (RevisionData) o;
        revData.setChangeDate(new Date());
        revData.setUsername(getUsername());
    }

    private String getUsername() {
    	return "some username"
    }

}

Jede Revision die erzeugt wird, durchläuft nun unserer Listener und wird mit den Informationen angereichert. Diese Informationen werden in einer separaten Tabelle mit entsprechender Referenz zur Revision in der Datenbank abgelegt und können mittels AuditReader ausgelesen werden:

hibernate_envers_revisiondata

// Revision-Objekt und zusätzliche Daten auf Basis der ID (8) und der Revision (55) ermitteln.
AuditQuery revQuery = auditReader.createQuery().forRevisionsOfEntity(Product.class, false, true);
revQuery.add(AuditEntity.revisionNumber().eq(55));
revQuery.add(AuditEntity.id().eq(8));

// liefert Objekt Array mit Entität, RevisionObjekt und RevisionType
Object[] revObject = (Object[]) revQuery.getSingleResult();

Product revProduct = (Product) revObject[0];
RevisionData revData = (RevisionData) revObject[1];
RevisionType revType = (RevisionType) revObject[2];

Fazit

Man sollte sich schon gut überlegen welche Daten wirklich dem Auditing unterworfen werden sollen, da dieser augenscheinlich so leichtgewichtige Ansatz natürlich bei schreibenden Operationen zu einem wesentlich höheren Aufwand auf Seiten der Datenbank führt. Davon abgesehen ist Envers eine sehr einfache Möglichkeit die Historisierung von Entitäten zu realisieren. Ein paar Annotationen reichen aus und Envers kümmert sich um den Rest.

Weite Informationen: Envers Docs

Github Projekt: https://github.com/GEDOPLAN/hibernate-envers

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Bitte füllen Sie dieses Feld aus.
Bitte füllen Sie dieses Feld aus.
Bitte gib eine gültige E-Mail-Adresse ein.
Sie müssen den Bedingungen zustimmen, um fortzufahren.

Autor

Diesen Artikel teilen

LinkedIn
Xing

Gibt es noch Fragen?

Fragen beantworten wir sehr gerne! Schreibe uns einfach per Kontaktformular.

Kurse

weitere Blogbeiträge

IT-Training - GEDOPLAN
Webprogrammierung

Angular, testen mit Karma/Jasmine

Anwendungen zu testen ist ein leidiges Thema und wird gerne aus Zeitgründen vernachlässigt. Jeder der schon mal ein größeres Refactoring…

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!