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

Удалить эти сиротские записи, При необходимости внести изменения и перезапустить сервер.

Это решило мое исключение. Вы можете попробовать то же самое.