使用JPA / EJB代码的“传递给持久错误的分离实体”


81

我正在尝试运行以下基本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分配即可解决。

但是我没有得到,我必须修改什么才能使代码正常工作?

Answers:


50

ERD

假设您有两个实体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;

}

保留或合并之前,您要做的是在每张照片中设置“相册参考”。

        Album myAlbum = new Album();
        Photo photo1 = new Photo();
        Photo photo2 = new Photo();

        photo1.setAlbum(myAlbum);
        photo2.setAlbum(myAlbum);       

这是在持久或合并之前附加相关实体的方法。


2
如果您使用泛型,则无需使用“ targetEntity = Photo.class”
Rollerball

嗨,在将相册对象设置为photo1和photo2 ...之后,我们要保存“照片”或“相册”列表的对象是什么?
Dilanka Rathnayake

129

发生此错误是因为设置了对象的ID。Hibernate区分临时对象和分离对象,并且persist仅适用于临时对象。如果persist断定对象已分离(将因为设置了ID而将其分离),它将返回“已分离的对象传递给持久对象”错误。您可以在此处此处找到更多详细信息。

但是,这仅您指定要自动生成的主键时才适用:如果将字段配置为始终手动设置,那么您的代码将起作用。


当我说我使用的是JPA而不是Hibernate时,我在技术上是正确的,因此上述声明不适用吗?我是JPA的新手(准确地说是3次

Sun的JPA Javadoc(java.sun.com/javaee/5/docs/api/javax/persistence/…)和Toplink的Java完全相同,建议您在技术上是正确的。但是,它实际上可以归结为规范中所说的persist()应该表现为的样子,可悲的是,我不知道那是什么。
Tomislav Nakic-Alfirevic

这个解决方案对我有用。我坚持一个对象,将ID存储在某个地方。然后,我想用一个新值覆盖该现有对象,因此我创建了该对象,将ID复制回去,并且在尝试保存时它被炸毁。最后,我不得不重新查询数据库(findById),进行更改,然后保留对象。
cs94njw 2012年


12

我得到了答案,我正在使用:

em.persist(user);

我用merge代替了persist:

em.merge(user);

但是不知道,为什么坚持没有用。:(


11
这不是解决方案!
Ammar Bozorgvar 2011年

1
我知道,但这对我有用。还有其他适合您的答案吗?如果是的话,那么我会很乐意选择它。
zengr 2011年

4
之所以起作用,是因为您的对象已从休眠会话中分离出来,或者该对象是暂时的,但是由于您声明了主键,所以休眠将其视为已分离,因此使用合并将对象再次附加到会话中,这使您可以更新数据库中的对象; 见:docs.jboss.org/hibernate/core/3.3/reference/en/html/...


5

我知道为时已晚,并且每个人都得到了答案。但是要添加的更多一点:设置GenerateType时,预期对象上的persist()会生成一个id。

如果已经有一个由用户设置为Id的值,则hibernate会将其视为已保存的记录,因此将其视为已分离。

如果id为null-在这种情况下,当类型为AUTO或IDENTITY等时,将引发null指针异常,除非id是从表或序列中生成的。

设计:当表具有bean属性作为主键时会发生这种情况。仅当自动生成ID时,才必须设置GenerateType。删除此选项,插入内容应使用用户指定的ID。(将属性映射到主键字段是一个糟糕的设计)


5

在这里.persist()仅会插入记录。如果我们使用.merge()它将检查是否存在具有当前ID的记录,如果存在,则将更新,否则将插入新记录。


在得到您的答复之前,这花了我很多时间。非常感谢你!
Thach Van

3

如果您在数据库中将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
s1ddok

2

我遇到了这个问题,它是由二级缓存引起的:

  1. 我使用冬眠坚持了一个实体
  2. 然后,我删除了从一个与第二级缓存不交互的单独进程创建的行
  3. 我坚持使用相同标识符的另一个实体(我的标识符值不会自动生成)

因此,因为缓存没有失效,所以hibernate假定它正在处理同一实体的分离实例。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.