带有 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】:

假设您有两个实体 AlbumPhoto。相册包含许多照片,因此是一对多的关系。

专辑类

@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 读取未提交的数据?

从 JPA/EJB3 持久性上下文中分离实体

Mysql/Glassfish 资源 jdbc 无效

使用任务控制进行 Java 分析 - 我在哪里可以看到我自己的方法的花费时间?

在 JBoss/WildFly 中注入 EntityManager