Hibernate Interceptors

Winterschlaf-Abfangjäger

1. Überblick

In dieser Diskussion werden verschiedene Möglichkeiten zum Abfangen von Operationen innerhalb der abstrahierten relationalen Mapping-Implementierung vonHibernate’suntersucht.

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 und der Anwendung von Hibernate bereit. Mit einem solchen Rückruf kann eine Anwendung die Kernoperationen von Hibernate abfangen, z. B.save, update, delete, etc.

Es gibt zwei Möglichkeiten, Abfangjäger zu definieren:

  1. implementing the org.hibernate.Interceptor Schnittstelle

  2. extending the org.hibernate.EmptyInterceptor Klasse

2.1. Implementieren einerInterceptor-Schnittstelle

Implementing org.hibernate.Interceptor requires implementing about 14 accompanying methods. Diese Methoden umfassenonLoad, onSave, onDelete, findDirty, und einige weitere.

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

Ein typisches Beispiel würde so aussehen:

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 vorliegen, wird dringend empfohlen, die Klasseextending the EmptyInterceptorzu verwenden und nur die erforderlichen Methoden zu überschreiben.

2.2. EmptyInterceptor verlängern

Das Erweitern der Klasseorg.hibernate.EmptyInterceptorbietet eine einfachere Möglichkeit, einen Interceptor zu definieren. We now only need to override the methods that relate to the operation we want to intercept.

Zum Beispiel können wir unsereCustomInterceptor 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 Methode vononSaveü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 umUserhandelt.

Es ist zwar möglich, einen Wert vontrue oderfalse zurückzugeben, es wird jedoch empfohlen, die Weitergabe des EreignissesonSavedurch Aufrufen vonsuper.onSave() zuzulassen.

Another use-case would be providing an audit trail for database interactions. Mit der MethodeonFlushDirty()können wir feststellen, wann sich eine Entität ändert.

Für das ObjektUserkönnen wir entscheiden, die DatumseigenschaftlastModifiedzu aktualisieren, wenn Änderungen an Entitäten vom TypUserauftreten.

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 wiedelete undload (Objektinitialisierung) können abgefangen werden, indem die entsprechenden MethodenonDelete bzw.onLoad implementiert werden.

3. Interceptors registrieren

Ein Interceptor im Ruhezustand kann entweder alsSession-Bereich oder alsSessionFactory-scoped registriert werden.

3.1. Session-scoped Interceptor

Ein Interceptor mitSession-Bereich 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 für eine bestimmte Ruhezustandssitzung registriert.

3.2. SessionFactory-scopedInterceptor

EinSessionFactory-scoped Interceptor wird registriert, bevor einSessionFactory. erstellt wird. Dies erfolgt normalerweise über dieapplyInterceptor-Methode für eineSessionFactoryBuilder-Instanz:

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

Es ist wichtig zu beachten, dass ein Interceptor mitSessionFactory-scoped 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 Abfangjäger mitSessionFactory-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 mitSessionFactory-Bereich die MethodereadResolve der SchnittstelleSerializable implementieren.

4. Fazit

Wir haben gesehen, wie Hibernate-Interceptors entweder alsSession-Bereich oder alsSessionFactory-Bereich 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.

Weitere Alternativen zu Interceptors sind Hibernate Events und JPA Callbacks.

Und wie immer können Sie die vollständigensource code over on Github überprüfen.