Hibernate Перехватчики

Hibernate Перехватчики

1. обзор

В этом обсуждении мы рассмотрим различные способы перехвата операций в рамках реализации абстрактного реляционного отображенияHibernate’s.

2. Определение перехватчиков гибернации

Hibernate Interceptor - это интерфейс, который позволяет нам реагировать на определенные события в Hibernate.

Эти перехватчики регистрируются как обратные вызовы и обеспечивают каналы связи между сеансом Hibernate и приложением. С помощью такого обратного вызова приложение может перехватывать основные операции Hibernate, такие какsave, update, delete, etc.

Есть два способа определения перехватчиков:

  1. implementing the org.hibernate.Interceptor интерфейс

  2. extending the org.hibernate.EmptyInterceptor класс

2.1. Реализация интерфейсаInterceptor

Implementing org.hibernate.Interceptor requires implementing about 14 accompanying methods. Эти методы включаютonLoad, onSave, onDelete, findDirty, и некоторые другие.

Также важно убедиться, что любой класс, реализующий интерфейс Interceptor, является сериализуемым (implements java.io.Serializable).

Типичный пример будет выглядеть так:

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

}

Если нет особых требований, настоятельно рекомендуется использовать классextending the EmptyInterceptor и переопределить только требуемые методы.

2.2. РасширениеEmptyInterceptor

Расширение классаorg.hibernate.EmptyInterceptor обеспечивает более простой способ определения перехватчика. We now only need to override the methods that relate to the operation we want to intercept.

Например, мы можем определить нашCustomInterceptor как:

public class CustomInterceptor extends EmptyInterceptor {
}

И если нам нужно перехватить операции сохранения данных до их выполнения, нам нужно переопределить метод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);
}

Обратите внимание, как эта реализация просто выводит сущность - если этоUser.

Хотя можно вернуть значениеtrue илиfalse, рекомендуется разрешить распространение событияonSave путем вызоваsuper.onSave().

Another use-case would be providing an audit trail for database interactions. Мы можем использовать методonFlushDirty(), чтобы узнать, когда объект изменяется.

Для объектаUser мы можем решить обновить его свойство датыlastModified всякий раз, когда происходят изменения в сущностях типаUser.

Это может быть достигнуто с помощью:

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

Другие события, такие какdelete иload (инициализация объекта), могут быть перехвачены путем реализации соответствующих методовonDelete иonLoad соответственно.

3. РегистрацияInterceptors

Перехватчик Hibernate может быть зарегистрирован какSession-scoped илиSessionFactory-scoped.

3.1. Session-scoped Interceptorс

ПерехватчикSession-scoped связан с конкретным сеансом. Он создается, когда сеанс определяется или открывается как:

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

Выше мы явно зарегистрировали перехватчик с определенным сеансом гибернации.

3.2. SessionFactory-область действияInterceptor

ПерехватчикSessionFactory-scoped регистрируется перед построениемSessionFactory.. Обычно это делается с помощью методаapplyInterceptor на экземпляреSessionFactoryBuilder:

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

Важно отметить, что перехватчикSessionFactory-scoped будет применяться ко всем сеансам. Следовательно, мы должны быть осторожны, чтобы не сохранять конкретное состояние сеанса - так как этот перехватчик будет использоваться разными сеансами одновременно

Для поведения конкретного сеанса рекомендуется явно открыть сеанс с другим перехватчиком, как показано ранее.

Для перехватчиков с областью видимостиSessionFactory нам, естественно, необходимо обеспечить безопасность потоков. Это может быть достигнуто указанием контекста сеанса в файле свойств:

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

Или добавив это в наш файл конфигурации XML:


    org.hibernate.context.internal.ThreadLocalSessionContext

Кроме того, для обеспечения сериализуемости перехватчикиSessionFactory-scoped должны реализовывать методreadResolve интерфейсаSerializable.

4. Заключение

Мы видели, как определять и регистрировать перехватчики Hibernate либо какSession-scoped, либо какSessionFactory-scoped. В любом случае мы должны убедиться, что перехватчики сериализуемы, особенно если мы хотим сериализуемый сеанс.

Другие альтернативы перехватчикам включают события гибернации и обратные вызовы JPA.

И, как всегда, вы можете просмотреть всеsource code over on Github.