Перейти к основному содержимому

Спящие перехватчики

· 4 мин. чтения

1. Обзор

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

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

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

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

Существует два способа определения перехватчиков:

  1. реализация `` интерфейса org.hibernate.Interceptor
  2. расширение `` класса org.hibernate.EmptyInterceptor

2.1. Реализация интерфейса перехватчика

Реализация org.hibernate.Interceptor требует реализации около 14 сопутствующих методов . Эти методы включают onLoad, onSave, onDelete, findDirty и некоторые другие.

Также важно убедиться, что любой класс, реализующий интерфейс Interceptor, является сериализуемым ( реализует 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;
}

}

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

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

Расширение класса org.hibernate.EmptyInterceptor обеспечивает более простой способ определения перехватчика. Теперь нам нужно только переопределить методы, относящиеся к операции, которую мы хотим перехватить.

Например, мы можем определить наш 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() .

Другим вариантом использования может быть предоставление контрольного журнала для взаимодействия с базой данных. Мы можем использовать метод 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);
}

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

3. Регистрация перехватчиков

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

3.1. Перехватчик на уровне сеанса

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

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

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

3.2. Перехватчик с областью действия SessionFactory ``

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

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

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

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

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

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

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

<property name="hibernate.current_session_context_class">
org.hibernate.context.internal.ThreadLocalSessionContext
</property>

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

4. Вывод

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

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

И, как всегда, вы можете проверить полный исходный код на Github .