我在下面使用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合并的文章,我仍然无法理解这里发生的事情。
感谢您的时间。
答案 0 :(得分: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对象,mergedsubstuff集合可以一起删除,因为我们不再在该循环内进行合并。
感谢所有读过我这个荒谬冗长问题的人,希望我的问题对未来的某些人有用。
答案 1 :(得分:1)
您的问题在于以下几行:
Collection<SubStuff> mergedSubStuffs = new ArrayList<SubStuff>(SubStuffs.size());
...
stuff.setSubStuffCollection(mergedSubStuffs);
JPA会将新集合视为完整的新子集实体,并始终将其插入。继续在Stuff实体中使用SubStuff的原始集合,你会没事的。