Org.hibernate.ObjectNotFoundException: не существует строки с данным идентификатором
У меня была проблема с Hibernate 4.1.8, что привело к следующему исключению:
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [test.hibernate.TestPrepravkaOsobaSAdresou$Uvazek#2]
У меня простая ассоциация OneToMany между двумя объектами:
@Entity(name = "Ppv")
@Table(name = "PPV")
public static class Ppv {
@Id
Long ppvId;
@OneToMany(fetch = FetchType.EAGER, mappedBy = "ppv")
Set<Uvazek> uvazeks = new HashSet<Uvazek>(0);
}
@Entity(name = "Uvazek")
@Table(name = "UVAZEK")
public static class Uvazek {
@Id
Long uvazekId;
@ManyToOne
@JoinColumn(name = "PPV_FXID")
Ppv ppv;
}
и тестовый случай, когда у меня есть один Ppv и два Увасек. Когда я загружаю и отсоединяю Ppv, удаляю один Uvazek, связанный с загруженным Ppv и слияние Ppv, я получаю исключение.
jdbcTemplate.execute("insert into PPV values(1)");
jdbcTemplate.execute("insert into UVAZEK values(2, 1)");
jdbcTemplate.execute("insert into UVAZEK values(3, 1)");
Ppv ppv = (Ppv) getSession().get(Ppv.class, 1l);
getSession().clear();
getSession().delete(getSession().get(Uvazek.class, 2l));
getSession().flush();
getSession().merge(ppv);
getSession().flush(); //Causes the exception
Во время слияния Ppv Hibernate пытается загрузить удаленный Uvazek. Несмотря на то, что Uvazek удален, Hibernate все еще имеет информацию об этом в
org.hibernate.collection.internal.AbstractPersistentCollection.storedSnapshot
на uvazek, установленном на отдельном Ppv. В предыдущей версии (< 4.1.8) это работает. В этом простом примере я могу восстановить его, добавив orphanRemoval=true
на uvazeks, установленный на Ppv, и вместо удаления uvazek удалите его из uvazeks, установленного на Ppv.
Итак, мой вопрос: это ошибка Hibernate или моя плохая практика?
Ответы
Ответ 1
Проблема заключается в том, что Uvazek с id = 2 пытается объединиться. Hibernate видит, что у него есть ключ, но он не знает, загрязнен ли объект, поэтому неясно, должно ли быть сделано обновление SQL.
Но поскольку ключ равен 2, Hibernate знает, что объект должен существовать в базе данных, поэтому он пытается загрузить объект, чтобы сравнить его с версией, только что полученной в памяти, чтобы увидеть, есть ли у объекта некоторые ожидающие изменения для синхронизации с базой данных.
Но выбор не возвращает никаких результатов, поэтому Hibernate имеет противоречивую информацию: с одной стороны, база данных говорит, что объект не существует. С другой стороны, объект в памяти говорит, что объект должен существовать с ключом 2. Невозможно решить, что правильно, поэтому бросается ObjectNotFoundException
.
Что случилось, так это то, что перед этой версией код случайно основывался на ошибке, которая тем временем была исправлена, поэтому это больше не работает.
Лучшая практика заключается в том, чтобы избежать ясности и использовать ее только при необходимости в качестве оптимизации памяти, очищая только те объекты, которые, как вы знаете, не будут изменены или необходимы в том же сеансе, посмотрите на Является ли Session clear() считающимся вредным.
Ответ 2
Вам также нужно удалить ссылку на Uvazek из Ppv. В противном случае Hibernate пытается восстановить отношение, когда вы объедините его обратно и не получится, потому что вы удалили ссылку Uvazek.
Вот почему добавление удаления сирот для вас.
Ответ 3
У меня было такое же исключение Hibernate.
После отладки в какой-то момент я понял, что проблема вызвана дочерними записями Orphan.
Как многие жалуются, когда они ищут запись, она существует. То, что я понял, заключается в том, что проблема связана не с наличием записи, а с гибернацией, которая не находит ее в таблице, скорее из-за дочерних записей для детей-сирот.
Записи, которые ссылаются на несуществующих родителей!
Что я сделал, найдите ссылки внешнего ключа, соответствующие таблице, связанной с Bean.
Чтобы найти ссылки на внешние ключи в разработчике SQL
1. Сохраните приведенный ниже код XML в файл (fk_reference.xml)
<items>
<item type="editor" node="TableNode" vertical="true">
<title><![CDATA[FK References]]></title>
<query>
<sql>
<![CDATA[select a.owner,
a.table_name,
a.constraint_name,
a.status
from all_constraints a
where a.constraint_type = 'R'
and exists(
select 1
from all_constraints
where constraint_name=a.r_constraint_name
and constraint_type in ('P', 'U')
and table_name = :OBJECT_NAME
and owner = :OBJECT_OWNER)
order by table_name, constraint_name]]>
</sql>
</query>
</item></items>
2. Добавьте расширение USER DEFINED в SQL Developer
Чтобы найти записи сирот во всех упомянутых таблицах
выберите * из CHILD_TABLE где FOREIGNKEY не включен (выберите PRIMARYKEY из PARENT_TABLE);
Удалить эти сиротские записи, При необходимости внести изменения и перезапустить сервер.
Это решило мое исключение. Вы можете попробовать то же самое.