HibernateException при обновлении коллекции, настроенной для удаления сироты: не удается сохранить родительский объект - PullRequest
0 голосов
/ 26 апреля 2019

Я работаю над проектом Java, и мне нужно написать новый модуль для копирования некоторых данных из одной базы данных в другую (те же таблицы).

У меня есть объект Contrat, содержащий несколько полей и следующее поле:

@OneToMany(mappedBy = "contrat", fetch = FetchType.LAZY)
@Fetch(FetchMode.SUBSELECT)
@Cascade( { org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })
@BatchSize(size = 50)
private Set<MonElement> elements = new HashSet<MonElement>();

Я должен прочитать некоторые объекты "Contrat" ​​из базы данных и записать их в другую базу данных.

Я колеблюсь между 2 решениями:

  • используйте jdbc, чтобы запросить первую базу данных и получить объекты, а затем записать эти объекты во вторую базу данных (обращая внимание на порядок и различные ключи). Это будет долго.
  • , так как проект в настоящее время использует Hibernate и содержит все классы отображения hibernate, я думал об открытии первого сеанса для первой базы данных, чтении объекта Contrat hibernate, установке нулевых идентификаторов в дочерних элементах и ​​записи объекта в база данных назначения со вторым сеансом. Это должно быть быстрее.

Я написал тестовый класс для второго варианта использования, и процесс завершился неудачей со следующим исключением:

    org.hibernate.HibernateException: Don't change the reference to a 
    collection with cascade="all-delete-orphan"

Я думаю, что ссылка должна измениться, когда я установил для идентификаторов значение null, но я не уверен: я не понимаю, как изменение поля члена коллекции может изменить ссылку на коллекцию

Обратите внимание, что если я удаляю DELETE_ORPHAN из конфигурации, все работает, все объекты и их зависимости записываются в базу данных. Поэтому я хотел бы использовать решение гибернации, которое работает быстрее, но я должен сохранить функцию DELETE_ORPHAN, потому что приложение в настоящее время использует эту функцию, чтобы гарантировать, что каждый MonElement, удаленный из набора элементов, будет удален в базе данных. Мне не нужна эта функция, но я не могу ее удалить.

Кроме того, мне нужно установить нулевые идентификаторы MonElement, чтобы генерировать новые, поскольку их идентификаторы в первой базе данных могут существовать в целевой базе данных.

Вот код, который я написал, который хорошо работает, когда я удаляю опцию DELETE_ORPHAN.

    SessionFactory sessionFactory = new AnnotationConfiguration().configure("/hibernate.cfg.src.xml").buildSessionFactory();
    Session session = sessionFactory.openSession();
    // search the Contrat object
    Criteria crit = session.createCriteria(Contrat.class);
    CriteriaUtil.addEqualCriteria(crit, "column", "65465454");
    Contrat contrat = (Contrat)crit.list().get(0);

    session.close();

    SessionFactory sessionFactoryDest = new AnnotationConfiguration().configure("/hibernate.cfg.dest.xml").buildSessionFactory();
    Session sessionDest = sessionFactoryDest.openSession();
    Transaction transaction = sessionDest.beginTransaction();

    // setting id to null, also for the elements in the elements Set
    contrat.setId(null);
    for (MonElement element:contrat.getElements()) {
        element.setId(null);
    }
    // writing the object in the database
    sessionDest.save(contrat);
    transaction.commit();
    sessionDest.flush();
    sessionDest.close();

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

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

Если я не найду решение, я сделаю что-то грязное: продублирую все объекты спящего объекта в моем новом проекте и удалим параметр DELETE_ORPHAN во вновь созданном Контрасте. Таким образом, приложение будет продолжать использовать свое отображение, а мой новый проект будет использовать мое конкретное отображение. Но я хочу этого избежать.

Спасибо

1 Ответ

0 голосов
/ 04 мая 2019

Правильное решение было написано crizzis в качестве комментария к моему вопросу. Я цитирую его:

Я бы попробовал обернуть элементы contrat.elements в новую коллекцию (contrat.setElements (new HashSet <> (contrat.getElements ())), прежде чем пытаться сохранить контракт с новым сеансом

Хорошо работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...