1. Общ преглед
В тази дискусия ще разгледаме различни начини за прихващане на операции в рамките на абстрактното изпълнение на релационно картографиране на Hibernate.
2. Дефиниране на Hibernate Interceptors
Hibernate Interceptor е интерфейс, който ни позволява да реагираме на определени събития в Hibernate.
Тези прехващачи са регистрирани като обратни обаждания и осигуряват комуникационни връзки между сесията на Hibernate и приложението. С такъв обратен разговор приложение може да прихваща основните операции на Hibernate като запазване, актуализиране, изтриване и т.н.
Има два начина за определяне на прехващачи:
- внедряване на org.hibernate.Interceptor интерфейс
- разширяване на класа 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.