Помилка hibernate: 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, спробуйте зберегти цей об’єкт, і, можливо, вже є об’єкт із тим самим первинним ключем, пов’язаним із сеансом.

Приклад

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

Таким простим підходом буде перевірка, чи оновлювали / вилучали ви будь-яке значення в таблиці, яка є частиною чогось, і пізніше ви намагаєтесь знову вставити ті самі об'єкти


4
Приємна відповідь². Первинним ключем була моя проблема, вирішена за допомогою встановлення GeneratedValue послідовності для postgresql.
Родріго Феррарі

2
У мене було те саме питання. У моєму випадку у мене був об'єкт, який шукали в коді, і я намагався створити новий об'єкт з тим же ідентифікатором в іншому фрагменті коду, тоді як перший об'єкт ще був на сесії сплячого режиму.
dellasavia

18

Це лише один момент, коли сплячий створює більше проблем, ніж вирішує. У моєму випадку є багато об’єктів з однаковим ідентифікатором 0, оскільки вони нові та не мають жодного. ДБ їх породжує. Десь я прочитав, що 0 сигналів не встановлено. Інтуїтивно зрозумілий спосіб їх зберегти - це повторення над ними та вимова до сплячого режиму для збереження предметів. Але ви не можете цього зробити - "Звичайно, ви повинні знати, що сплячий працює так і тому, тому вам доведеться .." Тож тепер я можу спробувати змінити Ідс на Лонг замість на довгий і подивитися, чи працює він. Врешті-решт, простіше зробити це за допомогою простого картографа самостійно, адже сплячий режим - це лише додаткове непрозоре навантаження. Інший приклад: спроба зчитувати параметри з однієї бази даних та зберігати їх в іншій змушує вас майже всю роботу виконувати вручну.


13
Я теж ненавиджу сплячку ... і бази даних ... Після всіх проблем, які вони викликали у мене, я думаю, що легше використовувати текстовий файл (жартую, але все ж ...).
Ігор Попов

У моєму випадку є багато об’єктів з однаковим ідентифікатором 0, оскільки вони нові та не мають жодного. ДБ їх породжує. Десь я прочитав, що 0 сигналів не встановлено. Інтуїтивно зрозумілий спосіб їх зберегти - це повторення над ними та вимова до сплячого режиму для збереження предметів . Мені потрібно зробити саме це. Не могли б ви сказати мені, який "сплячий спосіб" це зробити?
Рамзес

У моєму випадку мені довелося використовувати session.merge (мій об’єкт), оскільки у мене було два екземпляри цього об'єкта. Зазвичай це відбувається, коли сутність зберігається і відключається від сеансу. Ще один екземпляр цієї суб'єкта господарювання просить перейти в сплячку. Цей другий екземпляр залишився приєднаним до сесії. Перший екземпляр модифікований. Більше на getj2ee.over-blog.com/…
Reddymails

це не спляча проблема, а нерозуміння того, як це працює
ACV

17

USe 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, було викинуто виключення дублікатаObject.
  Я міг це вирішити, видаливши каскад з одного кінця.


Вирішили мою проблему разом із stackoverflow.com/questions/4334970/…
Magno C

5

Ви можете використовувати session.merge(obj), якщо ви робите збереження при різних сесіях з однаковим постійним об'єктом ідентифікатора.
Це спрацювало, у мене було те саме питання раніше.


4

Я також зіткнувся з цією проблемою і мені важко було знайти помилку.

Проблема у мене була така:

Об’єкт був прочитаний Дао з іншого сплячого сеансу.

Щоб уникнути цього винятку, просто перечитайте об’єкт разом із дао, який збирається потім зберегти / оновити цей об’єкт.

так:

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();

4

Ця проблема виникає, коли ми оновлюємо той самий об’єкт сеансу, який ми використовували для отримання об’єкта з бази даних.

Ви можете використовувати метод злиття сплячки замість методу оновлення.

Наприклад, спочатку використовуйте session.get (), а потім ви можете використовувати session.merge (об'єкт). Цей метод не створить жодних проблем. Ми також можемо використовувати метод merge () для оновлення об'єкта в базі даних.


3

Отримайте об’єкт всередині сеансу, ось приклад:

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

3
Приємна спроба, але об’єкт "ob" повертається без оновлених даних (враховуючи, що він був оновлений за допомогою програми під час виконання, між об'єктами, що виходять із бази даних, та їх збереженням).
Олексій

2

Чи правильні ваші відображення Id? Якщо база даних відповідає за створення ідентифікатора через ідентифікатор, вам потрібно зіставити ваш об'єкт використання.


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);
}

2

перед тим, як починаються об'єкти, що повторюються, слід закрити сеанс, а потім розпочати новий сеанс

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

тому таким чином за один сеанс існує не більше ніж одна сутність, яка має один ідентифікатор.


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

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


1

Перевірте, чи забули ви поставити стовпчик @GenerateValue для @Id. У мене були такі ж проблеми з багатьма до багатьох стосунків між фільмом та жанром. Програма кинула Hibernate Error: org.hibernate.NonUniqueObjectException: інший об'єкт із тим самим значенням ідентифікатора вже був пов’язаний з помилкою сеансу. Пізніше я з’ясував, що мені просто потрібно переконатися, що ви маєте @GenerateValue до методу get GenreId.


як я можу застосувати ваше рішення до мого питання? чи потрібно створити стовпчик ідентифікатора в класі Завдання? stackoverflow.com/questions/55732955 / ...
sbattou

1

просто перевірте ідентифікатор, чи потрібен він нульовий або 0 подібний

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

додайте або оновіть, де вміст встановлено від форми до Pojo


1

Я новачок у NHibernate, і моя проблема полягала в тому, що я використовував інший сеанс для запиту свого об’єкта, ніж я його врятував. Тому сеанс збереження не знав про об'єкт.

Це здається очевидним, але, читаючи попередні відповіді, я шукав всюди 2 об’єкти, а не 2 сеанси.


1

@GeneratedValue (strategy = GenerationType.IDENTITY), додавання цієї примітки до властивості первинного ключа у вашому об'єкті має вирішити цю проблему.


1

Я вирішив цю проблему.
Насправді це відбувається, тому що ми забули реалізацію властивості типу Generator 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

Java:

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

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. Встановіть атрибут класу тегу генератора під унікальним стовпцем identity:

    <generator class="identity">

Примітка. Встановіть унікальний стовпець, identityа не assigned.


0

Інша річ, яка працювала для мене, - це зробити те, що екземпляр мінливий Long замість long


У мене була мінлива основного ключа long id; змінивши його на Long id; працювали

Всього найкращого


0

Ви завжди можете робити сеанс флеш. Flush синхронізує стан усіх ваших об’єктів під час сеансу (будь-ласка, хтось виправить мене, якщо я помиляюся), і, можливо, це вирішить вашу проблему в деяких випадках.

Реалізація власних рівних та хеш-коду може вам також допомогти.


0

Ви можете перевірити свої параметри каскаду. Це може спричинити налаштування каскаду на ваших моделях. Я видалив налаштування каскаду (фактично не дозволяючи вставити / оновити каскад), і це вирішило мою проблему


0

Я також виявив цю помилку. Що для мене працювало - це переконатися, що первинний ключ (який автоматично генерується) - це не PDT (тобто long, int, ect.), А об'єкт (тобто Long, Integer тощо)

Коли ви створюєте об'єкт для його збереження, переконайтеся, що ви пропустите нуль, а не 0.



0

Я вирішив подібну проблему, як таку:

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.