带有 JPA/EJB 代码的“分离实体传递给持久错误”
Posted
技术标签:
【中文标题】带有 JPA/EJB 代码的“分离实体传递给持久错误”【英文标题】:"detached entity passed to persist error" with JPA/EJB code 【发布时间】:2011-01-27 08:22:38 【问题描述】:我正在尝试运行这个基本的 JPA/EJB 代码:
public static void main(String[] args)
UserBean user = new UserBean();
user.setId(1);
user.setUserName("name1");
user.setPassword("passwd1");
em.persist(user);
我收到此错误:
javax.ejb.EJBException: javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.JPA.Database
有什么想法吗?
我在网上搜索,我找到的原因是:
这是由您创建对象的方式引起的,即如果您明确设置了 ID 属性。删除 ID 分配修复了它。
但我没弄明白,我需要修改什么才能让代码正常工作?
【问题讨论】:
【参考方案1】:这里 .persist() 只会插入记录。如果我们使用 .merge() 它将检查当前ID是否存在任何记录,如果有存在,它将更新,否则将插入一条新记录。
【讨论】:
我花了很多时间才得到您的答复。非常感谢!【参考方案2】:如果你在你的数据库中设置id为主键并自增,那么这行代码是错误的:
user.setId(1);
试试这个:
public static void main(String[] args)
UserBean user = new UserBean();
user.setUserName("name1");
user.setPassword("passwd1");
em.persist(user);
【讨论】:
我试过了,我知道得到这个错误:detached entity passed to persist
【参考方案3】:
发生错误是因为设置了对象的 ID。 Hibernate 区分瞬态对象和分离对象,persist
仅适用于瞬态对象。如果persist
断定对象已分离(因为设置了 ID,它将返回),它将返回“已分离对象传递给持久性”错误。您可以在here 和here 找到更多详细信息。
但是,这仅适用于如果您已指定要自动生成的主键:如果该字段配置为始终手动设置,则您的代码有效。
【讨论】:
当我说我使用的是 JPA 而不是 Hibernate 时,我在技术上是否正确,所以上面的陈述不应该适用吗?我是 JPA 的新手(准确地说是 3 个:P) Sun 的 JPA Javadoc (java.sun.com/javaee/5/docs/api/javax/persistence/…) 和 Toplink 的完全一样,建议您在技术上是正确的。然而,这真的归结为规范所说的 persist() 应该表现得像什么,可悲的是,我不知道那是什么。 这个解决方案对我有用。我正在持久化一个对象,将 ID 保存在某个地方。然后我想用一个新值覆盖现有的对象,所以我创建了对象,将 ID 复制回来,当我尝试保存时它爆炸了。最后,我不得不重新查询数据库 (findById),进行更改,然后持久化 那个 对象。【参考方案4】:如果您使用在您的实体中生成id = GenerationType.AUTO
策略。
将user.setId (1)
替换为user.setId (null)
,问题就解决了。
【讨论】:
【参考方案5】:我知道这有点太晚了,而且每个人都得到了答案。但还有一点要补充:当设置了 GenerateType 时,对象上的 persist() 预计会生成一个 id。
如果用户已经为 Id 设置了值,则休眠将其视为已保存记录,因此将其视为已分离。
如果 id 为 null - 在这种情况下,当类型为 AUTO 或 IDENTITY 等时会引发空指针异常,除非 id 是从表或序列等生成的。
设计:当表有一个 bean 属性作为主键时会发生这种情况。 仅当自动生成 id 时才必须设置 GenerateType。 删除它,插入应该与用户指定的 id 一起使用。 (将属性映射到主键字段是一个糟糕的设计)
【讨论】:
【参考方案6】:删除
user.setId(1);
因为它是在数据库上自动生成的, 并继续使用 persist 命令。
【讨论】:
【参考方案7】:我遇到了这个问题,是二级缓存造成的:
-
我使用 hibernate 持久化了一个实体
然后我删除了从不与二级缓存交互的单独进程创建的行
我持久化了另一个具有相同标识符的实体(我的标识符值不是自动生成的)
因此,由于缓存没有失效,休眠假设它正在处理同一实体的分离实例。
【讨论】:
【参考方案8】:假设您有两个实体 Album
和 Photo
。相册包含许多照片,因此是一对多的关系。
专辑类
@Entity
public class Album
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
Integer albumId;
String albumName;
@OneToMany(targetEntity=Photo.class,mappedBy="album",cascade=CascadeType.ALL,orphanRemoval=true)
Set<Photo> photos = new HashSet<Photo>();
照片类
@Entity
public class Photo
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
Integer photo_id;
String photoName;
@ManyToOne(targetEntity=Album.class)
@JoinColumn(name="album_id")
Album album;
在persist或merge之前你要做的是在每张照片中设置相册引用。
Album myAlbum = new Album();
Photo photo1 = new Photo();
Photo photo2 = new Photo();
photo1.setAlbum(myAlbum);
photo2.setAlbum(myAlbum);
这是在持久化或合并之前附加相关实体的方法。
【讨论】:
如果使用泛型,则无需使用“targetEntity=Photo.class” 嗨,在将相册对象设置为 photo1 和 photo2 后...我们必须保存什么对象来保存照片或相册列表?【参考方案9】:我得到了答案,我正在使用:
em.persist(user);
我用merge代替了persist:
em.merge(user);
但不知道,为什么坚持不起作用。 :(
【讨论】:
这不是解决办法! 我知道,但这对我有用。这里还有其他对您有用的答案吗?如果是,那我会非常乐意选择它。 之所以有效,是因为您的对象已从休眠会话中分离,或者对象是瞬态的,但休眠将其视为已分离,因为您声明了主键,使用合并您将对象再次附加到会话中使您能够更新数据库中的对象;见:docs.jboss.org/hibernate/core/3.3/reference/en/html/…以上是关于带有 JPA/EJB 代码的“分离实体传递给持久错误”的主要内容,如果未能解决你的问题,请参考以下文章
结合 JPA、EJB 和 JSF 托管 bean 的首选设计模式是啥?
如何使用 JPA/EJB3 和 WildFly 读取未提交的数据?