“分离实体传递到持久错误”与 JPA/EJB 代码

问题描述 投票:0回答:10

我正在尝试运行这个基本的 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 分配即可修复该问题。

但是我不明白,我需要修改什么才能使代码正常工作?

java jpa ejb-3.0
10个回答
140
投票

该错误是因为设置了对象的ID而发生的。 Hibernate 区分瞬态对象和分离对象,并且

persist
仅适用于瞬态对象。如果
persist
断定对象已分离(因为设置了 ID,因此会分离),它将返回“分离的对象已传递到持久化”错误。您可以在此处此处找到更多详细信息。

但是,这仅适用于如果您已指定要自动生成的主键:如果该字段配置为始终手动设置,则您的代码可以工作。


51
投票

ERD

假设您有两个实体

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;

}

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

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

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

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


25
投票

删除

user.setId(1);

因为它是在数据库上自动生成的, 并继续执行 persist 命令。


14
投票

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

em.persist(user);

我使用合并代替坚持:

em.merge(user);

但不知道,为什么坚持不起作用。 :(


11
投票

如果您用于在实体中生成

id = GenerationType.AUTO
策略。

user.setId (1)
替换为
user.setId (null)
,问题就解决了。


7
投票

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


5
投票

我知道有点太晚了,希望每个人都能得到答案。但还需要补充一点:当设置GenerateType时,对象上的persist()预计会生成一个id。

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

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

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


3
投票

如果你在数据库中设置id为主键并且自增,那么这行代码是错误的:

user.setId(1);

试试这个:

public static void main(String[] args){
         UserBean user = new UserBean();
         user.setUserName("name1");
         user.setPassword("passwd1");
         em.persist(user);
  }

2
投票

我遇到了这个问题,是二级缓存造成的:

  1. 我使用 hibernate 持久化一个实体
  2. 然后我删除了从不与二级缓存交互的单独进程创建的行
  3. 我保留了具有相同标识符的另一个实体(我的标识符值不是自动生成的)

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


0
投票

您可以分配 ID,然后保留一个实体,例如,如果您的 ID 由用户的电子邮件表示。但是,要使其正常工作,您需要确保您的 ID 字段没有设置 @GenerateValue。

例如,如果您的实体具有电子邮件 ID 字段并设置了 @GenerateValue,则此代码将引发异常:

public class UserEntity {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    @Column(name = "email")
    private String email;

    ...
}

并尝试保留这样的对象:

UserEntity user = new UserEntity();
user.setEmail(email);
entityManager.getTransaction().begin();
entityManager.persist(user);
entityManager.getTransaction().commit();

因此,为了使其正常工作,您需要删除@GenerateValue,但保留@ID:

public class UserEntity {
    @Id
    @Column(name = "email")
    private String email;

    ...
}
© www.soinside.com 2019 - 2024. All rights reserved.