#hibernate #jpa #hibernate-5.x #jpa-2.2 #hibernate-5
#впасть в спящий режим #jpa #спящий режим-5.x #jpa-2.2 #спящий режим-5
Вопрос:
Я имею дело с некоторым устаревшим кодом, который местами не имеет чистого и надежного способа получения JPA EntityManager (или сеанса гибернации) для данного объекта, необходимого для вызова метода lock(…). По сути, у нас есть метод в объекте, который должен делать что-то вроде:
@Entity
public class SomeEntity {
@Column(...)
@Basic(...)
private String name;
...
public void doSomething(/* arguments irrelevant */) {
...
myCurrentEntityManagerOrSession.lock(...);
...
}
...
}
Вопрос в том, как это сделать — как получить доступ к этому менеджеру объектов или сеансу? Есть два способа, которые мы придумали для достижения того, что нам нужно, ни один из них не желателен, хотя оба работают:
Вариант № 1 — неловкий триггер свойств
final String originalName = getName();
setName("modified " originalName);
setName(originalName);
… чтобы «обмануть» Hibernate (мы используем улучшение байт-кода, кстати) для выполнения самой блокировки, затем отмените изменение, но сохраните блокировку (мы бы сделали это для любого «недорогого» свойства).
Вариант № 2: Неуместное злоупотребление отражением улучшений байт-кода Hibernate и его внутреннего API
private static final Method HIBERNATE_ENTITY_ENTRY_GETTER;
static {
Method getter;
try {
getter = SomeEntity.class.getMethod("$_hibernate_getEntityEntry");
} catch (NoSuchMethodException e) {
/// Oooh .. not good!
getter = null;
}
HIBERNATE_ENTITY_ENTRY_GETTER = getter;
}
public Session getEntitySession() {
try {
final MutableEntityEntry entry = (MutableEntityEntry) HIBERNATE_ENTITY_ENTRY_GETTER.invoke(this);
return (Session) entry.getPersistenceContext().getSession();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public void lock(final LockMode lockMode) {
getEntitySession().lock(this, lockMode);
}
public void lock() {
lock(LockMode.OPTIMISTIC_FORCE_INCREMENT);
}
Есть ли правильный, чистый способ получить EntityManager / Session, который в настоящее время управляет / владеет определенным объектом? Мы знаем, что базовый движок, конечно, уже должен это знать… но, похоже, это хорошо скрыто.
ДОПОЛНИТЕЛЬНОЕ ПРИМЕЧАНИЕ: ThreadLocal может быть ответом в некоторых проектах, но не в этом. Не спрашивай.
Комментарии:
1. Чтобы сделать ваш код более чистым, вы должны переместить
doSomething
метод за пределы вашего объекта.2. doSomething() только кажется, что не имеет к этому никакого отношения. Это так и есть. Он и так идеально чистый, и его удаление не решает проблему.
3. Код объекта не должен манипулировать с EntityManager / Session. За это отвечает код бизнес-уровня.
4. Совершенно не имеет отношения к обсуждению. Мы могли бы обсудить это в другом месте — обратите внимание, что изменение свойств объекта в любом случае манипулирует блокировками с использованием сеансов. Подумайте об этом, что метод кода бизнес-уровня имеет только ссылку на объект, ничего больше, и я не могу это исправить. Как он может заблокировать объект?