Hibernate Interceptors

1. Übersicht

In dieser Diskussion werden verschiedene Möglichkeiten zum Abfangen von Operationen in der abstrahierten relationalen Mapping-Implementierung von Hibernate untersucht.

2. Definieren von Interceptors im Ruhezustand

Der Hibernate Interceptor ist eine Schnittstelle, über die wir auf bestimmte Ereignisse im Hibernate reagieren können.

Diese Interceptors werden als Rückrufe registriert und stellen Kommunikationsverbindungen zwischen der Sitzung von Hibernate und der Anwendung bereit. Mit einem solchen Rückruf kann eine Anwendung die Kernvorgänge von Hibernate wie Speichern, Aktualisieren, Löschen usw. abfangen .

Es gibt zwei Möglichkeiten, Interceptors zu definieren:

  1. Implementieren der Schnittstelle org.hibernate.Interceptor
  2. Erweitern der Klasse org.hibernate.EmptyInterceptor

2.1. Implementieren einer Interceptor- Schnittstelle

Für die Implementierung von org.hibernate.Interceptor müssen ca. 14 zugehörige Methoden implementiert werden . Diese Methoden umfassen onLoad, onSave, onDelete, findDirty und einige weitere.

Es ist auch wichtig sicherzustellen, dass jede Klasse, die die Interceptor-Schnittstelle implementiert, serialisierbar ist ( implementiert java.io.Serializable ).

Ein typisches Beispiel würde aussehen wie:

public class CustomInterceptorImpl implements Interceptor, Serializable { @Override public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException { // ... return false; } // ... @Override public String onPrepareStatement(String sql) { // ... return sql; } }

Wenn keine besonderen Anforderungen bestehen, wird dringend empfohlen , die EmptyInterceptor- Klasse zu erweitern und nur die erforderlichen Methoden zu überschreiben.

2.2. Die Ausweitung EmptyInterceptor

Das Erweitern der Klasse org.hibernate.EmptyInterceptor bietet eine einfachere Möglichkeit, einen Interceptor zu definieren. Wir müssen jetzt nur noch die Methoden überschreiben, die sich auf die Operation beziehen, die wir abfangen möchten.

Zum Beispiel können wir unseren CustomInterceptor wie folgt definieren :

public class CustomInterceptor extends EmptyInterceptor { }

Und wenn wir Datenspeicherungsvorgänge abfangen müssen, bevor sie ausgeführt werden, müssen wir die onSave- Methode überschreiben :

@Override public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) { if (entity instanceof User) { logger.info(((User) entity).toString()); } return super.onSave(entity, id, state, propertyNames, types); }

Beachten Sie, wie diese Implementierung die Entität einfach druckt - wenn es sich um einen Benutzer handelt .

Es ist zwar möglich, den Wert true oder false zurückzugeben , es wird jedoch empfohlen , die Weitergabe des onSave- Ereignisses durch Aufrufen von super.onSave () zuzulassen .

Ein weiterer Anwendungsfall wäre die Bereitstellung eines Prüfpfads für Datenbankinteraktionen. Wir können die onFlushDirty () -Methode verwenden, um zu wissen, wann sich eine Entität ändert.

Für das User- Objekt können wir entscheiden, seine lastModified-Datumseigenschaft zu aktualisieren, wenn Änderungen an Entitäten vom Typ User auftreten .

Dies kann erreicht werden mit:

@Override public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object [] previousState, String[] propertyNames, Type[] types) { if (entity instanceof User) { ((User) entity).setLastModified(new Date()); logger.info(((User) entity).toString()); } return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types); }

Andere Ereignisse wie Löschen und Laden (Objektinitialisierung) können durch Implementieren der entsprechenden onDelete- bzw. onLoad- Methoden abgefangen werden.

3. Abfangjäger registrieren

Ein Interceptor im Ruhezustand kann entweder als Sitzungsbereich oder als Sitzungsbereich registriert werden .

3.1. Interceptor mit Sitzungsbereich

Ein Interceptor mit Sitzungsbereich ist mit einer bestimmten Sitzung verknüpft. Es wird erstellt, wenn die Sitzung definiert oder geöffnet wird als:

public static Session getSessionWithInterceptor(Interceptor interceptor) throws IOException { return getSessionFactory().withOptions() .interceptor(interceptor).openSession(); }

Oben haben wir explizit einen Interceptor mit einer bestimmten Ruhezustandssitzung registriert.

3.2. SessionFactory- gesteuerter Interceptor

Vor dem Erstellen einer SessionFactory wird ein Interceptor mit SessionFactory- Gültigkeitsbereich registriert . Dies erfolgt normalerweise über die applyInterceptor- Methode in einer SessionFactoryBuilder- Instanz:

ServiceRegistry serviceRegistry = configureServiceRegistry(); SessionFactory sessionFactory = getSessionFactoryBuilder(serviceRegistry) .applyInterceptor(new CustomInterceptor()) .build();

Es ist wichtig zu beachten, dass ein Interceptor mit SessionFactory- Gültigkeitsbereich auf alle Sitzungen angewendet wird. Daher müssen wir darauf achten, keinen sitzungsspezifischen Status zu speichern, da dieser Interceptor von verschiedenen Sitzungen gleichzeitig verwendet wird.

Für ein sitzungsspezifisches Verhalten wird empfohlen, eine Sitzung explizit mit einem anderen Interceptor zu öffnen, wie zuvor gezeigt.

Für Interceptors mit SessionFactory-Bereich müssen wir natürlich sicherstellen, dass sie threadsicher sind. Dies kann durch Angabe eines Sitzungskontexts in der Eigenschaftendatei erreicht werden:

hibernate.current_session_context_class=org.hibernate.context.internal.ThreadLocalSessionContext

Oder indem Sie dies zu unserer XML-Konfigurationsdatei hinzufügen:

 org.hibernate.context.internal.ThreadLocalSessionContext 

Um die Serialisierbarkeit sicherzustellen, müssen Interceptors mit SessionFactory- Bereich die readResolve- Methode der serialisierbaren Schnittstelle implementieren.

4. Fazit

Wir haben gesehen, wie Hibernate-Interceptors entweder als Session- Scoped oder als SessionFactory- Scoped definiert und registriert werden . In beiden Fällen müssen wir sicherstellen, dass die Interceptors serialisierbar sind, insbesondere wenn wir eine serialisierbare Sitzung wünschen.

Andere Alternativen zu Interceptors sind Hibernate Events und JPA Callbacks.

Und wie immer können Sie den vollständigen Quellcode auf Github überprüfen.