更新包含删除孤儿配置的集合时,发生HibernateException:无法保存父对象

时间:2019-04-26 17:11:39

标签: java hibernate jpa hibernate-mapping

我从事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映射类,因此我正在考虑向第一个数据库打开第一个会话,读取hibernate Contrat对象,将children元素中的id设置为null并将该对象写入具有第二个会话的目标数据库。它应该更快。

我为第二个用例编写了一个测试类,并且该过程失败,并带有以下异常:

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

我认为将id设置为null时引用必须更改,但是我不确定:我不知道更改Collection成员的字段如何可以更改Collection引用

请注意,如果我从配置中删除DELETE_ORPHAN,则一切正常,所有对象及其依赖项均写入数据库。 因此,我想使用更快的休眠解决方案,但我必须保留DELETE_ORPHAN功能,因为应用程序当前使用此功能来确保从元素集中删除的每个MonElement都将在数据库中删除。 我不需要此功能,但无法删除它。

另外,我需要将MonElement id设置为null以便生成新的id,因为它们在第一个数据库中的id可能存在于目标数据库中。

这是我编写的代码,当删除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();

这比管理自己的查询,主键/外键以及对象之间的依赖关系要快得多。

有人有摆脱这种异常的想法吗? 或者,也许我应该更改Set的状态。 实际上,我并不是要删除此Set中的任何元素,我只是希望将它们视为新对象。

如果找不到解决方案,我将做一些肮脏的事情:在新项目中复制所有休眠实体对象,并在新创建的Contrat中删除DELETE_ORPHAN参数。 因此,应用程序将继续使用其映射,而我的新项目将使用我的特定映射。但我想避免这种情况。

谢谢

1 个答案:

答案 0 :(得分:0)

crizzis 写了一个正确的解决方案,作为对我的问题的评论。 我引用他:

  

在尝试与新会话保持合同之前,我会尝试将contrat.elements包装在新集合中(contrat.setElements(new HashSet <>(contrat.getElements()))

效果很好。