«1. Обзор
В этом обсуждении мы рассмотрим различные способы перехвата операций в реализации абстрактного реляционного отображения Hibernate.
2. Определение Hibernate Interceptor
Hibernate Interceptor — это интерфейс, который позволяет нам реагировать на определенные события в Hibernate.
Эти перехватчики регистрируются как обратные вызовы и обеспечивают связь между сеансом Hibernate и приложением. С помощью такого обратного вызова приложение может перехватывать основные операции Hibernate, такие как сохранение, обновление, удаление и т. д.
Существует два способа определения перехватчиков:
- implementing the org.hibernate.Interceptor interface
- extending the org.hibernate.EmptyInterceptor class
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);
}
Обратите внимание, как эта реализация просто выводит объект, если это пользователь.
Хотя возможно вернуть значение 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 или SessionFactory.
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: метод интерфейса Serializable.
<property name="hibernate.current_session_context_class">
org.hibernate.context.internal.ThreadLocalSessionContext
</property>
4. Заключение
Мы увидели, как определять и регистрировать перехватчики Hibernate как в области действия Session, так и в области SessionFactory. В любом случае мы должны убедиться, что перехватчики сериализуемы, особенно если нам нужен сериализуемый сеанс.
Другие альтернативы перехватчикам включают Hibernate Events и JPA Callbacks.
«И, как всегда, вы можете проверить полный исходный код на Github.
«