我在下面使用JPA和Hibernate,但是我无法将合并工作, 但在我描述我遇到的与JPA有关的问题之前,让我列出我想要完成的事情,以防我的问题源于我的方法。

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

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

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合并的文章,我仍然无法理解这里发生的事情。

感谢您的时间。

有帮助吗?

解决方案

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对象,mergedsubstuff集合可以一起删除,因为我们不再在该循环内进行合并。

感谢所有读过我这个荒谬冗长问题的人,希望我的问题对未来的某些人有用。

其他提示

您的问题在于以下几行:

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top