Ошибка гибернации: другой объект с тем же значение идентификатора уже был связан с сессии
у меня по существу есть некоторые объекты в этой конфигурации (реальная модель данных немного сложнее):
- A имеет отношения "многие ко многим" С B. (B имеет
inverse="true") - B имеет отношения "многие к одному" с C. (У меня есть
cascadeзначение"save-update") - C-это своего рода таблица типов/категорий.
кроме того, я должен, вероятно, упомянуть, что первичные ключи генерируются базой данных при сохранении.
С моими данными, я иногда возникают проблемы, когда A имеет набор различных объектов B, и эти объекты B относятся к одному и тому же объекту C.
когда я называю session.saveOrUpdate(myAObject), Я получаю сообщение об ошибке гибернации:"a different object with the same identifier value was already associated with the session: C". Я знаю, что hibernate не может вставлять/обновлять / удалять один и тот же объект дважды в одном сеансе, но есть ли способ обойти это? Это не кажется, что это было бы так необычно ситуации.
во время моего исследования этой проблемы я видел, как люди предлагают использовать session.merge(), но когда я это делаю, любые "конфликтующие" объекты вставляются в базу данных как пустые объекты со всеми значениями, установленными в null. Ясно, что это не то, что мы хотим.
[Edit] еще одна вещь, которую я забыл упомянуть, заключается в том, что (по архитектурным причинам, не зависящим от меня) каждое чтение или запись должны выполняться в отдельном сеансе.
15 ответов:
скорее всего, это потому, что объекты B не ссылаются на один и тот же экземпляр объекта Java C. Они ссылаются на одну и ту же строку в базе данных (т. е. на один и тот же первичный ключ), но это разные ее копии.
Итак, что происходит, так это то, что сеанс Hibernate, который управляет сущностями, будет отслеживать, какой объект Java соответствует строке с тем же первичным ключом.
одним из вариантов было бы убедиться, что сущности объектов B которые ссылаются на одну и ту же строку, фактически ссылаются на один и тот же экземпляр объекта C. альтернативно отключите каскадирование для этой переменной-члена. Таким образом, когда B сохраняется, C нет. Однако вам придется сохранить C вручную отдельно. Если C-это таблица типов / категорий, то, вероятно, имеет смысл быть таким образом.
вам нужно сделать только одну вещь. Беги
session_object.clear()а затем сохраните новый объект. Это очистит сеанс (как метко названный) и удалит оскорбительный дубликат объекта из вашего сеанса.
перенесите задачу назначения идентификатора объекта из режима гибернации в базу данных с помощью:
<generator class="native"/>это решило проблему для меня.
Я согласен с @Hemant kumar, спасибо, что сильно отличаетесь. согласно его решению, я решил свою проблему.
например:
@Test public void testSavePerson() { try (Session session = sessionFactory.openSession()) { Transaction tx = session.beginTransaction(); Person person1 = new Person(); Person person2 = new Person(); person1.setName("222"); person2.setName("111"); session.save(person1); session.save(person2); tx.commit(); } }человек.java
public class Person { private int id; private String name; @Id @Column(name = "id") public int getId() { return id; } public void setId(int id) { this.id = id; } @Basic @Column(name = "name") public String getName() { return name; } public void setName(String name) { this.name = name; } }этот код всегда ошибается в моем приложении:
A different object with the same identifier value was already associated with the sessionпозже я узнал, что я лягушка к autoincrease мой первичный ключ!мое решение-добавить этот код на свой первичный ключ:
@GeneratedValue(strategy = GenerationType.AUTO)
один из способов решить вышеуказанную проблему будет переопределить
hashcode().
Также сбросьте сеанс гибернации до и после сохранения.getHibernateTemplate().flush();явно устанавливая отсоединенный объект в
nullтоже помогает.
Это означает, что вы пытаетесь сохранить несколько строк в таблице с ссылкой на один и тот же объект.
проверьте свойство id вашего класса сущностей.
@Id private Integer id;до
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(unique = true, nullable = false) private Integer id;
просто наткнулся на это сообщение, но в коде c#. Не уверен, что это актуально (точно такое же сообщение об ошибке, хотя).
Я отлаживал код с точками останова и расширил некоторые коллекции через частные члены, в то время как отладчик был в точке останова. После повторного запуска кода, не копаясь в структурах, сообщение об ошибке исчезло. Похоже, что акт просмотра частных лениво загруженных коллекций заставил NHibernate загружать вещи, которые не должны были быть загружен в то время (потому что они были в частных членов).
сам код завернут в довольно сложную транзакцию, которая может обновлять большое количество записей и многие зависимости как часть этой транзакции (процесс импорта).
надеюсь, ключ к любому другому, кто сталкивается с этой проблемой.
найдите атрибут "Каскад" в Hibernate и удалите его. Когда вы устанавливаете "Каскад" доступным, он будет вызывать другие операции (сохранение, обновление и удаление) на других сущностях, которые имеют отношение к связанным классам. Так же будет происходить идентификация стоимости. Это сработало со мной.
У меня была эта ошибка несколько дней подряд, и я слишком много времени потратил на исправление этой ошибки.
public boolean save(OrderHeader header) { Session session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); try { session.save(header); for (OrderDetail detail : header.getDetails()) { session.save(detail); } transaction.commit(); session.close(); return true; } catch (HibernateException exception) { exception.printStackTrace(); transaction.rollback(); return false; } }прежде чем я получу эту ошибку , я не упомянул тип генерации ID на объекте OrderDetil. когда без генерации идентификатора Orderdetails он сохраняет идентификатор как 0 для каждого объекта OrderDetail. это то, что #jbx объяснил. Да это лучший ответ. это один пример того, как это происходит.
попробуйте разместить код вашего запроса, прежде. Это исправит мою проблему. например, изменить это:
query1 query2 - get the error updateдля этого:
query2 query1 update
я столкнулся с проблемой из-за того, что генерация первичного ключа неверна, когда я вставляю строку следующим образом:
public void addTerminal(String typeOfDevice,Map<Byte,Integer> map) { // TODO Auto-generated method stub try { Set<Byte> keySet = map.keySet(); for (Byte byte1 : keySet) { Device device=new Device(); device.setNumDevice(DeviceCount.map.get(byte1)); device.setTimestamp(System.currentTimeMillis()); device.setTypeDevice(byte1); this.getHibernateTemplate().save(device); } System.out.println("hah"); }catch (Exception e) { // TODO: handle exception logger.warn("wrong"); logger.warn(e.getStackTrace()+e.getMessage()); } }изменить класс ID генератор удостоверение
<id name="id" type="int"> <column name="id" /> <generator class="identity" /> </id>
в моем случае только flush () не работал. Мне пришлось использовать clear () после flush ().
public Object merge(final Object detachedInstance) { this.getHibernateTemplate().flush(); this.getHibernateTemplate().clear(); try { this.getHibernateTemplate().evict(detachedInstance); } }
Comments