From f7792551f8460735633bd17d61b188c9d5ac1e2f Mon Sep 17 00:00:00 2001 From: Hardik Singh Behl Date: Mon, 1 Sep 2025 12:26:25 +0530 Subject: [PATCH 1/8] add domain entities --- .../baeldung/statelesssession/Article.java | 47 +++++++++++++++++++ .../com/baeldung/statelesssession/Author.java | 47 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Article.java create mode 100644 persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Author.java diff --git a/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Article.java b/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Article.java new file mode 100644 index 000000000000..51b34b8701fd --- /dev/null +++ b/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Article.java @@ -0,0 +1,47 @@ +package com.baeldung.statelesssession; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@Entity +@Table(name = "articles") +class Article { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String title; + + @ManyToOne + @JoinColumn(name = "author_id", nullable = false) + private Author author; + + public Long getId() { + return id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public Author getAuthor() { + return author; + } + + public void setAuthor(Author author) { + this.author = author; + } + +} \ No newline at end of file diff --git a/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Author.java b/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Author.java new file mode 100644 index 000000000000..40ebdf500b78 --- /dev/null +++ b/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Author.java @@ -0,0 +1,47 @@ +package com.baeldung.statelesssession; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; + +import java.util.List; + +@Entity +@Table(name = "authors") +class Author { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String name; + + @OneToMany(mappedBy = "author") + private List
articles; + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public List
getArticles() { + return articles; + } + + public void setArticles(List
articles) { + this.articles = articles; + } + +} \ No newline at end of file From 7d48dd49bda816f37a4840e5bcbcba0e6c145de5 Mon Sep 17 00:00:00 2001 From: Hardik Singh Behl Date: Mon, 1 Sep 2025 12:27:07 +0530 Subject: [PATCH 2/8] add main application entry --- .../com/baeldung/statelesssession/Application.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Application.java diff --git a/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Application.java b/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Application.java new file mode 100644 index 000000000000..8554f3a8883b --- /dev/null +++ b/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Application.java @@ -0,0 +1,13 @@ +package com.baeldung.statelesssession; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + +} \ No newline at end of file From b4c2e456ee482a0df421897c664f8e29d81aefd5 Mon Sep 17 00:00:00 2001 From: Hardik Singh Behl Date: Mon, 1 Sep 2025 12:30:55 +0530 Subject: [PATCH 3/8] add test cases --- .../StatelessSessionIntegrationTest.java | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java diff --git a/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java b/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java new file mode 100644 index 000000000000..aed507c80e68 --- /dev/null +++ b/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java @@ -0,0 +1,161 @@ +package com.baeldung.statelesssession; + +import jakarta.persistence.EntityManagerFactory; +import net.bytebuddy.utility.RandomString; +import org.hibernate.LazyInitializationException; +import org.hibernate.SessionFactory; +import org.hibernate.StatelessSession; +import org.hibernate.TransientObjectException; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@SpringBootTest(classes = Application.class) +class StatelessSessionIntegrationTest { + + @Autowired + private EntityManagerFactory entityManagerFactory; + + @Test + void whenInsertingEntityWithUnsavedReference_thenTransientObjectExceptionThrown() { + SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + Author author = new Author(); + author.setName(RandomString.make()); + + Article article = new Article(); + article.setTitle(RandomString.make()); + article.setAuthor(author); + + Exception exception = assertThrows(TransientObjectException.class, () -> { + statelessSession.insert(article); + }); + assertThat(exception.getMessage()) + .contains("object references an unsaved transient instance - save the transient instance before flushing"); + } + } + + @Test + void whenInsertingEntitiesInsideTransaction_thenEntitiesSavedSuccessfully() { + SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + statelessSession.getTransaction().begin(); + + Author author = new Author(); + author.setName(RandomString.make()); + + statelessSession.insert(author); + + Article article = new Article(); + article.setTitle(RandomString.make()); + article.setAuthor(author); + + statelessSession.insert(article); + + statelessSession.getTransaction().commit(); + + assertThat(author.getId()) + .isNotNull(); + assertThat(article.getId()) + .isNotNull(); + assertThat(article.getAuthor()) + .isEqualTo(author); + } + } + + @Test + void whenCollectionAccessedLazily_thenLazyInitializationExceptionThrown() { + SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); + Long authorId; + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + statelessSession.getTransaction().begin(); + + Author author = new Author(); + author.setName(RandomString.make()); + statelessSession.insert(author); + + Article article = new Article(); + article.setTitle(RandomString.make()); + article.setAuthor(author); + statelessSession.insert(article); + + statelessSession.getTransaction().commit(); + authorId = author.getId(); + } + + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + Author author = statelessSession.get(Author.class, authorId); + assertThat(author) + .hasNoNullFieldsOrProperties(); + + assertThrows(LazyInitializationException.class, () -> { + author.getArticles().size(); + }); + } + } + + @Test + void whenRelatedEntityFetchedUsingJoin_thenCollectionLoadedEagerly() { + SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); + Long authorId; + + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + statelessSession.getTransaction().begin(); + + Author author = new Author(); + author.setName(RandomString.make()); + statelessSession.insert(author); + + Article article = new Article(); + article.setTitle(RandomString.make()); + article.setAuthor(author); + statelessSession.insert(article); + + statelessSession.getTransaction().commit(); + authorId = author.getId(); + } + + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + Author author = statelessSession.createQuery( + "SELECT a FROM Author a LEFT JOIN FETCH a.articles WHERE a.id = :id", + Author.class) + .setParameter("id", authorId) + .getSingleResult(); + + assertThat(author) + .hasNoNullFieldsOrProperties(); + + assertThat(author.getArticles()) + .isNotNull() + .hasSize(1); + } + } + + @Test + void whenSameEntityRetrievedMultipleTimes_thenDifferentInstancesReturned() { + SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); + Long authorId; + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + statelessSession.getTransaction().begin(); + + Author author = new Author(); + author.setName(RandomString.make()); + statelessSession.insert(author); + + statelessSession.getTransaction().commit(); + authorId = author.getId(); + } + + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + Author author1 = statelessSession.get(Author.class, authorId); + Author author2 = statelessSession.get(Author.class, authorId); + + assertThat(author1) + .isNotSameAs(author2); + } + } + +} From f52185e482928d2fa16050ba1ed1af7b571dfa5d Mon Sep 17 00:00:00 2001 From: Hardik Singh Behl Date: Mon, 1 Sep 2025 13:51:18 +0530 Subject: [PATCH 4/8] explicitly enable cascade and lazy loading --- .../src/main/java/com/baeldung/statelesssession/Article.java | 3 ++- .../src/main/java/com/baeldung/statelesssession/Author.java | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Article.java b/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Article.java index 51b34b8701fd..9ac18cfe4e3a 100644 --- a/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Article.java +++ b/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Article.java @@ -2,6 +2,7 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -20,7 +21,7 @@ class Article { @Column(nullable = false) private String title; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "author_id", nullable = false) private Author author; diff --git a/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Author.java b/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Author.java index 40ebdf500b78..3681be3d834a 100644 --- a/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Author.java +++ b/persistence-modules/hibernate6/src/main/java/com/baeldung/statelesssession/Author.java @@ -1,7 +1,9 @@ package com.baeldung.statelesssession; +import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -21,7 +23,7 @@ class Author { @Column(nullable = false) private String name; - @OneToMany(mappedBy = "author") + @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY) private List
articles; public Long getId() { From 425bea90b388e95f814b645a9e72ecf4065a8cf5 Mon Sep 17 00:00:00 2001 From: Hardik Singh Behl Date: Mon, 1 Sep 2025 14:04:24 +0530 Subject: [PATCH 5/8] improve indentation --- .../StatelessSessionIntegrationTest.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java b/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java index aed507c80e68..78e95c1989b5 100644 --- a/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java +++ b/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java @@ -46,13 +46,11 @@ void whenInsertingEntitiesInsideTransaction_thenEntitiesSavedSuccessfully() { Author author = new Author(); author.setName(RandomString.make()); - statelessSession.insert(author); Article article = new Article(); article.setTitle(RandomString.make()); article.setAuthor(author); - statelessSession.insert(article); statelessSession.getTransaction().commit(); @@ -90,7 +88,6 @@ void whenCollectionAccessedLazily_thenLazyInitializationExceptionThrown() { Author author = statelessSession.get(Author.class, authorId); assertThat(author) .hasNoNullFieldsOrProperties(); - assertThrows(LazyInitializationException.class, () -> { author.getArticles().size(); }); @@ -119,7 +116,8 @@ void whenRelatedEntityFetchedUsingJoin_thenCollectionLoadedEagerly() { } try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { - Author author = statelessSession.createQuery( + Author author = statelessSession + .createQuery( "SELECT a FROM Author a LEFT JOIN FETCH a.articles WHERE a.id = :id", Author.class) .setParameter("id", authorId) @@ -127,7 +125,6 @@ void whenRelatedEntityFetchedUsingJoin_thenCollectionLoadedEagerly() { assertThat(author) .hasNoNullFieldsOrProperties(); - assertThat(author.getArticles()) .isNotNull() .hasSize(1); @@ -158,4 +155,4 @@ void whenSameEntityRetrievedMultipleTimes_thenDifferentInstancesReturned() { } } -} +} \ No newline at end of file From 4215dfdadc67cf0df39f3eedba11d0d4322d9cf1 Mon Sep 17 00:00:00 2001 From: Hardik Singh Behl Date: Mon, 1 Sep 2025 14:21:45 +0530 Subject: [PATCH 6/8] add test cases for dirty checking --- .../StatelessSessionIntegrationTest.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java b/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java index 78e95c1989b5..f6746d857d66 100644 --- a/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java +++ b/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java @@ -155,4 +155,55 @@ void whenSameEntityRetrievedMultipleTimes_thenDifferentInstancesReturned() { } } + @Test + void whenEntityModifiedInStatelessSession_thenChangesNotAutomaticallyPersisted() { + SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); + Long authorId; + String originalName = RandomString.make(); + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + statelessSession.getTransaction().begin(); + + Author author = new Author(); + author.setName(originalName); + statelessSession.insert(author); + + author.setName(RandomString.make()); + + statelessSession.getTransaction().commit(); + authorId = author.getId(); + } + + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + Author author = statelessSession.get(Author.class, authorId); + assertThat(author.getName()) + .isEqualTo(originalName); + } + } + + @Test + void whenEntityExplicitlyUpdated_thenChangesPersisted() { + SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); + Long authorId; + String newName = RandomString.make(); + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + statelessSession.getTransaction().begin(); + + Author author = new Author(); + author.setName(RandomString.make()); + statelessSession.insert(author); + + author.setName(newName); + statelessSession.update(author); + + statelessSession.getTransaction().commit(); + authorId = author.getId(); + } + + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + Author author = statelessSession.get(Author.class, authorId); + assertThat(author.getName()) + .isEqualTo(newName); + } + } + } \ No newline at end of file From 842a828e26c48a43c9c045086342d7a543fe14b2 Mon Sep 17 00:00:00 2001 From: Hardik Singh Behl Date: Thu, 4 Sep 2025 13:45:16 +0530 Subject: [PATCH 7/8] add test case for loading association eagerly using EntityGraph --- .../StatelessSessionIntegrationTest.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java b/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java index f6746d857d66..c3c790f20322 100644 --- a/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java +++ b/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java @@ -1,5 +1,6 @@ package com.baeldung.statelesssession; +import jakarta.persistence.EntityGraph; import jakarta.persistence.EntityManagerFactory; import net.bytebuddy.utility.RandomString; import org.hibernate.LazyInitializationException; @@ -131,6 +132,45 @@ void whenRelatedEntityFetchedUsingJoin_thenCollectionLoadedEagerly() { } } + @Test + void whenRelatedEntityFetchedUsingEntityGraph_thenCollectionLoadedEagerly() { + SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); + Long authorId; + + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + statelessSession.getTransaction().begin(); + + Author author = new Author(); + author.setName(RandomString.make()); + statelessSession.insert(author); + + Article article = new Article(); + article.setTitle(RandomString.make()); + article.setAuthor(author); + statelessSession.insert(article); + + statelessSession.getTransaction().commit(); + authorId = author.getId(); + } + + try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { + EntityGraph graph = statelessSession.createEntityGraph(Author.class); + graph.addAttributeNodes("articles"); + + Author author = statelessSession + .createQuery("SELECT a FROM Author a WHERE a.id = :id", Author.class) + .setParameter("id", authorId) + .setHint("jakarta.persistence.fetchgraph", graph) + .getSingleResult(); + + assertThat(author) + .hasNoNullFieldsOrProperties(); + assertThat(author.getArticles()) + .isNotNull() + .hasSize(1); + } + } + @Test void whenSameEntityRetrievedMultipleTimes_thenDifferentInstancesReturned() { SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class); From b33394e339ba5bc1a613725d39e866c1e62bbac0 Mon Sep 17 00:00:00 2001 From: Hardik Singh Behl Date: Thu, 4 Sep 2025 13:53:05 +0530 Subject: [PATCH 8/8] refactor: update variable name --- .../statelesssession/StatelessSessionIntegrationTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java b/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java index c3c790f20322..6b75f7c79c39 100644 --- a/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java +++ b/persistence-modules/hibernate6/src/test/java/com/baeldung/statelesssession/StatelessSessionIntegrationTest.java @@ -154,13 +154,13 @@ void whenRelatedEntityFetchedUsingEntityGraph_thenCollectionLoadedEagerly() { } try (StatelessSession statelessSession = sessionFactory.openStatelessSession()) { - EntityGraph graph = statelessSession.createEntityGraph(Author.class); - graph.addAttributeNodes("articles"); + EntityGraph authorWithArticlesGraph = statelessSession.createEntityGraph(Author.class); + authorWithArticlesGraph.addAttributeNodes("articles"); Author author = statelessSession .createQuery("SELECT a FROM Author a WHERE a.id = :id", Author.class) .setParameter("id", authorId) - .setHint("jakarta.persistence.fetchgraph", graph) + .setHint("jakarta.persistence.fetchgraph", authorWithArticlesGraph) .getSingleResult(); assertThat(author)