Хибернатни прехващачи

1. Общ преглед

В тази дискусия ще разгледаме различни начини за прихващане на операции в рамките на абстрактното изпълнение на релационно картографиране на Hibernate.

2. Дефиниране на Hibernate Interceptors

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

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

Има два начина за определяне на прехващачи:

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

2.1. Осъществяването на Interceptor Interface

Внедряването на 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 () , за да знаем кога даден обект се променя.

За обекта Потребител можем да решим да актуализираме неговото свойство lastModified date, когато се случат промени в обекти от тип Потребител .

Това може да се постигне с:

@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. Регистриране на прехващачи

Прихващачът за хибернация може да бъде регистриран като обхванат от сесия или обхват от сесия .

3.1. Прехващач със сесия

A сесия -scoped прехващач е свързано с конкретна сесия. Създава се, когато сесията се дефинира или отваря като:

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

По-горе изрично регистрирахме прихващач с определена сесия на хибернация.

3.2. SessionFactory -scoped Interceptor

А 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 конфигурационен файл:

 org.hibernate.context.internal.ThreadLocalSessionContext 

Също така, за да се осигури сериализуемост, прихващачите с обхват SessionFactory трябва да внедрят метода readResolve на интерфейса за сериализиране .

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

Видяхме как да дефинираме и регистрираме прехващачи на хибернация или като Session -scoped или SessionFactory -scoped. И в двата случая трябва да гарантираме, че прехващачите са сериализуеми, особено ако искаме сериализуема сесия.

Други алтернативи на прехващачите включват Hibernate Events и JPA Callbacks.

И както винаги, можете да проверите пълния изходен код на Github.