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



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




  • 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] еще одна вещь, которую я забыл упомянуть, заключается в том, что (по архитектурным причинам, не зависящим от меня) каждое чтение или запись должны выполняться в отдельном сеансе.

1544   15  

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 и удалите его. Когда вы устанавливаете "Каскад" доступным, он будет вызывать другие операции (сохранение, обновление и удаление) на других сущностях, которые имеют отношение к связанным классам. Так же будет происходить идентификация стоимости. Это сработало со мной.

добавить аннотацию @GeneratedValue к Бобу, который вы вставляете.

У меня была эта ошибка несколько дней подряд, и я слишком много времени потратил на исправление этой ошибки.

 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

    Ничего не найдено.