Ошибка гибернации: другой объект с тем же значением идентификатора уже был связан с сеансом
По существу у меня есть некоторые объекты в этой конфигурации (реальная модель данных немного сложнее):
- 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"
. Я знаю, что спящий режим не может вставлять/обновлять/удалять один и тот же объект дважды в одном сеансе, но существует ли какой-то способ этого? Это не похоже, что это было бы редкостью ситуации.
Во время моего исследования этой проблемы я видел, как люди предлагают использовать session.merge()
, но когда я это делаю, любые "конфликтующие" объекты вставляются в базу данных в виде пустых объектов со всеми значениями, равными нулю. Ясно, что это не то, что мы хотим.
[Edit] Еще одна вещь, о которой я забыл упомянуть, заключается в том, что (по причинам, не зависящим от архитектуры) каждое чтение или запись необходимо выполнять в отдельном сеансе.
Ответы
Ответ 1
Скорее всего, это связано с тем, что объекты B не относятся к одному экземпляру объекта Java C. Они относятся к одной и той же строке в базе данных (т.е. К одному и тому же первичному ключу), но это разные копии.
Итак, происходит то, что сеанс Hibernate, который управляет сущностями, будет отслеживать, какой Java-объект соответствует строке с тем же самым первичным ключом.
Один из вариантов заключается в том, чтобы убедиться, что объекты объектов B, относящиеся к одной и той же строке, фактически ссылаются на один и тот же экземпляр объекта C. Альтернативно отключите каскадирование для этой переменной-члена. Таким образом, когда B сохраняется, C нет. Однако вам придется сохранять C вручную отдельно. Если C - таблица типа/категории, то, вероятно, имеет смысл быть таким образом.
Ответ 2
Просто установите каскад в MERGE, это должно сделать трюк.
Ответ 3
Вам нужно всего лишь сделать одно. Запустите session_object.clear()
и затем сохраните новый объект. Это очистит сеанс (как точно названный) и удалит оскорбительный дублирующий объект из сеанса.
Ответ 4
Я согласен с @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();
}
}
Person.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
, позже я обнаружил, что забыл автоматически увеличить мой первичный ключ!
Мое решение состоит в том, чтобы добавить этот код в ваш первичный ключ:
@GeneratedValue(strategy = GenerationType.AUTO)
Ответ 5
Перенести задачу назначения идентификатора объекта из Hibernate в базу данных с помощью:
<generator class="native"/>
Это решило проблему для меня.
Ответ 6
Один из способов решения вышеуказанной проблемы - переопределить hashcode()
.
Также очистите сеанс гибернации до и после сохранения.
getHibernateTemplate().flush();
Также явная привязка выделенного объекта к null
.
Ответ 7
Добавить аннотацию
@GeneratedValue
к bean, который вы вставляете.
Ответ 8
Это означает, что вы пытаетесь сохранить несколько строк в вашей таблице со ссылкой на один и тот же объект.
проверьте свойство id вашего Entity Class.
@Id
private Integer id;
в
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(unique = true, nullable = false)
private Integer id;
Ответ 9
Просто наткнулся на это сообщение, но в коде С#. Не уверен, что это релевантно (точно такое же сообщение об ошибке).
Я отлаживал код с точками останова и расширил некоторые коллекции через частные члены, в то время как отладчик находился в точке останова. После повторного запуска кода без копания в структурах сообщение об ошибке исчезнет. Похоже, что поиск частных коллекций с ленивой загрузкой заставил NHibernate загружать вещи, которые в то время не были загружены (потому что они были в частных членах).
Сам код заключен в довольно сложную транзакцию, которая может обновлять большое количество записей и многих зависимостей как часть этой транзакции (процесс импорта).
Надеюсь, ключ к кому-то еще, кто сталкивается с проблемой.
Ответ 10
Найдите атрибут "Каскад" в спящем режиме и удалите его. Когда вы устанавливаете доступный "Каскад", он будет вызывать другие операции (сохранять, обновлять и удалять) на других объектах, которые имеют отношения со связанными классами. То же самое значение идентичности будет происходить.
Он работал со мной.
Ответ 11
У меня была эта ошибка несколько дней подряд, и я потратил слишком много времени на исправление этой ошибки.
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;
}
}
Прежде чем я получу эту ошибку, у меня не было упомянутого типа генерации идентификатора объекта OrderDetil. когда без генерации идентификатора Orderdetails он сохраняет Id как 0 для каждого объекта OrderDetail. это то, что #jbx объяснил. Да, это лучший ответ. этот пример, как это происходит.
Ответ 12
Попробуйте поместить код вашего запроса раньше.
Это исправить мою проблему.
например измените это:
query1
query2 - get the error
update
:
query2
query1
update
Ответ 13
вы не можете устанавливать идентификатор объекта перед вызовом запроса на обновление.
Ответ 14
Я встретил проблему из-за того, что генерация первичного ключа ошибочна, когда я вставляю такую строку:
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>
Ответ 15
В моем случае только flush() не работал. Я должен был использовать clear() после flush().
public Object merge(final Object detachedInstance)
{
this.getHibernateTemplate().flush();
this.getHibernateTemplate().clear();
try
{
this.getHibernateTemplate().evict(detachedInstance);
}
}
Ответ 16
если вы используете EntityRepository, тогда используйте saveAndFlush вместо save
Ответ 17
Если оставить открытую вкладку выражений в моей среде IDE, которая делала вызов get Hibernate для объекта, вызывающего это исключение. Я пытался удалить этот же объект. Также у меня была точка останова на вызове удаления, которая, кажется, необходима, чтобы эта ошибка произошла. Простое создание другой вкладки выражений в качестве передней вкладки или изменение настроек, чтобы ide не останавливался на точках останова, решило эту проблему.