1. Обзор
В этом обсуждении мы рассмотрим различные способы перехвата операций в реализации абстрактного реляционного отображения Hibernate .
2. Определение перехватчиков Hibernate
Hibernate Interceptor — это интерфейс, который позволяет нам реагировать на определенные события в Hibernate.
Эти перехватчики регистрируются как обратные вызовы и обеспечивают связь между сеансом Hibernate и приложением. С помощью такого обратного вызова приложение может перехватывать основные операции Hibernate, такие как сохранение, обновление, удаление и т. д.
Существует два способа определения перехватчиков:
- реализация `` интерфейса
org.hibernate.Interceptor
- расширение `` класса
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 .