2

我在下面使用带有 Hibernate 的 JPA,我无法让合并工作,但在我描述我在使用 JPA 时遇到的问题之前,让我列出我想要完成的工作,以防万一我的问题源于我的方法.

我有来自系统的数据,需要放入另一个系统。为此,我正在读取数据,然后基于该数据构造新的 ORM 对象,然后我想将 ORM 对象持久保存到数据库中。现在,当数据库为空时,程序可以正常运行,只需调用 em.persist(object) 即可。但是,我希望能够在数据库中有数据时运行此过程,添加新数据并根据需要更新旧数据,这就是我遇到问题的地方。

我做一个简单的检查,看看该项目是否已经存在于数据库中,如果没有找到,我坚持,如果记录存在,我尝试合并。失败并出现重复记录错误;

ERROR JDBCExceptionReporter - Violation of UNIQUE KEY constraint 'UK-SubStuff-StuffId-SubStuffNumber'. Cannot insert duplicate key in object 'SubStuff'.

让我感到奇怪的是 em.merge() 调用正在尝试插入而不是更新(我通过 SQL 日志记录确认了这一点)。

Hibernate: insert into SubStuff (SubStuffNumber, StuffId, Name, TypeId) values (?, ?, ?, ?)

我应该注意我正在使用级联成子对象的对象。失败发生在子对象上,这对我来说很有意义,因为我希望它首先尝试合并子对象。

以下是我的代码,请原谅粗略的状态,我已尝试在此处记录一些解决方法。

private void storeData(Collection<Stuff> Stuffs) {

    for (Stuff stuff : Stuffs) {
        //I think this first block can be safely ignored, as I am having no issues with it
        //  Left it in just in case someone more experianced then I sees the root of the issue here.
        Collection<SubStuff> subStuffs = stuff.getSubStuffCollection();
        for (SubStuff s : subStuffs) {
            //Persist SubStuff Type, which DOES NOT cascade,
            //  due to it not having an internal SubStuff collection
            Query q = em.createNamedQuery("SubStuffType.findByType");
            q.setParameter("type", f.getTypeId().getType());
            try {
                SubStuffType sst = (SubStuffType) q.getSingleResult();
                s.setTypeId(sst);
            } catch (NoResultException ex) {
                if (logger.isDebugEnabled()) logger.debug("SubStuff Type not found, persisting");
                em.persist(s.getTypeId());
            }
        }

        if (em.find(Stuff.class, stuff.getId()) == null) {
            //Persist on Stuffs will cascade to SubStuffs
            em.persist(stuff);
        } else {
            //  Failing to merge SubStuff, tries to insert duplicate
            //  Merge SubStuff first
            // The block below is my attempt to merge the SubStuff Collection before merging Stuff,
            //  it creates the same isuse as a straight merge of Stuff.
            Collection<SubStuff> mergedSubStuffs = new ArrayList<SubStuff>(SubStuffs.size());
            for (SubStuff s : SubStuffs) {
                Query q = em.createNamedQuery("SubStuff.findBySubStuffNumberStuffId");
                q.setParameter("SubStuffNumber", s.getSubStuffNumber());
                q.setParameter("StuffId", stuff.getId());
                try {
                    SubStuff subStuff = (SubStuff) q.getSingleResult();
        // -----> Merge fails, with an duplicate insert error
                    SubStuff mergedSubStuff = em.merge(s);
                    mergedSubStuffs.add(mergedSubStuff);
                } catch (NoResultException ex) {
                    throw ex;
                }
            }
            stuff.setSubStuffCollection(mergedSubStuffs);

        // -----> This will fails with same error as above, if I remove the attempt
            //  to merge the sub objects
            em.merge(stuff);
        }
    }
}

如果有 JPA 经验的人可以帮助我,我将不胜感激。Hibernate 的 saveOrUpdate() 和 JPA 的 merge() 之间的差异显然让我感到困惑,但是尽管阅读了几篇关于 EnityManger 合并的文章,我仍然无法理解这里发生的事情。

谢谢你的时间。

4

2 回答 2

3

StackOverflow 的诅咒再次袭来。在解决这个问题大约一天后,我决定发布这个问题,在 20 分钟内我有一个灵光乍现的时刻并解决了它。部分原因是我已经澄清了我的想法,足以发布这个问题。在考虑哪些信息可能与该问题相关时,我意识到应该归咎于我的自动生成的密钥(或者我的正确,我在合并时愚蠢地不处理它们)。

我的问题是 SubStuff(最差的替代命名方案,对此感到抱歉)有一个自动生成的人工主键。所以在合并时我需要做;

SubStuff subStuff = (SubStuff) q.getSingleResult();
//++++++++
s.setId(subStuff.getId());
//++++++++

//The following code can be removed.
//--- SubStuff mergedSubStuff = em.merge(f);
//--- mergedSubStuffs.add(mergedSubStuff);

这会将主键设置为数据库中已经存在的行,并且在初始测试时似乎工作正常。

对 merge 的调用可以简化为仅对 em.merge(stuff) 的调用,因为这将级联 substuff 对象,并且由于我们不再在该循环内进行合并,因此可以一起删除 mergesubstuff 集合。

感谢阅读我冗长的问题的任何人,希望我的问题将来对某人有用。

于 2009-04-29T18:20:07.613 回答
1

您的问题在于以下几行:

Collection<SubStuff> mergedSubStuffs = new ArrayList<SubStuff>(SubStuffs.size());
...
stuff.setSubStuffCollection(mergedSubStuffs);

JPA 会将新集合视为完整的新子实体集,并将始终照此插入它们。继续使用 Stuff 实体中的原始 SubStuff 集合,你会没事的。

于 2009-04-29T16:31:16.737 回答