Как правильно повторно прикрепить отдельные объекты в Hibernate?

186

У меня есть ситуация, в которой мне нужно повторно присоединить отдельные объекты к сеансу гибернации, хотя в сеансе МОЖЕТ уже существовать объект с таким же идентификатором, что приведет к ошибкам.

Прямо сейчас я могу сделать одну из двух вещей.

  1. getHibernateTemplate().update( obj ) Это работает тогда и только тогда, когда объект не существует в сеансе гибернации. Выдаются исключения, указывающие, что объект с данным идентификатором уже существует в сеансе, когда он понадобится мне позже.

  2. getHibernateTemplate().merge( obj ) Это работает, если и только если объект существует в сеансе гибернации. Исключения генерируются, когда мне нужно, чтобы объект был в сеансе позже, если я использую это.

Учитывая эти два сценария, как я могу в общем случае присоединять сеансы к объектам? Я не хочу использовать исключения для управления потоком решения этой проблемы, так как должно быть более элегантное решение ...

Стефан Кендалл
источник

Ответы:

181

Таким образом, кажется, что в JPA нет способа присоединить устаревшую отдельную сущность.

merge() перенесет устаревшее состояние в БД и перезапишет любые промежуточные обновления.

refresh() не может быть вызван на отдельном объекте.

lock() не может быть вызван для отсоединенной сущности, и даже если бы он мог, и он снова прикрепил сущность, вызов 'lock' с аргументом 'LockMode.NONE', подразумевающий, что вы блокируете, но не блокируете, является наиболее противоречивым элементом разработки API Я когда-либо видел.

Итак, вы застряли. Есть detach()метод, но нет attach()или reattach(). Очевидный шаг в жизненном цикле объекта вам недоступен.

Судя по количеству подобных вопросов о JPA, кажется, что даже если JPA действительно утверждает, что имеет согласованную модель, это, безусловно, не соответствует ментальной модели большинства программистов, которые были прокляты тратить много часов, пытаясь понять, как получить JPA, чтобы делать самые простые вещи, и в конечном итоге с кодом управления кэшем во всех своих приложениях.

Кажется, единственный способ сделать это - сбросить устаревшую отдельную сущность и выполнить запрос на поиск с тем же идентификатором, который попадет в L2 или DB.

Mik

mikhailfranco
источник
1
Интересно, есть ли причина, по которой спецификация JPA не позволяет использовать refresh()отдельные объекты? Просматривая спецификации 2.0, я не вижу никакого оправдания; просто это не разрешено.
FGreg
11
Это точно НЕ точно. От JPwH: *Reattaching a modified detached instance* A detached instance may be reattached to a new Session (and managed by this new persistence context) by calling update() on the detached object. In our experience, it may be easier for you to understand the following code if you rename the update() method in your mind to reattach()—however, there is a good reason it’s called updating.больше можно найти в разделе 9.3.2
cwash
Постоянные объекты работают отлично, грязный флаг устанавливается на основе дельты между начальной загрузкой и значениями во времени flush (). Отдельные объекты нужны, и в настоящее время не имеют этой функциональности. Способ для hibernate сделать это состоит в том, чтобы добавить дополнительный хэш / идентификатор для отдельных объектов. И сохраняйте снимок последнего доступного состояния отсоединенного объекта, так же как и для постоянных объектов. Таким образом, они могут использовать весь этот существующий код и заставить его работать для отдельных объектов. Таким образом, как заметил @mikhailfranco, мы не будем «выдвигать устаревшее состояние в БД и перезаписывать любые промежуточные обновления»
том
2
Согласно Hibernate Javadoc (но не JPA), lock(LockMode.NONE)фактически может быть вызван на временном объекте, и он действительно присоединяет объект к сеансу. См stackoverflow.com/a/3683370/14379
seanf
Блокировка не сработала для меня: java.lang.IllegalArgumentException: сущность не находится в контексте постоянства в org.hibernate.internal.SessionImpl.lock (SessionImpl.java:3491) в org.hibernate.internal.SessionImpl.lock (SessionImpl. Java: 3482) на com.github.vok.framework.DisableTransactionControlEMDelegate.lock (DB.kt)
Мартин Высны