JPA级联持久化和对分离实体的引用会引发PersistentObjectException。为什么? [英] JPA cascade persist and references to detached entities throws PersistentObjectException. Why?

查看:163
本文介绍了JPA级联持久化和对分离实体的引用会引发PersistentObjectException。为什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个实体Foo引用一个实体吧:

  @Entity 
public class Foo {

@OneToOne(cascade = {PERSIST,MERGE,REFRESH},fetch = EAGER)
public Bar getBar(){
return bar;
}
}

当我坚持一个新的Foo,它可以得到一个参考新的酒吧或现有的酒吧。当它获得一个现在的Bar,这恰好是分开的,我的JPA提供者(Hibernate)抛出了以下异常:

  :org.hibernate.PersistentObjectException:分离的实体传递给persist:com.example.Bar 
在org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:102)
在org.hibernate。 impl.SessionImpl.firePersist(SessionImpl.java:636)
在org.hibernate.impl.SessionImpl.persist(SessionImpl.java:628)
在org.hibernate.engine.EJB3CascadingAction $ 1.cascade(EJB3CascadingAction .java:28)
在org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:291)
在org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:239)
在org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
在org.hibernate.engine.Cascade.cascade(Cascade.java:153)
在org.hibernate.event。 def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:454)
在org.hi bernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288)
在org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
在org.hibernate.event。 def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
在org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
在org.hibernate.event.def.DefaultPersistEventListener。 entityIsTransient(DefaultPersistEventListener.java:154)
在org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
在org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener。 java:61)
在org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:645)
在org.hibernate.impl.SessionImpl.persist(SessionImpl.java:619)
在org.hibernate.impl.SessionImpl.persist(SessionImpl.java:623)
在org.hi bernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:220)
... 112更多

当我确定对Bar的引用进行管理(附加)时,或者在关联中省略级联PERSIST时,都可以正常工作。



100%满意。如果我删除级联持续,我显然不能坚持一个Foo参考一个新的酒吧了。引用Bar进行管理需要遵循以下代码:

  if(foo.getBar()。getID()! = null&!entityManager.contains(foo.getBar())){
foo.setBar(entityManager.merge(foo.getUBar()));
}
entityManager.persist(foo);

对于单个酒吧,这可能不是一件大事,但如果我必须采取所有属性考虑到这一点,我最终会得到相当可怕的代码,似乎打败了使用ORM的原因。我可能会很好地使用JDBC来持久化我的对象图。



当给定一个现有的Bar引用时,JPA必须做的唯一的事情是将其ID并插入到表中的一列,包含Foo。当Bar被附加时,它完全是这样的,但是当Bar分离时抛出异常。



我的问题是为什么需要酒吧?当Bar实例从分离状态转换到附加状态时,它的ID不会改变,而且该ID似乎是唯一需要的。



这可能是一个错误在Hibernate还是我缺少某些东西?

解决方案

可以使用 merge()而不是 persist()在这种情况下:

  foo = entityManager.merge(FOO); 

应用于新实例时, merge()使其持久化(实际上 - 返回具有相同状态的持久化实例),并且合并级联引用,因为您尝试手动执行。


I have an entity Foo that references an entity Bar:

@Entity
public class Foo {

    @OneToOne(cascade = {PERSIST, MERGE, REFRESH}, fetch = EAGER)
    public Bar getBar() {
        return bar;
    }
}

When I persist a new Foo, it can get a reference to either a new Bar or an existing Bar. When it gets an existing Bar, which happens to be detached, my JPA provider (Hibernate) throws the following exception:

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.example.Bar
 at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:102)
 at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:636)
 at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:628)
 at org.hibernate.engine.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:28)
 at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:291)
 at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:239)
 at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
 at org.hibernate.engine.Cascade.cascade(Cascade.java:153)
 at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:454)
 at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288)
 at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
 at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:130)
 at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
 at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
 at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
 at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
 at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:645)
 at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:619)
 at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:623)
 at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:220)
 ... 112 more

When I either make sure the reference to Bar is managed (attached) or when I omit the cascade PERSIST in the relation, all works well.

Neither solution however is 100% satisfactory. If I remove the cascade persist, I obviously can't persist a Foo with a reference to a new Bar anymore. Making the reference to Bar managed necessitates code like this prior to persisting:

if (foo.getBar().getID() != null && !entityManager.contains(foo.getBar())) {
    foo.setBar(entityManager.merge(foo.getUBar()));
}
entityManager.persist(foo);

For a single Bar this might not seem like a big deal, but if I have to take all properties into account like this I'll end up with pretty horrible code that seems to defeat the reason of using ORM in the first place. I might as well well persist my object graph manually using JDBC again.

When given an existing Bar reference the only thing JPA has to do is take its ID and insert that in a column of the table that holds Foo. It does exactly this when Bar is attached, but throws the exception when Bar is detached.

My question is; why does it need Bar to be attached? Surely its ID won't change when the Bar instance transitions from detached to attached state, and that ID seems to be the only thing needed here.

Is this perhaps a bug in Hibernate or am I missing something?

解决方案

You can use merge() instead of persist() in this case:

foo = entityManager.merge(foo); 

When applied to the new instance, merge() makes it persistent (actually - returns the persistent instance with the same state), and merges cascaded references, as you try to do manually.

这篇关于JPA级联持久化和对分离实体的引用会引发PersistentObjectException。为什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆