CodeGym /Kurse /Hibernate deep-dive /Auswahl von Entitäten und Feldern für das Audit

Auswahl von Entitäten und Feldern für das Audit

Hibernate deep-dive
Level 21, Lektion 2
Verfügbar

1. Risiken eines totalen Audits

Der erste Impuls nach der Bekanntschaft mit Envers ist meist: „Super! Jetzt weiß ich, wer, wann und was geändert hat, ich setze @Audited auf alle Entitäten – und bin glücklich.“ Das ist ungefähr so, als würde man das Logging auf TRACE-Ebene in allen Paketen einschalten und sich freuen, dass „jetzt alles sichtbar ist“. Sichtbar wird es schon sein, aber Sie selbst werden es später nicht mehr lesen wollen.

Beginnen wir mit der bodenständigsten Sache: Envers bedeutet zusätzliche Einträge in der Datenbank. Wenn eine audited-Entität geändert wird, führt Hibernate ein normales UPDATE (oder INSERT/DELETE) in der Haupttabelle durch und schreibt zusätzlich in die Historie. Das bedeutet mehr SQL, mehr Daten, mehr Indizes, mehr Speicherplatz. In einem Testprojekt ist das in Ordnung, aber selbst in einem Übungslabor werden Sie merken, dass das SQL-Log „wortreicher“ wird.

Das zweite Problem ist Rauschen. Die Historie ist nützlich, wenn man sie lesen und verstehen kann. Wenn Sie alles Mögliche auditieren, werden die Revisionen Änderungen an „Dienstfeldern“, technischen Flags, irgendwelchen internen Zählern und Feldern enthalten, die sich häufig und fast immer automatisch ändern. Sie öffnen die Historie eines Produkts und sehen 200 Revisionen, von denen 180 „updatedAt aktualisiert“ oder „irgendeinen technischen Indikator neu berechnet“ sind. Formal gibt es eine Historie. Praktisch ist sie wie ein großer Ordner „download (37)“ auf dem Desktop: Die Dateien sind wichtig, aber es ist schwer, damit zu leben.

Das dritte Problem sind die Kosten des Lesens der Historie. Selbst ohne AuditReader ist es wichtig zu verstehen: Die Historie wird später entweder als Liste von Revisionen oder als „Zustandsaufnahmen“ für jede Revision gelesen. Wenn Sie bei jeder Revision 30 Spalten speichern, darunter große Textfelder, und es ändert sich nur ein kleines, dann speichern Sie „Luftarchiv“. Envers speichert Aufnahmen, keine Diffs. Das ist praktisch für die Wiederherstellung des Zustands, aber teuer, wenn Sie nicht einschränken, was Sie als „wichtigen Zustand“ betrachten.

Es gibt auch einen rein methodischen Grund. Der Kurs „Hibernate deep-dive“ lehrt Sie, die Verbindung zwischen Java-Code, Persistenzkontext und SQL zu sehen. Wenn wir das Audit für „alles“ einschalten, erscheinen im SQL viele zusätzliche Einfügungen, und einem Anfänger fällt es schwerer zu verstehen, was in diesem Strom zur Geschäftsoperation und was zur infrastrukturellen Historie gehört. Daher sollte das Audit im Übungsprojekt dosiert sein: Wir schalten es dort ein, wo es wirklich hilft, das Verhalten zu analysieren.

2. Historische Fragen und die Wahl des Audits

Bevor wir über @Audited und Annotationen diskutieren, ist es sinnvoll, einen Schritt zurückzutreten und zu fragen: „Welche historischen Fragen brauchen wir überhaupt?“ Das klingt banal, aber genau dieser Schritt wird meistens übersprungen. Und dann bekommt man „Audit um des Audits willen“: Das Schema ist aufgebläht, SQL ist gewachsen, aber die Historie wird von niemandem genutzt, weil unklar ist – was soll man dort suchen?

Ein gutes Audit beginnt fast immer mit einem Satz wie: „Wir müssen in der Lage sein, die Frage zu beantworten …“ und dann kommt die konkrete Frage. Für die Domäne Commerce Persistence Lab sind solche Fragen sehr natürlich. Zum Beispiel: „Welchen Preis hatte das Produkt vor der Erhöhung?“, „Wann wurde das Produkt ausgeblendet (Soft Delete) und wann wieder eingeblendet?“, „In welcher Reihenfolge änderten sich die Bestellstatus?“. Das sind keine Fragen nach dem „letzten Zeitstempel“, sondern nach der Abfolge von Zuständen.

Es ist praktisch, die Fragen gedanklich in zwei Typen zu unterteilen. Es gibt Fragen nach der „Änderungstatsache“: „Wann wurde überhaupt geändert?“. Dafür reicht manchmal updatedAt (nicht immer, aber manchmal). Und es gibt Fragen nach dem „Inhalt der Vergangenheit“: „Was genau war vor der Änderung?“. Dafür ist updatedAt machtlos: Es weiß nur „wann“, aber nicht „was“.

Halten wir das in einer kleinen Tabelle fest (die Tabelle ist unsere freundliche Art, auf Listen zu verzichten, damit das Gehirn es leichter hat):

Historische Frage Was gespeichert werden muss Typischer Kandidat für Envers
„Welchen Preis hatte das Produkt vor der Änderung?“ vergangene Werte von price Product (Preis)
„In welcher Reihenfolge änderten sich die Bestellstatus?“ Abfolge von status über die Zeit PurchaseOrder (Status)
„Wann wurde das Produkt logisch ausgeblendet/gelöscht?“ vergangener Wert des Flags deleted Product (Soft-Delete-Zustand)
„Welche Bestände gab es vor einer Minute?“ sehr häufige Mengenänderungen normalerweise nicht Envers, sondern ein separates Modell (in unserem Kurs machen wir es nicht)

Beachten Sie die letzte Zeile. Sie ist wichtig: Nicht alles, was „als Historie gespeichert werden kann“, ist bequem über Envers zu speichern. Wenn ein Feld sehr häufig geändert wird, kann Envers zu einem „Archivgenerator“ werden, in dem man die nützlichen Informationen erst mit der Schaufel ausgraben muss.

Ein weiterer nützlicher Test ist: „Stellen Sie sich einen Bildschirm/Bericht vor“. Wenn Sie sich leicht vorstellen können, wie die Historie einem Menschen angezeigt wird (oder wie eine Untersuchung damit durchgeführt wird), ist das ein gutes Zeichen. Wenn Sie sich nicht vorstellen können, wie und warum Sie die Historie lesen werden, dann erzeugen Sie höchstwahrscheinlich nur Rauschen.

3. Kriterien: Wert, Häufigkeit, Breite

Jetzt nähern wir uns der Wahl der Audit-Grenzen als einer normalen technischen Entscheidung. Es gibt kein magisches „richtig/falsch“, aber es gibt sehr praktische Kriterien. Um nicht den Überblick zu verlieren, schlage ich vor, drei Achsen im Kopf zu behalten: Wert, Änderungshäufigkeit und Datenbreite (wie viele und welche Daten Sie in jeder Aufnahme speichern).

Beginnen wir mit dem „Wert“. Wert bedeutet nicht „die Entität ist wichtig“, sondern „die Historie dieser Entität wird tatsächlich gelesen“. Ist das Produkt (Product) wichtig? Ja. Aber wird jemand die Historie lesen: „Wie oft hat sich das interne Feld lastReindexedAt geändert“? Eher nicht. Die Historie des Preises dagegen schon.

Die Änderungshäufigkeit ist Ihr zukünftiges Datenvolumen. Wenn Sie InventoryItem.availableQty haben und es bei jeder Reservierung geändert wird, dann verwandelt sich das Audit in einen Strom kleiner Revisionen. Formal ist das „Historie“, aber aus Produktsicht ähnelt es eher Telemetrie. In einem Übungsprojekt brauchen wir eine Historie von Ereignissen, nicht eine „Blackbox“ des Flugzeugs mit Daten jede Sekunde.

Die Datenbreite ist ein sehr unterschätzter Punkt. Envers speichert Aufnahmen. Wenn Sie eine Entität mit einem großen Text auditieren, wird dieser Text in der Audit-Tabelle bei jeder Revision wiederholt, auch wenn sich nur der Status geändert hat. Stellen Sie sich vor: Das Feld technicalSpecs mit 20 KB und der Status ändert sich 5 Mal. Sie speichern dieselben 20 KB fünf Mal. Die Datenbank wird es zwar verkraften (sie ist geduldig), aber Sie werden sich später über die Größe von *_AUD wundern.

Damit das nicht nur Theorie bleibt, hier eine kleine „Entscheidungsmatrix“. Sie erhebt keinen Anspruch auf absolute Wahrheit, funktioniert aber als schneller Filter:

Wert der Historie Änderungshäufigkeit Typische Lösung
hoch niedrig/mäßig Envers bestens geeignet
hoch hoch Vorsicht: Envers möglich, aber Volumen und Rauschen nehmen zu
niedrig beliebig Meistens kein Audit
mittel niedrig Selektives Audit (nur Felder) oder nur Aggregate

Und nun ein kleines Flussdiagramm, damit das Gehirn eines Anfängers die Entscheidung wie einen Algorithmus durchlaufen kann, nicht wie eine Philosophie:

flowchart TD
    %% Schnellfilter: Frage vorhanden → Häufigkeit → und erst dann Entscheidung über Envers.
    A["Entität/Feld"] --> B{"Gibt es eine klare historische Frage?"}
    B -- "nein" --> C["Kein Audit"]
    B -- "ja" --> D{"Wie oft ändert es sich?"}
    D -- "selten/mäßig" --> E["Mutig auditieren"]
    D -- "häufig" --> F{"Wird die Historie für Menschen/Untersuchungen gebraucht, nicht für Telemetrie?"}
    F -- "nein" --> C
    F -- "ja" --> G["Selektiv auditieren und Volumen überwachen"]

Im Übungsprojekt Commerce Persistence Lab werden wir meistens in den Zweig „selten/mäßig → mutig auditieren“ fallen. Das hilft, den Fokus zu halten: Envers als verständliche, lesbare Historie des Geschäftszustands.

4. Entitäten für das Audit im Labor

Wenden wir nun die Kriterien auf unser Projekt an. Wir sind nicht im luftleeren Raum: Wir haben konkrete Entitäten, konkrete Szenarien und ein konkretes Tagesziel – zu lernen, wie man die Historie des Produktpreises und des Bestellstatus liest. Daher wird die Wahl nicht „ideal für alle Unternehmen der Welt“ sein, sondern „richtig für ein Übungslabor und logisch für ein echtes Backoffice“.

Ich erinnere an den Kernpunkt: In diesem Teil des Kurses bauen wir kein „totales Audit von allem“. Wir bauen ein Audit, das später bequem zu lesen und zu diskutieren ist und unser Schema nicht in ein archäologisches Museum verwandelt.

Schauen wir uns die wichtigsten Entitäten an:

Entität Audit im Rahmen des Tages? Warum das sinnvoll ist
Product ja Preis und Status sind klassische historische Fragen; Soft Delete ist ebenfalls nützlich über die Zeit zu sehen
PurchaseOrder ja Bestellstatus – eine zentrale Geschäftsspur; Entwicklung der Bestellung bequem nachvollziehbar
OrderItem eher nicht Historie der Bestellzusammensetzung kann nützlich sein, ist aber hier nicht das Ziel; leichtes Aufblähen
InventoryItem nein Häufige Änderungen (Reservierungen/Freigaben), Historie wäre verrauscht und riesig
Customer nein Audit von E-Mail/Status möglich, bringt heute aber keinen Nutzen und fügt Tabellen hinzu
ProductDetails nein Dort meist große Textfelder; für Preis-/Status-Historie überflüssig
Category normalerweise nein Historische Umbenennungen möglich, in unserem Szenario sekundär
ProductCategoryAssignment nein Änderungen möglich, aber derzeit nicht Ziel; zudem Link-Entität mit eigenen Nuancen

Beachten Sie das „eher nicht“ bei OrderItem. Das liegt nicht daran, dass es „nicht erlaubt“ wäre. Sondern daran, dass das Audit nur dann eingeführt werden sollte, wenn eine verständliche historische Frage auftaucht. Hier lauten die Fragen: „Wie änderte sich der Produktpreis“ und „Wie änderte sich der Bestellstatus“. Die Historie der Bestellzusammensetzung ist ein separates Thema, das schnell dazu führt, wie Envers Änderungen an Kollektionen speichert. Das ist nützlich, aber dort gibt es eine ganz andere Menge an Nuancen.

Ein weiterer wichtiger Punkt: InventorySnapshot (falls es im Projekt nach dem Kursplan bereits vorhanden ist) ist von Natur aus historisch. Es wurde als „Aufnahme zu einem Datum“ erstellt. Es mit Envers zu auditieren, wäre wie ein Fotoalbum zu fotografieren, um die Fotos nicht zu verlieren. Formal möglich, aber sinnlos.

Und ja, der „schmerzhafteste“ Kandidat ist InventoryItem. Studierende haben oft den Drang, „Bestände zu auditieren, weil das wichtig ist!“. Es ist wichtig, aber es ist eine andere Datenkategorie. Bestände ändern sich häufig, und das Audit verwandelt sich in einen Strom. Für diese Art von Aufgaben verwendet man normalerweise ein separates Event-Log oder separate Snapshots. In unserem Kurs gehen wir nicht dorthin, um den Fokus nicht zu verlieren.

5. Audit der Klasse und der Felder

Okay, wir haben die Entitäten ausgewählt. Aber die zweite Hälfte der Entscheidung ist noch wichtiger: Was genau innerhalb der Entität wollen wir in der Historie speichern? Envers bietet hier zwei grundlegende Stile, und die Wahl des Stils beeinflusst sowohl die Größe von *_AUD als auch die Lesbarkeit historischer Aufnahmen.

Der erste Stil – „Audit der gesamten Entität“: Setzen Sie @Audited auf die Klasse. Dann betrachtet Envers die Historie als für die gesamte Entität erforderlich und speichert die Werte aller persistenten Felder (außer denen, die Sie explizit ausschließen). Dieser Stil ist gut, wenn Sie die Entität „so wie sie war“ zu einem bestimmten Zeitpunkt wiederherstellen möchten.

Der zweite Stil – „Audit nur der relevanten Felder“: Setzen Sie @Audited nur auf bestimmte Eigenschaften. Das reduziert Rauschen und die Breite der Audit-Tabelle, aber es ist wichtig zu bedenken: Die historische Aufnahme enthält nur die Werte der auditierten Felder. Die übrigen Felder können beim Lesen der Historie null oder Standardwerte sein (und das ist kein Fehler, sondern eine logische Konsequenz daraus, dass diese Daten nicht in der Historie gespeichert wurden).

Um Verwirrung zu vermeiden, vergleichen wir die Ansätze in einer Tabelle:

Ansatz Vorteile Nachteile Wann oft geeignet
@Audited auf der Klasse vollständige Aufnahme, einfachere Wiederherstellung des vergangenen Zustands breitere Tabelle, mehr Rauschen, leichtes Auditieren von Überflüssigem PurchaseOrder, wichtige Aggregate
@Audited auf Feldern enge Historie, weniger Rauschen, nur das Nötige gespeichert unvollständige Aufnahme; Lesen erfordert Disziplin („erwarte keine Felder, die nicht vorhanden sind“) Product für Preis-/Status-Historie

Product: Selektives Audit des Zustands

Für die Historie des Produktpreises möchte man normalerweise nicht alles sehen, sondern genau das, „weswegen wir gekommen sind“: Preis und Status. In unserem Projekt ist der Preis Money und der Status ein Enum. Zusätzlich ist es nützlich, das Flag Soft Delete zu auditieren, damit in der Historie ersichtlich ist, „wann das Produkt ausgeblendet wurde“.

import com.example.commerce.common.jpa.Money;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.envers.Audited;

@Entity
public class Product {
    // Die ID ist ebenfalls Teil des Modells, aber in die Historie kommen wir nicht dafür.
    @Id private Long id;

    // Wir auditieren nur den Geschäftszustand, den wir tatsächlich in der Historie lesen möchten.
    @Audited private Money price;
    @Audited private ProductStatus status;

    // Soft Delete – ebenfalls Geschäftszustand: wichtig zu verstehen, wann das Produkt „verschwunden“ und „zurückgekehrt“ ist.
    @Audited private boolean deleted;
}

Es sieht einfach aus, aber die Bedeutung ist stark: Sie sagen von vornherein: „Die Historie dieses Produkts ist für uns die Historie seines Geschäftszustands, nicht sein ganzes Leben bis zum letzten Leerzeichen im Namen.“ Wenn später name oder sku in die Historie aufgenommen werden müssen, tun Sie das bewusst, aufgrund einer echten Frage.

PurchaseOrder: Audit ohne Positionen

Für eine Bestellung (PurchaseOrder) ist die Historie meist wichtiger und „breiter“: Status, Lieferadresse (bei uns eine Aufnahme von Address), Summe. Aber die Positionen (items) werden oft zur Quelle explosionsartigen Wachstums der Historie: Sie ändern sich, werden hinzugefügt, gelöscht, plus Envers beginnt, Änderungen an Kollektionen zu speichern. Heute ist für uns der Status wichtig, nicht die „vollständige Buchhaltung der Änderungen jeder Position“.

import jakarta.persistence.*;
import org.hibernate.envers.*;
import java.util.List;

@Entity
@Audited // Audit des „Kopfs“ der Bestellung: nützlich für die Historie der Status und Schlüsselfelder.
public class PurchaseOrder {
    @Id private Long id;

    // Das Feld wird in die Aufnahmen einfließen, weil wir die gesamte Klasse auditieren.
    private OrderStatus status;

    // Kollektionen schließen wir aus, damit Envers nicht beginnt, die Historie der Positionszusammensetzung zu speichern.
    @NotAudited
    @OneToMany(mappedBy = "order")
    private List<OrderItem> items;
}

Wichtig ist hier nicht, dass „die Positionen unwichtig sind“. Wichtig ist, dass wir das Audit auf ein verständliches Szenario begrenzen: die Historie des Bestellstatus. Das Audit von Kollektionen ist ein separates Thema, und wir möchten nicht, dass es hier als Nebeneffekt hereinplatzt.

6. Technisches Rauschen: Timestamps und große Felder

Wenn Sie in einem realen Projekt ein Audit durchführen, werden die meisten Probleme nicht durch die „wichtigen Geschäftsfelder“ verursacht, sondern durch kleine Dienst-Utilities. Sie sehen harmlos aus, aber genau sie verwandeln die Historie in Rauschen. Daher ist es nützlich, solche Felder frühzeitig erkennen zu können und zu verstehen, wie Envers darauf reagiert.

Das klassischste Beispiel ist updatedAt. Wenn Sie updatedAt speichern und es bei jedem UPDATE aktualisieren und die Entität auditiert ist, dann wird updatedAt in die Historie gelangen (wenn Sie es nicht ausschließen). Auf den ersten Blick scheint es nützlich: „Wir werden wissen, wann aktualisiert wurde“. Aber Envers hat bereits den Revisions-Zeitstempel in REVINFO. Das ergibt „zwei Zeitmarken pro Ereignis“, und nicht selten beginnen sie miteinander zu konkurrieren (besonders wenn updatedAt irgendwo implizit gesetzt wird).

Die praktische Schlussfolgerung ist: Wenn Sie Envers als primäre Historie verwenden, wird updatedAt sehr oft zu einer „weiteren Wahrheitsquelle, die auseinanderdriften kann“. Entweder schließen Sie updatedAt vom Audit aus oder überdenken grundsätzlich seine Notwendigkeit in auditierten Entitäten.

So sieht es im Code aus, wenn wir die gesamte Entität auditieren möchten, aber ein Dienstfeld entfernen:

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import org.hibernate.envers.Audited;
import org.hibernate.envers.NotAudited;

import java.time.Instant;

@Entity
@Audited // Audit der gesamten Entität als Aufnahme.
public class Product {
    @Id private Long id;

    private ProductStatus status;

    // Dieses Dienstfeld wird häufig aktualisiert und fügt keine nützliche „lesbare Historie“ hinzu.
    @NotAudited
    private Instant updatedAt;
}

Eine weitere Quelle von Rauschen sind große Felder (Texte, JSON, Spezifikationen). In unserem Projekt ist dafür sehr bequem die separate Entität ProductDetails vorgesehen. Und das betrifft nicht nur Lazy Loading und Fetching, sondern auch das Audit: Sie können ProductDetails nicht auditieren und dadurch große Texte in *_AUD vermeiden.

Nun ein wichtiger Punkt zu „fast unveränderlichen“ Daten. Zum Beispiel ist sku eines Produkts oft eindeutig und sollte sich grundsätzlich nicht ändern. In diesem Fall bringt das Audit von sku fast nichts: Die Historie dazu wird langweilig sein (im guten Sinne). Daher kann man sku auch nicht auditieren, wenn Ihr Historienszenario „Preis und Status“ ist. Wenn Sie jedoch möchten, dass die historische Aufnahme eines Produkts für einen Menschen autarker ist („Was ist das überhaupt für ein Produkt?“), dann kann das Audit von sku und name gerechtfertigt sein. Wieder zurück zur Hauptregel: Die Entscheidung wird für das Lesen der Historie getroffen, nicht für die Schönheit der Annotationen.

Und schließlich Soft Delete. Wenn Sie bereits Soft Delete implementiert haben, wird das Flag deleted Teil des Geschäftszustands. Wenn Sie die Frage „Wann wurde das Produkt ausgeblendet?“ beantworten möchten, sollten Sie dieses Flag in das Audit aufnehmen. Wenn Sie es nicht aufnehmen, gibt es zwar eine Preishistorie, aber die Sichtbarkeitshistorie des Produkts verschwindet, und später werden Sie Verschwörungstheorien darüber aufstellen, warum das Produkt verschwunden ist.

7. Assoziationen und Kollektionen: Grenzen des Audits

Ein Hibernate-Projekt ohne Assoziationen ist wie ein Online-Shop ohne Produkte: theoretisch möglich, aber es sieht verdächtig aus. Daher müssen wir bei der Wahl der Audit-Grenzen bedenken: Sie auditieren fast nie „ein einzelnes Feld im Vakuum“. Sie auditieren eine Entität, und diese hat Verweise auf andere Entitäten und Kollektionen. Und Envers kann beginnen, Ihnen so aktiv zu „helfen“, dass Sie den Preis dieser Hilfe nicht sofort verstehen.

Für to-one-Assoziationen (z. B. PurchaseOrder -> Customer) ist die grundlegende Logik einfach: Wenn Sie die Bestellung auditieren und nichts Besonderes tun, wird in der Audit-Tabelle normalerweise der Fremdschlüssel (customer_id) als Teil der Aufnahme gespeichert. Aber es stellt sich die Frage: „Müssen wir den Kunden selbst auditieren?“ Für die Status-Historie der Bestellung reicht es normalerweise, customer_id zu kennen. Wir müssen nicht unbedingt die Historie des Kunden im selben Mechanismus speichern, sonst beginnt das Audit auszuufern.

In Envers gibt es eine Einstellung, mit der man sagen kann: „Entität wird auditiert, aber das verknüpfte Objekt selbst nicht auditieren.“ Auf grundlegender Ebene ist es nützlich zumindest zu wissen, dass dies möglich ist, um nicht in die Falle zu tappen: „Da wir die Bestellung auditieren, müssen wir auch alle verknüpften Entitäten auditieren.“

import jakarta.persistence.ManyToOne;
import org.hibernate.envers.Audited;
import org.hibernate.envers.RelationTargetAuditMode;

// In der Audit-Aufnahme der Bestellung speichern wir den Verweis (FK), aber die Historie von Customer führen wir separat nicht.
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
@ManyToOne
private Customer customer;

Kommen wir nun zum Gefährlichsten – Kollektionen (OneToMany, ManyToMany). Wenn Sie eine Entität auditieren, kann Envers beginnen, Änderungen an Kollektionen als separate Art historischer Daten zu verfolgen. Das kann zur Entstehung zusätzlicher Audit-Tabellen, zu mehr Einfügungen und zu einem komplexeren Lesen der Historie führen. Und das ist nicht „schlecht“ – es ist einfach der Preis dafür, dass Sie nicht nur die Historie von Feldern, sondern auch von Beziehungszusammensetzungen haben möchten.

Für die Preis- und Status-Historie ist dies fast garantiert eine unnötige Verkomplizierung. Daher machen wir für PurchaseOrder eine einfache und verständliche Entscheidung: Wir auditieren die Bestellung als „Kopf“ (Status, Summe, Adresse) und schließen die Kollektion items über @NotAudited aus. Das bewahrt den Hauptnutzen: Die Status-Historie bleibt lesbar und kurz.

Ein weiterer subtiler Punkt betrifft die Link-Entität (ProductCategoryAssignment). Das ist eine Entität für die „Verbindung“. Wenn Sie Product auditieren und es eine Kategorie-Historie gibt, kann Envers die Historie in eine „Chronik der Umsortierungen“ verwandeln, die nur dann interessant ist, wenn Sie wirklich die Aufgabe haben, nachzuvollziehen, wer wann die Kategorien geändert hat. Hier haben wir keine solche Anforderung, also ziehen wir das nicht ins Audit hinein.

Die Hauptregel hier lautet: Das Audit von Aggregaten muss kontrollierbar sein. Sobald Sie das Gefühl haben, dass „das Audit ein halbes Projekt mit sich zieht“, halten Sie inne und kehren Sie zu den Fragen aus Abschnitt 2 zurück. Wenn es keine Frage gibt – ist es nicht nötig.

8. Typische Fehler bei der Wahl der Audit-Grenzen

Bei der Wahl der Audit-Grenzen zeigen sich Fehler normalerweise nicht sofort. Zuerst freut man sich, dass „alles funktioniert“, dann wächst die Datenbank, SQL wird wortreicher, und die Historie verwandelt sich in einen überfüllten Dachboden. Daher ist es nützlich im Voraus zu wissen, wo die häufigsten Fallstricke liegen, um zumindest bewusst darauf zu treten.

Fehler Nr. 1: „Setzen wir @Audited auf alles, dann sehen wir weiter.“
Das ist die beliebteste Falle. Sie scheint logisch, weil das Rückgängigmachen des Audits „einfacher“ zu sein scheint, als es später hinzuzufügen. In der Praxis bekommen Sie schnell eine Lawine von Audit-Tabellen und Revisionen, und dann haben Sie Angst, daran zu rühren, weil „wir könnten ja die Historie beschädigen“. Richtig ist, mit 1–2 Schlüsselentitäten zu beginnen und das Audit nur unter echten historischen Fragen zu erweitern.

Fehler Nr. 2: Auditieren von hochfrequenten technischen Entitäten (z. B. InventoryItem).
Bestände ändern sich häufig, und Envers wird jede Mikroänderung gewissenhaft aufzeichnen. Sie erhalten eine riesige Historie, die schwer zu lesen ist und kaum einer „Historie von Geschäftsereignissen“ ähnelt. In einem Übungsprojekt ist es besser, Envers für „seltene, aber wichtige“ Änderungen zu verwenden – Preis des Produkts, Bestellstatus, Sichtbarkeit des Produkts.

Fehler Nr. 3: Versehentliches Auditieren von Kollektionen und dann über das explosionsartige Schema-Wachstum staunen.
Sehr einfach: Setzen Sie @Audited auf PurchaseOrder, vergessen Sie items und sehen dann, dass zusätzliche Audit-Tabellen und viele zusätzliche Einfügungen erschienen sind. Das ist kein Fehler von Envers, sondern seine Aufgabe. Kollektionen sollten nur dann in das Audit aufgenommen werden, wenn Sie ein bewusstes Ziel haben, die Historie der Kollektionszusammensetzung zu lesen.

Fehler Nr. 4: Große Texte und „breite“ Felder ins Audit ziehen.
Wenn Sie eine Entität mit einem Feld von mehreren Kilobyte auditieren, wird dieses Feld über die Revisionen vervielfacht, weil Envers Aufnahmen speichert. Infolgedessen wächst *_AUD viel schneller, als man es „nach der Anzahl der Änderungen“ erwarten würde. Für solche Daten entweder eine separate Entität ohne Audit auslagern (wie ProductDetails) oder das Feld über @NotAudited ausschließen.

Fehler Nr. 5: Erwarten, dass in der historischen Aufnahme Felder gefüllt sind, die nicht auditiert wurden.
Das ist eine Anfängerfalle: „Ich lese Product auf einer Revision, warum ist name=null?“ Weil Sie name nicht in der Historie gespeichert haben. Wenn Sie selektives Audit verwenden, ist die Disziplin einfach: Beim Lesen der Historie stützen Sie sich auf die auditierten Felder oder erstellen ein separates historisches DTO-Modell, das das tatsächlich Gespeicherte abbildet.

1
Aufgabe
Hibernate deep-dive, Level 21, Lektion 2
Gesperrt
Historie nur fuer Preis und Status eines Produkts
Historie nur fuer Preis und Status eines Produkts
1
Aufgabe
Hibernate deep-dive, Level 21, Lektion 2
Gesperrt
Bestellung mit Audit ohne laute Felder
Bestellung mit Audit ohne laute Felder
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION