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

114

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

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#com.pojo.rtrequests.User@d079b40b]

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

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()перед сохранением, но все равно не повезло.

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

Какие-либо предложения?

суровый
источник
Вот еще одна замечательная ветка, которая помогла решить мою проблему getj2ee.over-blog.com/…
Reddymails,

Ответы:

173

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

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

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

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

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

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

пример

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

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

Майкл Уайлс
источник
4
Хороший ответ². Первичный ключ был моей проблемой, решенной с помощью GeneratedValue, установив последовательность для postgresql.
Родриго Феррари
2
Я была такая же проблема. В моем случае у меня был объект, который искали в коде, и я пытался создать новый объект с тем же идентификатором в другом фрагменте кода, пока первый объект еще находился в сеансе гибернации.
dellasavia
18

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

Hein
источник
13
Я тоже ненавижу спящий режим ... и базы данных ... После всех проблем, которые они вызвали у меня, я думаю, что проще использовать текстовый файл (я шучу, но все же ...).
Игорь Попов
В моем случае есть много объектов с одинаковым идентификатором 0, потому что они новые и их нет. БД их генерирует. Где-то я читал, что 0 сигналов не задано. Интуитивно понятный способ сохранить их - перебрать их и сказать, что спящий режим сохраняет объекты . Мне нужно сделать именно это. Не могли бы вы сказать мне, какой "способ гибернации" сделать это?
Рамзес
В моем случае мне пришлось использовать session.merge (myobject), поскольку у меня было два экземпляра этого объекта. Обычно это происходит, когда сущность сохраняется и отключается от сеанса. Другой экземпляр этого объекта запрашивается для перехода в спящий режим. Этот второй экземпляр остался присоединенным к сеансу. Доработан первый экземпляр. Подробнее на getj2ee.over-blog.com/…
Reddymails
это не проблемы с
ACV
17

ИСПОЛЬЗОВАНИЕ session.evict(object);Функция evict()метода используется для удаления экземпляра из кеша сеанса. Итак, при первом сохранении объекта, сохраните объект, вызвав session.save(object)метод перед удалением объекта из кеша. Таким же образом обновите объект, вызвав session.saveOrUpdate(object)или session.update(object)перед вызовом evict ().

Рахул Н
источник
11

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

Прашант Бари
источник
9

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

Redzedi
источник
Решил мою проблему вместе с stackoverflow.com/questions/4334970/…
Magno C
5

Вы можете использовать session.merge(obj), если выполняете сохранение с разными сеансами с постоянным объектом с одинаковым идентификатором.
Это сработало, раньше у меня была такая же проблема.

Паван Кумар
источник
4

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

У меня была следующая проблема:

Объект был прочитан Дао с другим сеансом гибернации.

Чтобы избежать этого исключения, просто перечитайте объект с помощью 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();
    }

}

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

Фритхоф
источник
4

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

  1. Удаление объекта (с помощью HQL)
  2. Немедленное сохранение нового объекта с тем же идентификатором

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

String delQuery = "DELETE FROM OasisNode";
session.createQuery( delQuery ).executeUpdate();
session.flush();
session.clear();
Wbdarby
источник
4

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

Вы можете использовать метод слияния спящего режима вместо метода обновления.

например, сначала используйте session.get (), а затем вы можете использовать session.merge (объект). Этот метод не создаст никаких проблем. Мы также можем использовать метод merge () для обновления объекта в базе данных.

Джайендра Биртария
источник
3

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

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);
sock_osg
источник
3
Хорошая попытка, но объект «ob» возвращается без обновленных данных (учитывая, что он был обновлен через приложение во время выполнения, между получением объектов из базы данных и ее сохранением).
Alex
2

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

cwap
источник
2

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

/**
 * 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);
}
Timp
источник
2

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

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

таким образом, в одном сеансе не может быть более одной сущности с одним и тем же идентификатором.

engtuncay
источник
2

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

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

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

Такого делать нельзя. Решение - либо удалить сеанс перед обновлением, либо изменить бизнес-логику.

vipin cp
источник
1

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

Thupten
источник
как я могу применить ваше решение к моему вопросу? создать столбец идентификатора в классе задач? stackoverflow.com/questions/55732955/…
sbattou
1

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

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

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

Аян Саркар
источник
1

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

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

ДастинА
источник
1

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

Джей
источник
1

Я решил эту проблему.
На самом деле это происходит потому, что мы забыли о реализации свойства PK типа генератора в классе bean-компонента. Так что сделайте его любым, например, как

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

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

Анкит Шарма
источник
1

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

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

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
  2. Перегрузите метод getsession, чтобы он принял параметр, например 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, это должно сработать для вас.

Рахул Кумар
источник
1

У меня была похожая проблема. В моем случае я забыл установить 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

Ява:

@SequenceGenerator(name = "mySG", schema = "my", 
sequenceName = "my_id_seq", allocationSize = 20 <-)
kaba713
источник
1

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

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

Араш морадабади
источник
1

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

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

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

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

Эрель
источник
1

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

В hbm.xmlфайле

  1. Нам нужно установить для dynamic-updateатрибута тега класса значение true:

    <class dynamic-update="true">
  2. Установите для атрибута class тега генератора в столбце unique значение identity:

    <generator class="identity">

Примечание. Установите для уникального столбца значение, identityа не assigned.

RaGa__M
источник
0

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


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

Всего наилучшего


источник
0

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

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

caarlos0
источник
0

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

user1609848
источник
0

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

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

Жако Ван Никерк
источник
0

Это помогает?

User userObj1 = new User();
User userObj2 = userObj1;
.
.
.
rtsession.save(userObj1);
rtsession.save(userObj2); 
Джордж Папатеодору
источник
0

Я решил подобную проблему:

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
user3132194
источник