一、概述
在本文中,我们将了解 Hibernate 的 ,它在尝试保存分离的实体时发生。PersistentObjectException
我们将首先了解状态的含义以及 Hibernate 的化和方法之间的区别。 之后,我们将在各种用例中重现该错误并查看如何修复它。detached
persist
merge
2. 分离实体
让我们先简要回顾一下状态是什么以及它与实体生命周期的关系。detached
detached
的实体是不再被跟踪的 Java 对象。 如果我们关闭或清除会话,实体可以达到这种状态。 类似地,我们可以通过手动将实体从持久性上下文中移除来分离实体。 persistence context
我们将在本文中的代码示例中使用和实体。 要分离特定的实体,我们可以使用 。 此外,我们可以通过使用清除会话来从上下文中分离所有实体。Post
Comment
Post
session.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);
}
首先,我们持久化实体,然后使用将其分离。Post
session.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 抽象,我们应该避免它们。update
save
saveOrUpdate
persist
merge,
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
}
我们可以注意到实体与具有多对一的关系。Comment
Post
级联类型设置为 。 因此,我们只会将操作传播到关联的 。CascadeType.MERGE
merge
Post
换句话说,如果我们一个实体,Hibernate 会将操作传播到关联的并且两个实体都将在数据库中更新。 但是,如果我们想使用此设置 ,我们必须首先关联的 :merge
Comment
Post
persist
Comment
merge
Post
@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");
}
另一方面,如果级联类型设置为PERSIST
或ALL
,Hibernate将尝试在分离的关联字段上传播持久操作。 因此,当我们使用这些级联类型之一化实体时,Hibernate 将化关联的分离的, 这将导致另一个 。persist
Post
persist
Comment
PersistentObjectException
5.结论
在本文中,我们讨论了 Hbernate 的并了解了其主要原因。PersistentObjectException
我们可以通过正确使用Hibernate的、、和方法来避免它。save
persist
update
merge
saveOrUpdate
此外,对JPA级联类型的良好利用将防止在我们的实体关联中发生。PersistentObjectException
0 评论