JPA EntityManger 在合并时执行数据库插入?

发布于 2024-07-19 03:29:27 字数 3456 浏览 4 评论 0原文

我正在使用 JPA 和 Hibernate,但我在合并工作时遇到了困难, 但在描述我在使用 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 合并的文章,我仍然无法理解这里发生的事情。

谢谢你的时间。

I am using JPA with Hibernate underneath, and I am having trouble getting merge to work,
but before I describe the problems I am encountering with JPA, let me lay out what I am trying to accomplish, just in case my problems stems from my approach.

I have data from on system that I need to place into another system. To do this I am reading the data then constructing new ORM objects based on that data, I then want to persist the ORM objects into the database. Now when the database is empty, the program works without issue, with a simple em.persist(object) call. However I want to be able to run this process when the database has data in it, adding new data and updating old data as necessary, this is where I am having issues.

I do a simple check to see if the item already exists in the database, if nothing is found, I persist, if the record exists, I attempt a merge. Which fails with a duplicate record error;

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

It strikes me as strange that the em.merge() call is attempting a insert instead of a update (I confirmed this through SQL logging).

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

I should note that I am using objects that cascade into sub-objects. The failure is occurring on a sub-object, which makes sense to me as I would expect it to try and merge the sub-objects first.

Below is my code, please excuse the rough state, I've tried to document a few workaround approaches here.

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

If anyone with JPA experience can help me out I would really appreciate it. The differances between Hibernate's saveOrUpdate() and JPA's merge() are obviously tripping me up, but despite reading several articles on EnityManger's merge, I still can't wrap my head around what is going on here.

Thanks for your time.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

十年不长 2024-07-26 03:29:27

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

感谢任何读过我的长得可笑的问题的人,希望我的问题对将来的人有用。

The curse of StackOverflow has struck again. After working on this problem for about a day I decide to post this question, within 20 minutes I had an eureka moment and solved it. Partly because I had clarified my thoughts enough to post the question. In thinking about what information might be pertinent to the question I realized that my auto generated keys were to blame (or my correctly, my stupid non-handling of them on a merge).

My issue is that SubStuff (worst substitute naming scheme, sorry about that) has an autogenerated artificial primary key. So when merging I needed to do;

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

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

This sets the primary key to the row that already exists in the database and upon initial testing seems to work fine.

The call to merge can be simplified down to just the call to em.merge(stuff) as that will cascade the substuff objects and the mergedsubstuff collection can be removed all together as we are no longer doing a merge inside that loop.

Thanks to anyone who read through my ridiculously long question, hopefully my question will be useful to someone in the future.

孤凫 2024-07-26 03:29:27

您的问题在于以下几行:

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

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

Your problem is with the following couple of lines:

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

JPA will consider new collection as complete new set of sub-entities and will always insert them as such. Keep working with original collection of SubStuff within Stuff entity and you'll be fine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文