Каков правильный способ повторного прикрепления отдельных объектов в Hibernate?

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

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

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

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

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

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

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

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

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

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

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

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

Mik

Все эти ответы пропускают важное различие. update () используется для (re) прикрепления графического объекта к сеансу. Объектами, которые вы передаете, являются те, которые сделаны управляемыми.

merge () на самом деле не является API-интерфейсом (re). Уведомление merge () имеет возвращаемое значение? Это потому, что он возвращает вам управляемый граф, который может и не быть графом, который вы его передали. merge () – это JPA API, и его поведение определяется спецификацией JPA. Если объект, который вы передаете для слияния (), уже управляется (уже связанным с сеансом), то это график, с которым работает Hibernate; объект, переданный в тот же объект, возвращаемый из merge (). Если, однако, объект, который вы передаете в merge (), отключен, Hibernate создает новый управляемый граф объектов, который копирует состояние из вашего отдельного графика на новый управляемый граф. Опять же, все это продиктовано и регулируется спецификацией JPA.

С точки зрения общей страtagsи «убедитесь, что этот объект управляется или его управляют», это зависит от того, хотите ли вы также учитывать еще не вставленные данные. Предполагая, что вы это делаете, используйте что-то вроде

 if ( session.contains( myEntity ) ) { // nothing to do... myEntity is already associated with the session } else { session.saveOrUpdate( myEntity ); } 

Заметьте, я использовал saveOrUpdate (), а не update (). Если вы не хотите, чтобы все еще не вставленные данные обрабатывались здесь, используйте update () вместо этого …

Недифференцированный ответ: вы, вероятно, ищете расширенный контекст постоянства. Это одна из основных причин структуры Seam Framework. Если вы пытаетесь использовать Hibernate весной в частности, ознакомьтесь с этим документом Seam.

Дипломатический ответ: Это описано в документах Hibernate . Если вам нужно больше пояснений, ознакомьтесь с разделом 9.3.2 « Сохранение Java с помощью Hibernate» под названием «Работа с отдельными объектами». Я настоятельно рекомендую вам получить эту книгу, если вы делаете что-то большее, чем CRUD с Hibernate.

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

 session.lock(entity, LockMode.NONE); 

Он ничего не заблокирует, но он получит объект из кеша сеанса или (если его там не найти) прочитает его из БД.

Очень полезно предотвращать LazyInitException, когда вы перемещаете отношения из «старого» (например, из объектов HttpSession). Сначала вы «повторно присоединяете» сущность.

Использование get также может работать, за исключением случаев, когда вы получаете наследование (которое уже генерирует исключение на getId ()).

 entity = session.get(entity.getClass(), entity.getId()); 

Я вернулся к JavaDoc для org.hibernate.Session и нашел следующее:

Временные экземпляры могут быть сделаны постоянными, вызывая save() , saveOrUpdate() или saveOrUpdate() . Стойкие экземпляры могут быть сделаны временными, вызвав delete() . Любой экземпляр, возвращаемый методом get() или load() является постоянным. Отдельные экземпляры могут быть постоянными, вызывая update() , saveOrUpdate() , lock() или replicate() . Состояние временного или отсоединенного экземпляра также может быть сохранено как новый постоянный экземпляр, вызывая merge() .

Таким образом, saveOrUpdate() update() , saveOrUpdate() , lock() , replicate() и merge() являются вариантами-кандидатами.

update() : выдает исключение, если существует постоянный экземпляр с тем же идентификатором.

saveOrUpdate() : сохранить или обновить

lock() : Устаревший

replicate() : Сохранять состояние данного отдельного экземпляра, повторно используя текущее значение идентификатора.

merge() : Возвращает постоянный объект с тем же идентификатором. Данный экземпляр не ассоциируется с сеансом.

Следовательно, lock() не следует использовать сразу и на основе функционального требования можно выбрать один или несколько из них.

Я сделал это в C # с NHibernate, но он должен работать аналогично в Java:

 public virtual void Attach() { if (!HibernateSessionManager.Instance.GetSession().Contains(this)) { ISession session = HibernateSessionManager.Instance.GetSession(); using (ITransaction t = session.BeginTransaction()) { session.Lock(this, NHibernate.LockMode.None); t.Commit(); } } } 

Первая блокировка вызывалась для каждого объекта, потому что Contains всегда была ложной. Проблема в том, что NHibernate сравнивает объекты по идентификатору и типу базы данных. Содержит использует метод equals , который сравнивается по ссылке, если он не перезаписан. С помощью метода equals он работает без каких-либо исключений:

 public override bool Equals(object obj) { if (this == obj) { return true; } if (GetType() != obj.GetType()) { return false; } if (Id != ((BaseObject)obj).Id) { return false; } return true; } 

Session.contains(Object obj) проверяет ссылку и не обнаруживает другой экземпляр, который представляет ту же строку и уже прикреплен к ней.

Вот мое общее решение для Entities с свойством идентификатора.

 public static void update(final Session session, final Object entity) { // if the given instance is in session, nothing to do if (session.contains(entity)) return; // check if there is already a different attached instance representing the same row final ClassMetadata classMetadata = session.getSessionFactory().getClassMetadata(entity.getClass()); final Serializable identifier = classMetadata.getIdentifier(entity, (SessionImplementor) session); final Object sessionEntity = session.load(entity.getClass(), identifier); // override changes, last call to update wins if (sessionEntity != null) session.evict(sessionEntity); session.update(entity); } 

Это один из немногих аспектов .Net EntityFramework, которые мне нравятся, различные параметры прикрепления к измененным объектам и их свойствам.

Я придумал решение «обновить» объект из хранилища персистентности, который будет учитывать другие объекты, которые могут быть уже прикреплены к сеансу:

 public void refreshDetached(T entity, Long id) { // Check for any OTHER instances already attached to the session since // refresh will not work if there are any. T attached = (T) session.load(getPersistentClass(), id); if (attached != entity) { session.evict(attached); session.lock(entity, LockMode.NONE); } session.refresh(entity); } 

Извините, похоже, добавить комментарии (пока?).

Использование Hibernate 3.5.0-Final

В то время как метод Session#lock это не рекомендуется, javadoc предлагает использовать Session#buildLockRequest(LockOptions)#lock(entity) и если вы убедитесь, что ваши ассоциации имеют cascade=lock , то и ленивая загрузка тоже не является проблемой.

Итак, мой метод attach немного похож на

 MyEntity attach(MyEntity entity) { if(getSession().contains(entity)) return entity; getSession().buildLockRequest(LockOptions.NONE).lock(entity); return entity; 

Первоначальные тесты показывают, что это работает.

попробуйте getHibernateTemplate (). replicate (entity, ReplicationMode.LATEST_VERSION)

В исходном сообщении есть два метода: update(obj) и merge(obj) , которые упоминаются для работы, но в противоположных обстоятельствах. Если это действительно так, то почему бы не попробовать проверить, находится ли объект уже в сеансе, а затем вызвать update(obj) если он есть, иначе вызовите merge(obj) .

Тест на существование в сеансе – session.contains(obj) . Поэтому я бы подумал, что следующий псевдокод будет работать:

 if (session.contains(obj)) { session.update(obj); } else { session.merge(obj); } 

Возможно, на Eclipselink он немного отличается. Для повторного прикрепления отдельных объектов без получения устаревших данных я обычно делаю:

 Object obj = em.find(obj.getClass(), id); 

и как необязательный второй шаг (чтобы получить кеши недействительными):

 em.refresh(obj) 

для повторного подключения к этому объекту вы должны использовать merge ();

этот метод принимает в параметре выделение вашего объекта и возвращает объект, который будет прикреплен и перезагружен из базы данных.

 Example : Lot objAttach = em.merge(oldObjDetached); objAttach.setEtat(...); em.persist(objAttach); 

вызов первого слияния () (для обновления постоянного экземпляра), а затем блокировка (LockMode.NONE) (для прикрепления текущего экземпляра, а не возвращаемого слиянием ()), похоже, работает в некоторых случаях использования.

 try getHibernateTemplate().saveOrUpdate() 
  • Как добиться условного импорта ресурсов в контексте Spring XML?
  • Пользовательский HttpMessageConverter с @ResponseBody, чтобы делать вещи Json
  • Почему это приложение Spring с настройкой на основе Java не работает должным образом
  • Как сделать весеннюю инъекционную ценность в статическом поле
  • Spring Java Config vs Jboss 7
  • Как использовать YamlPropertiesFactoryBean для загрузки файлов YAML с использованием Spring Framework 4.1?
  • Bypass GeneratedValue в спящем режиме (данные объединить не в db?)
  • Spring MVC - Как получить все параметры запроса на карте в controllerе Spring?
  • Несколько примечаний с пружиной @RequestMapping
  • Вам нужна транзакция базы данных для чтения данных?
  • Форма представляет в Spring MVC 3 - объяснение
  • Давайте будем гением компьютера.