Hibernate Interceptors

1. Panoramica

In questa discussione, esamineremo vari modi di intercettare le operazioni all'interno dell'implementazione della mappatura relazionale astratta di Hibernate.

2. Definizione di Hibernate Interceptor

Hibernate Interceptor è un'interfaccia che ci consente di reagire a determinati eventi all'interno di Hibernate.

Questi intercettori vengono registrati come callback e forniscono collegamenti di comunicazione tra la sessione e l'applicazione di Hibernate. Con una tale richiamata, un'applicazione può intercettare le operazioni principali di Hibernate come salvare, aggiornare, eliminare, ecc.

Esistono due modi per definire gli intercettori:

  1. implementando l' interfaccia org.hibernate.Interceptor
  2. estendendo la classe org.hibernate.EmptyInterceptor

2.1. Implementazione di un Interceptor Interface

L'implementazione di org.hibernate.Interceptor richiede l'implementazione di circa 14 metodi di accompagnamento . Questi metodi includono onLoad, onSave, onDelete, findDirty e alcuni altri.

È anche importante assicurarsi che qualsiasi classe che implementa l'interfaccia Interceptor sia serializzabile ( implementa java.io.Serializable ).

Un tipico esempio sarebbe:

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

Se non ci sono requisiti speciali, è altamente raccomandato estendere la classe EmptyInterceptor e sovrascrivere solo i metodi richiesti.

2.2. Estensione di EmptyInterceptor

L'estensione della classe org.hibernate.EmptyInterceptor fornisce un modo più semplice per definire un intercettore. Ora dobbiamo solo sovrascrivere i metodi relativi all'operazione che vogliamo intercettare.

Ad esempio, possiamo definire il nostro CustomInterceptor come:

public class CustomInterceptor extends EmptyInterceptor { }

E se dobbiamo intercettare le operazioni di salvataggio dei dati prima che vengano eseguite, dobbiamo sovrascrivere il metodo 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); }

Nota come questa implementazione stampa semplicemente l'entità, se è un utente .

Sebbene sia possibile restituire un valore true o false , è buona norma consentire la propagazione dell'evento onSave richiamando super.onSave () .

Un altro caso d'uso sarebbe fornire una traccia di controllo per le interazioni del database. Possiamo usare il metodo onFlushDirty () per sapere quando un'entità cambia.

Per l' oggetto Utente , possiamo decidere di aggiornare la sua proprietà di data lastModified ogni volta che si verificano modifiche su entità di tipo Utente .

Ciò può essere ottenuto con:

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

Altri eventi come cancellazione e caricamento (inizializzazione di oggetti) possono essere intercettati implementando rispettivamente i metodi onDelete e onLoad corrispondenti.

3. Registrazione degli intercettori

Un intercettore Hibernate può essere registrato come Session -scoped o SessionFactory-scoped .

3.1. Intercettore con ambito di sessione

Un intercettore con ambito di sessione è collegato a una sessione specifica. Viene creato quando la sessione viene definita o aperta come:

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

In quanto sopra, abbiamo registrato esplicitamente un intercettore con una particolare sessione di ibernazione.

3.2. SessionFactory -scoped Interceptor

Un intercettore con ambito SessionFactory viene registrato prima di creare una SessionFactory. Questa operazione viene in genere eseguita tramite il metodo applyInterceptor su un'istanza SessionFactoryBuilder :

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

È importante notare che un intercettore con ambito SessionFactory verrà applicato a tutte le sessioni. Quindi, dobbiamo stare attenti a non memorizzare lo stato specifico della sessione, poiché questo intercettore verrà utilizzato da diverse sessioni contemporaneamente.

Per un comportamento specifico della sessione, si consiglia di aprire esplicitamente una sessione con un intercettore diverso come mostrato in precedenza.

Per gli intercettori con ambito SessionFactory , dobbiamo naturalmente assicurarci che sia thread-safe. Ciò può essere ottenuto specificando un contesto di sessione nel file delle proprietà:

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

O aggiungendolo al nostro file di configurazione XML:

 org.hibernate.context.internal.ThreadLocalSessionContext 

Inoltre, per garantire la serializzabilità, gli intercettatori con ambito SessionFactory devono implementare il metodo readResolve dell'interfaccia Serializable .

4. Conclusione

Abbiamo visto come definire e registrare gli intercettori Hibernate come Session -scoped o SessionFactory -scoped. In entrambi i casi, dobbiamo assicurarci che gli intercettori siano serializzabili soprattutto se vogliamo una sessione serializzabile.

Altre alternative agli intercettatori includono gli eventi di ibernazione e le richiamate JPA.

E, come sempre, puoi controllare il codice sorgente completo su Github.