Intercepteurs d'hibernation

1. Vue d'ensemble

Dans cette discussion, nous examinerons différentes manières d'intercepter des opérations dans l'implémentation de mappage relationnel abstrait d'Hibernate.

2. Définition des intercepteurs Hibernate

L'intercepteur Hibernate est une interface qui nous permet de réagir à certains événements dans Hibernate.

Ces intercepteurs sont enregistrés en tant que rappels et fournissent des liens de communication entre la session et l'application d'Hibernate. Avec un tel rappel, une application peut intercepter les opérations principales d'Hibernate telles que la sauvegarde, la mise à jour, la suppression, etc.

Il existe deux façons de définir les intercepteurs:

  1. implémentation de l' interface org.hibernate.Interceptor
  2. extension de la classe org.hibernate.EmptyInterceptor

2.1. Implémentation d'une interface d' intercepteur

L'implémentation de org.hibernate.Interceptor nécessite l'implémentation d'environ 14 méthodes d'accompagnement . Ces méthodes incluent onLoad, onSave, onDelete, findDirty et quelques autres.

Il est également important de s'assurer que toute classe qui implémente l'interface Interceptor est sérialisable ( implémente java.io.Serializable ).

Un exemple typique ressemblerait à ceci:

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; } }

S'il n'y a pas d'exigences particulières, il est fortement recommandé d' étendre la classe EmptyInterceptor et de ne remplacer que les méthodes requises.

2.2. Extension de EmptyInterceptor

L'extension de la classe org.hibernate.EmptyInterceptor fournit un moyen plus simple de définir un intercepteur. Il ne nous reste plus qu'à remplacer les méthodes liées à l'opération que nous voulons intercepter.

Par exemple, nous pouvons définir notre CustomInterceptor comme:

public class CustomInterceptor extends EmptyInterceptor { }

Et si nous devons intercepter les opérations de sauvegarde des données avant qu'elles ne soient exécutées, nous devons remplacer la méthode onSave :

@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); }

Remarquez comment cette implémentation imprime simplement l'entité - s'il s'agit d'un utilisateur .

Bien qu'il soit possible de renvoyer une valeur true ou false , il est recommandé d' autoriser la propagation de l' événement onSave en appelant super.onSave () .

Un autre cas d'utilisation serait de fournir une piste d'audit pour les interactions de base de données. Nous pouvons utiliser la méthode onFlushDirty () pour savoir quand une entité change.

Pour l' objet User , nous pouvons décider de mettre à jour sa propriété de date lastModified chaque fois que des changements sur des entités de type User se produisent.

Ceci peut être réalisé avec:

@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); }

D'autres événements tels que la suppression et le chargement (initialisation d'objet) peuvent être interceptés en implémentant respectivement les méthodes onDelete et onLoad correspondantes .

3. Enregistrement des intercepteurs

Un intercepteur Hibernate peut être enregistré en tant que Session -scoped ou SessionFactory-scoped .

3.1. Intercepteur à portée de session

Un intercepteur de type Session est lié à une session spécifique. Il est créé lorsque la session est définie ou ouverte comme:

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

Dans ce qui précède, nous avons explicitement enregistré un intercepteur avec une session d'hibernation particulière.

3.2. Intercepteur à portée SessionFactory

Un intercepteur ayant une portée SessionFactory est enregistré avant de créer une SessionFactory. Cela se fait généralement via la méthode applyInterceptor sur une instance SessionFactoryBuilder :

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

Il est important de noter qu'un intercepteur à portée SessionFactory sera appliqué à toutes les sessions. Par conséquent, nous devons faire attention à ne pas stocker l'état spécifique à la session - car cet intercepteur sera utilisé par différentes sessions simultanément.

Pour un comportement spécifique à une session, il est recommandé d'ouvrir explicitement une session avec un intercepteur différent comme indiqué précédemment.

Pour les intercepteurs de type SessionFactory , nous devons naturellement nous assurer qu'ils sont thread-safe. Cela peut être réalisé en spécifiant un contexte de session dans le fichier de propriétés:

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

Ou en ajoutant ceci à notre fichier de configuration XML:

 org.hibernate.context.internal.ThreadLocalSessionContext 

De plus, pour garantir la sérialisation, les intercepteurs à portée SessionFactory doivent implémenter la méthode readResolve de l' interface Serializable .

4. Conclusion

Nous avons vu comment définir et enregistrer les intercepteurs Hibernate en tant que Session- Scoped ou SessionFactory -scoped. Dans les deux cas, nous devons nous assurer que les intercepteurs sont sérialisables, surtout si nous voulons une session sérialisable.

D'autres alternatives aux intercepteurs incluent les événements Hibernate et les rappels JPA.

Et, comme toujours, vous pouvez consulter le code source complet sur Github.