拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 Hibernate 的“Detached Entity Passed to Persist”错误

Hibernate 的“Detached Entity Passed to Persist”错误

白鹭 - 2022-07-05 2317 0 2

一、概述

在本文中,我们将了解 Hibernate 的 ,它在尝试保存分离的实体时发生。PersistentObjectException

我们将首先了解状态的含义以及 Hibernate 的化和方法之间的区别。 之后,我们将在各种用例中重现该错误并查看如何修复它。detachedpersistmerge

2. 分离实体

让我们先简要回顾一下状态是什么以及它与实体生命周期的关系。detached

detached的实体是不再被跟踪的 Java 对象。 如果我们关闭或清除会话,实体可以达到这种状态。 类似地,我们可以通过手动将实体从持久性上下文中移除来分离实体。 persistence context

我们将在本文中的代码示例中使用和实体。 要分离特定的实体,我们可以使用 。 此外,我们可以通过使用清除会话来从上下文中分离所有实体。PostCommentPostsession.evict(post)session.clear()

例如,一些测试将需要一个分离的 。 那么,让我们看看我们如何实现这一点:Post

@Before
public void beforeEach() {
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();

this.detachedPost = new Post("Hibernate Tutorial");
session.persist(detachedPost);
session.evict(detachedPost);
}

首先,我们持久化实体,然后使用将其分离。Postsession.evict(post)

3. 试图坚持一个分离的实体

如果我们尝试持久化一个分离的实体,Hibernate 将抛出一个并带有“分离的实体传递给持久化”错误消息。PersistenceException

让我们尝试持久化一个分离的实体并期待这个异常:Post

@Test
public void givenDetachedPost_whenTryingToPersist_thenThrowException() {
detachedPost.setTitle("Hibernate Tutorial for Absolute Beginners");

assertThatThrownBy(() -> session.persist(detachedPost))
.isInstanceOf(PersistenceException.class)
.hasMessageContaining("org.hibernate.PersistentObjectException: detached entity passed to persist");
}

为了避免这种情况,我们应该了解实体状态并使用适当的方法来保存它。

如果我们使用 merge 方法,Hibernate 将根据字段**实体重新附加到持久化上下文**:**Id**merge

@Test
public void givenDetachedPost_whenTryingToMerge_thenNoExceptionIsThrown() {
detachedPost.setTitle("Hibernate Tutorial for Beginners");

session.merge(detachedPost);
session.getTransaction().commit();

List<Post> posts = session.createQuery("Select p from Post p", Post.class).list();
assertThat(posts).hasSize(1);
assertThat(posts.get(0).getTitle())
.isEqualTo("Hibernate Tutorial for Beginners");
}

同样,我们也可以使用其他 Hibernate 特定的方法,例如 、 和 。 与化和这些方法不是 JPA 规范的一部分。 因此,如果我们想使用 JPA 抽象,我们应该避免它们。updatesavesaveOrUpdatepersistmerge,

4. 试图通过关联来维持一个分离的实体

对于这个例子,我们将介绍实体:Comment

@Entity
public class Comment {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String text;

@ManyToOne(cascade = CascadeType.MERGE)
private Post post;

// constructor, getters and setters
}

我们可以注意到实体与具有多对一的关系。CommentPost

级联类型设置为 。 因此,我们只会将操作传播到关联的 。CascadeType.MERGEmergePost

换句话说,如果我们一个实体,Hibernate 会将操作传播到关联的并且两个实体都将在数据库中更新。 但是,如果我们想使用此设置 ,我们必须首先关联的 :mergeCommentPostpersistCommentmergePost

@Test
public void givenDetachedPost_whenMergeAndPersistComment_thenNoExceptionIsThrown() {
Comment comment = new Comment("nice article!");
Post mergedPost = (Post) session.merge(detachedPost);
comment.setPost(mergedPost);

session.persist(comment);
session.getTransaction().commit();

List<Comment> comments = session.createQuery("Select c from Comment c", Comment.class).list();
Comment savedComment = comments.get(0);
assertThat(savedComment.getText()).isEqualTo("nice article!");
assertThat(savedComment.getPost().getTitle())
.isEqualTo("Hibernate Tutorial");
}

另一方面,如果级联类型设置为PERSISTALL,Hibernate将尝试在分离的关联字段上传播持久操作。 因此,当我们使用这些级联类型之一化实体时,Hibernate 将化关联的分离的, 这将导致另一个 。persistPostpersistCommentPersistentObjectException

5.结论

在本文中,我们讨论了 Hbernate 的并了解了其主要原因。PersistentObjectException

我们可以通过正确使用Hibernate的、、和方法来避免它。savepersistupdatemergesaveOrUpdate

此外,对JPA级联类型的良好利用将防止在我们的实体关联中发生。PersistentObjectException


标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *