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

У меня есть два пользовательских объекта, и пока я пытаюсь сохранить объект, используя

session.save(userObj);

Я получаю следующую ошибку:

Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
[com.pojo.rtrequests.User#[email protected]]

Я создаю сеанс, используя

BaseHibernateDAO dao = new BaseHibernateDAO();          

rtsession = dao.getSession(userData.getRegion(),
                           BaseHibernateDAO.RTREQUESTS_DATABASE_NAME);

rttrans = rtsession.beginTransaction();
rttrans.begin();

rtsession.save(userObj1);
rtsession.save(userObj2);

rtsession.flush();
rttrans.commit();

rtsession.close(); // in finally block

Я также попытался сделать session.clear() перед сохранением, все равно не повезло.

Это для первого я получаю объект сеанса, когда приходит запрос пользователя, поэтому я понимаю, почему он говорит, что объект присутствует в сеансе.

Любые предложения?

Ответ 1

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

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

Я бы предложил вам разбить код, то есть прокомментировать биты, пока ошибка не исчезнет, ​​а затем верните код до тех пор, пока он не вернется, и вы должны найти ошибку.

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

Какой первичный генератор ключей вы используете?

Причина, по которой я спрашиваю, заключается в том, что эта ошибка связана с тем, как вы сообщаете hibernate, чтобы определить постоянное состояние объекта (т.е. постоянный объект или нет). Ошибка может произойти из-за того, что hibernate пытается сохранить и объект, который уже является постоянным. Фактически, если вы используете save hibernate, он попытается сохранить этот объект и, возможно, уже есть объект с тем же самым первичным ключом, связанным с сеансом.

Пример

Предполагая, что у вас есть объект класса hibernate для таблицы с 10 строками на основе комбинации первичных ключей (столбец 1 и столбец 2). Теперь вы удалили 5 строк из таблицы в некоторый момент времени. Теперь, если вы попытаетесь добавить те же 10 строк снова, в то время как hibernate пытается сохранить объекты в базе данных, 5 строк, которые были уже удалены, будут добавлены без ошибок. Теперь оставшиеся 5 строк, которые уже существуют, будут выбрасывать это исключение.

Таким образом, простой подход будет проверять, если вы обновили/удалили любое значение в таблице, которая является частью чего-то, а позже вы пытаетесь снова вставить те же объекты.

Ответ 2

Это только одна точка, в которой спящий режим создает больше проблем, чем решает. В моем случае есть много объектов с тем же идентификатором 0, потому что они новы и не имеют одного. ДБ их генерирует. Где-то я прочитал, что 0 сигналов Id не установлен. Интуитивно понятный способ их сохранения - итерация над ними и использование спящего режима для сохранения объектов. Но вы не можете этого сделать - "Конечно, вы должны знать, что спящий режим работает так и так, поэтому вам нужно..." Поэтому теперь я могу попытаться изменить идентификаторы на Long, а не на длинные и посмотреть, будет ли это работать. В конце концов, проще сделать это с помощью простого картографа самостоятельно, потому что спящий режим является просто дополнительным непрозрачным бременем. Другой пример: попытка считывать параметры из одной базы данных и сохранять их в другой, заставляет вас почти все работать вручную. Но если вам все равно придется это делать, использование спящего режима - это просто дополнительная работа.

Ответ 3

USe session.evict(object); Функция метода evict() используется для удаления экземпляра из кеша сеанса. Поэтому для первого сохранения объекта сохраните объект, вызвав метод session.save(object) перед выселением объекта из кеша. Аналогичным образом обновите объект, вызвав session.saveOrUpdate(object) или session.update(object) перед вызовом evict().

Ответ 4

Как уже указывал выше, я столкнулся с этой проблемой, когда у меня было cascade=all на обоих концах отношения one-to-many, поэтому пусть предполагается A → B (один-ко-многим из A и много- один из B) и обновлял экземпляр B в A, а затем вызывал saveOrUpdate (A), это приводило к циклическому запросу сохранения, то есть сохранению триггеров A, сохраняющих B, которые запускают сохранение A... и в третьем случае as сущность (из A) пыталась быть добавлена ​​в sessionPersistenceContext, было выбрано исключение duplicateObject.
Я мог бы решить это, удалив каскад с одного конца.

Ответ 5

Это может произойти, если вы использовали тот же объект сеанса для чтения и записи. Как? Предположим, вы создали один сеанс. Вы читаете запись из таблицы сотрудников с использованием первичного ключа Emp_id = 101 Теперь вы изменили запись на Java. И вы собираетесь сохранить запись Employee в базе данных. мы здесь не закрывали сессию. Поскольку объект, который был прочитан, также сохраняется в сеансе. Он конфликтует с объектом, который мы хотим написать. Отсюда возникает эта ошибка.

Ответ 6

Получить объект внутри сеанса, вот пример:

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);

Ответ 7

Я столкнулся с этой проблемой:

  • Удаление объекта (с использованием HQL)
  • Сразу же сохраняя новый объект с тем же идентификатором

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

String delQuery = "DELETE FROM OasisNode";
session.createQuery( delQuery ).executeUpdate();
session.flush();
session.clear();

Ответ 8

Я также столкнулся с этой проблемой и с трудом нашел ошибку.

У меня была проблема:

Объект был прочитан Dao с другим сеансом спящего режима.

Чтобы избежать этого исключения, просто перечитайте объект с dao, который впоследствии сохранит/обновит этот объект.

так:

class A{      

 readFoo(){
       someDaoA.read(myBadAssObject); //Different Session than in class B
    }

}

class B{



 saveFoo(){
       someDaoB.read(myBadAssObjectAgain); //Different Session than in class A
       [...]
       myBadAssObjectAgain.fooValue = 'bar';
       persist();
    }

}

Надеюсь, что сэкономить много людей!

Ответ 9

Правильны ли ваши сопоставления идентификаторов? Если база данных отвечает за создание идентификатора через идентификатор, вам нужно сопоставить свой пользовательский объект с этим.

Ответ 10

Проверьте, не забыл ли вы поместить столбец @GenerateValue для столбца @Id. У меня была такая же проблема со многими и многими отношениями между фильмом и жанром. Программа бросила Ошибка гибернации: org.hibernate.NonUniqueObjectException: другой объект с тем же значением идентификатора уже был связан с сеансом ошибка. Я узнал позже, что мне просто нужно убедиться, что у вас есть @GenerateValue для метода GenreId get.

Ответ 11

просто проверьте идентификатор, принимает ли он значение null или 0, например

if(offersubformtwo.getId()!=null && offersubformtwo.getId()!=0)

добавить или обновить, где содержимое задано из формы в Pojo

Ответ 12

Я столкнулся с этой проблемой при удалении объекта, ни выселение, ни явное не помогли.

/**
 * Deletes the given entity, even if hibernate has an old reference to it.
 * If the entity has already disappeared due to a db cascade then noop.
 */
public void delete(final Object entity) {
  Object merged = null;
  try {
    merged = getSession().merge(entity);
  }
  catch (ObjectNotFoundException e) {
    // disappeared already due to cascade
    return;
  }
  getSession().delete(merged);
}

Ответ 13

Попробуйте это. Ниже я работал у меня!

В файле hbm.xml

  • Нам нужно установить атрибут dynamic-update тега класса true:

    <class dynamic-update="true">
    
  • Установите атрибут класса тега генератора в уникальном столбце identity:

    <generator class="identity">
    

Примечание. Установите уникальный столбец identity, а не assigned.

Ответ 14

У меня была аналогичная проблема. В моем случае я забыл установить значение increment_by в базе данных таким же, как те, что использовались cache_size и allocationSize. (Стрелки указывают на указанные атрибуты)

SQL:

CREATED         26.07.16
LAST_DDL_TIME   26.07.16
SEQUENCE_OWNER  MY
SEQUENCE_NAME   MY_ID_SEQ
MIN_VALUE       1
MAX_VALUE       9999999999999999999999999999
INCREMENT_BY    20 <-
CYCLE_FLAG      N
ORDER_FLAG      N
CACHE_SIZE      20 <-
LAST_NUMBER     180

Java:

@SequenceGenerator(name = "mySG", schema = "my", 
sequenceName = "my_id_seq", allocationSize = 20 <-)

Ответ 15

У меня такая же ошибка, что и я заменил свой Set новым, полученным от Jackson.

Чтобы решить эту проблему, я сохраняю существующий набор, я удаляю из старого набора неизвестный элемент в новый список с помощью retainAll. Затем я добавляю новые с addAll.

    this.oldSet.retainAll(newSet);
    this.oldSet.addAll(newSet);

Не нужно иметь сеанс и манипулировать им.

Ответ 16

Еще одна вещь, которая работала для меня, заключалась в том, чтобы сделать переменную экземпляра Long вместо длинной


У меня была моя ключевая переменная long id; изменение его на Long id; работал

Все лучшее

Ответ 17

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

Реализация собственных equals и hashcode также может помочь вам.

Ответ 18

Вы можете проверить свои настройки каскада. Настройки Cascade на ваших моделях могут быть причиной этого. Я удалил Cascade Settings (по сути, не разрешая Cascade Inserts/Updates), и это решило мою проблему

Ответ 19

Я тоже нашел эту ошибку. Что сработало для меня, так это убедиться, что первичный ключ (который автоматически генерируется) не является PDT (т.е. Long, int, ect.), А является объектом (т.е. Long, Integer и т.д.)

Когда вы создаете свой объект для его сохранения, убедитесь, что вы передаете значение null, а не 0.

Ответ 20

Я новичок в NHibernate, и моя проблема заключалась в том, что я использовал другой сеанс для запроса моего объекта, чем для его сохранения. Таким образом, сеанс сохранения не знал об объекте.

Кажется очевидным, но, прочитав предыдущие ответы, я искал везде 2 объекта, а не 2 сеанса.

Ответ 21

Помогает ли это?

User userObj1 = new User();
User userObj2 = userObj1;
.
.
.
rtsession.save(userObj1);
rtsession.save(userObj2); 

Ответ 22

@GeneratedValue (strategy = GenerationType.IDENTITY), добавление этой аннотации к свойству первичного ключа в вашей сущности bean должно решить эту проблему.

Ответ 23

Я решил эту проблему.
На самом деле это происходит потому, что мы забыли реализовать свойство Generator Type PK в классе bean. Поэтому сделайте его любым типом, как

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;

когда мы сохраняем объекты bean, каждый объект получает тот же ID, поэтому первый объект сохраняется, когда другой объект сохраняется, а затем HIB FW через этот тип Exception: org.hibernate.NonUniqueObjectException: - другой объект с тем же значением идентификатора уже связанный с сеансом.

Ответ 24

Я решил аналогичную проблему:

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();

Ответ 25

до позиции начала повторяющихся объектов, вы должны закрыть сеанс и затем вы должны начать новый сеанс

session.close();      
session = HibernateUtil.getSessionFactory().openSession();

Таким образом, таким образом за один сеанс существует не более одного объекта с одинаковым идентификатором.

Ответ 26

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

  • Это происходит потому, что вы неправильно настроили файл mapping.xml для полей id, как показано ниже: -

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
    
  • Перегрузите метод getession, чтобы принять параметр, такой как isSessionClear, и очистите сеанс перед возвратом текущего сеанса, как показано ниже

    public static Session getSession(boolean isSessionClear) {
        if (session.isOpen() && isSessionClear) {
            session.clear();
            return session;
        } else if (session.isOpen()) {
            return session;
        } else {
            return sessionFactory.openSession();
        }
    }
    

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

Ответ 27

Один из способов решения этой проблемы - попытаться прочитать объект обратно из кэша hibernate/db, прежде чем делать какие-либо обновления, а затем сохраняться.

Пример:

            OrderHeader oh = orderHeaderDAO.get(orderHeaderId);
            oh.setShipFrom(facilityForOrder);
            orderHeaderDAO.persist(oh);

Примечание. Имейте в виду, что это не устраняет основную причину, но решает проблему.

Ответ 28

В отличие от wbdarby, это даже может произойти, когда объект извлекается, указывая идентификатор объекта на HQL. В случае попытки изменить поля объекта и сохранить его обратно в БД (модификация может быть вставлена, удалена или обновлена) за тот же сеанс, эта ошибка появится. Попробуйте очистить сеанс гибернации перед сохранением измененного объекта или создать совершенно новый сеанс.



Надеюсь, я помог; -)

Ответ 29

Поздно к вечеринке, но может помочь приходящим пользователям -

У меня возникла эта проблема, когда я выберите запись, используя getsession() и снова обновить другую запись с тем же идентификатором, используя session вызывает проблему. Добавлен код ниже.

Customer existingCustomer=getSession().get(Customer.class,1);
Customer customerFromUi;// This customer details comiong from UI with identifer 1

getSession().update(customerFromUi);// Here the issue comes

Это никогда не должно быть сделано. Решение - это либо выселение, либо обновление, либо изменение бизнес-логики.

Ответ 30

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

РЕШЕНИЕ: каждая функция должна открывать и закрывать сеанс

session.getTransaction.begin(); /* your operation */ session.close()