From ff3cc7b948e831d0757141adc62f4594dd3a3527 Mon Sep 17 00:00:00 2001 From: etrandafir93 <75391049+etrandafir93@users.noreply.github.com> Date: Sun, 26 Jun 2022 18:33:25 +0200 Subject: [PATCH] BAEL-5439: PersistentObjectException: detached entity passed to persist thrown by JPA and Hibernate (#12303) * BAEL-5439: added code samples for article * BAEL-5439: added static import for assertj --- .../detachedentity/HibernateUtil.java | 46 ++++++++ .../detachedentity/entity/Comment.java | 56 +++++++++ .../exception/detachedentity/entity/Post.java | 44 ++++++++ .../DetachedEntityUnitTest.java | 106 ++++++++++++++++++ 4 files changed, 252 insertions(+) create mode 100644 persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/HibernateUtil.java create mode 100644 persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/entity/Comment.java create mode 100644 persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/entity/Post.java create mode 100644 persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/detachedentity/DetachedEntityUnitTest.java diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/HibernateUtil.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/HibernateUtil.java new file mode 100644 index 0000000000..0420755354 --- /dev/null +++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/HibernateUtil.java @@ -0,0 +1,46 @@ +package com.baeldung.hibernate.exception.detachedentity; + +import java.util.Properties; + +import org.hibernate.SessionFactory; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.service.ServiceRegistry; + +import com.baeldung.hibernate.exception.detachedentity.entity.Comment; +import com.baeldung.hibernate.exception.detachedentity.entity.Post; + +public class HibernateUtil { + private static SessionFactory sessionFactory; + + public static SessionFactory getSessionFactory() { + if (sessionFactory == null) { + try { + Configuration configuration = new Configuration(); + Properties settings = new Properties(); + settings.put(Environment.DRIVER, "org.hsqldb.jdbcDriver"); + settings.put(Environment.URL, "jdbc:hsqldb:mem:transient"); + settings.put(Environment.USER, "sa"); + settings.put(Environment.PASS, ""); + settings.put(Environment.DIALECT, "org.hibernate.dialect.HSQLDialect"); + settings.put(Environment.SHOW_SQL, "true"); + settings.put(Environment.FORMAT_SQL, "true"); + settings.put(Environment.USE_SQL_COMMENTS, "true"); + settings.put(Environment.HBM2DDL_AUTO, "update"); + configuration.setProperties(settings); + + configuration.addAnnotatedClass(Comment.class); + configuration.addAnnotatedClass(Post.class); + + ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()) + .build(); + sessionFactory = configuration.buildSessionFactory(serviceRegistry); + + } catch (Exception e) { + e.printStackTrace(); + } + } + return sessionFactory; + } +} \ No newline at end of file diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/entity/Comment.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/entity/Comment.java new file mode 100644 index 0000000000..4a3e9739e2 --- /dev/null +++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/entity/Comment.java @@ -0,0 +1,56 @@ +package com.baeldung.hibernate.exception.detachedentity.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.ManyToOne; + +@Entity +public class Comment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String text; + + public Comment(String text) { + this.text = text; + } + + public Comment() { + } + + @ManyToOne + private Post post; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } + + public Post getPost() { + return post; + } + + public void setPost(Post post) { + this.post = post; + } + + @Override + public String toString() { + return "Comment{" + "id=" + id + ", name='" + text + '\'' + ", post=" + post + '}'; + } +} diff --git a/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/entity/Post.java b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/entity/Post.java new file mode 100644 index 0000000000..7d95b41948 --- /dev/null +++ b/persistence-modules/hibernate-exceptions/src/main/java/com/baeldung/hibernate/exception/detachedentity/entity/Post.java @@ -0,0 +1,44 @@ +package com.baeldung.hibernate.exception.detachedentity.entity; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; + +@Entity +public class Post { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + public Post() { + } + + public Post(String title) { + this.title = title; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + @Override + public String toString() { + return "Post{" + "id=" + id + ", text='" + title + '\'' + '}'; + } +} diff --git a/persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/detachedentity/DetachedEntityUnitTest.java b/persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/detachedentity/DetachedEntityUnitTest.java new file mode 100644 index 0000000000..afb0efae77 --- /dev/null +++ b/persistence-modules/hibernate-exceptions/src/test/java/com/baeldung/hibernate/exception/detachedentity/DetachedEntityUnitTest.java @@ -0,0 +1,106 @@ +package com.baeldung.hibernate.exception.detachedentity; + +import com.baeldung.hibernate.exception.detachedentity.entity.Comment; +import com.baeldung.hibernate.exception.detachedentity.entity.Post; + +import org.assertj.core.api.Assertions; +import org.hibernate.Session; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.PersistenceException; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class DetachedEntityUnitTest { + + private static Session session; + private Post detachedPost; + + @Before + public void beforeEach() { + session = HibernateUtil.getSessionFactory() + .openSession(); + session.beginTransaction(); + this.detachedPost = new Post("Hibernate Tutorial"); + session.persist(detachedPost); + session.evict(detachedPost); + } + + @After + public void afterEach() { + clearDatabase(); + session.close(); + } + + @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"); + } + + @Test + public void givenDetachedPost_whenTryingToMerge_thenNoExceptionIsThrown() { + detachedPost.setTitle("Hibernate Tutorial for Beginners"); + + session.merge(detachedPost); + session.getTransaction() + .commit(); + + List 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"); + } + + @Test + public void givenDetachedPost_whenPersistingNewCommentWithIt_thenThrowException() { + Comment comment = new Comment("nice article!"); + comment.setPost(detachedPost); + + session.persist(comment); + session.getTransaction() + .commit(); + + assertThatThrownBy(() -> session.persist(detachedPost)) + .isInstanceOf(PersistenceException.class) + .hasMessageContaining("org.hibernate.PersistentObjectException: detached entity passed to persist"); + } + + @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 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"); + } + + private void clearDatabase() { + if (!session.getTransaction() + .isActive()) { + session.beginTransaction(); + } + session.createQuery("DELETE FROM Comment") + .executeUpdate(); + session.createQuery("DELETE FROM Post") + .executeUpdate(); + } +}