Ошибка гибернации: другой объект с тем же значением идентификатора уже был связан с сеансом

По существу у меня есть некоторые объекты в этой конфигурации (реальная модель данных немного сложнее):

  • 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 не останавливался на точках останова, решило эту проблему.