From 7541a2d9e2870f5fec144d771d136efcd8640c92 Mon Sep 17 00:00:00 2001 From: Vlad Mihalcea Date: Mon, 31 Oct 2016 13:54:30 +0200 Subject: [PATCH] HHH-2643 - Improve Session.refresh() Javdoc to clarify contract Instead of writing it in the JavaDocs, it's much more appropriate to document this behavior in the User Guide (cherry picked from commit 5629457aa8d563b0739a24ef2bea156000ae9cae) Conflicts: documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc documentation/src/test/java/org/hibernate/userguide/pc/PersistenceContextTest.java --- .../chapters/pc/PersistenceContext.adoc | 25 +++++++++++++- .../userguide/pc/PersistenceContextTest.java | 33 ++++++++++++++++++- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc b/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc index caab6b5ce7..bcbdff948e 100644 --- a/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc +++ b/documentation/src/main/asciidoc/userguide/chapters/pc/PersistenceContext.adoc @@ -262,6 +262,29 @@ However, please note that Hibernate has the capability to handle this automatica See the discussion of non-identifier <>. ==== +[[pc-refresh-gotchas]] +==== Refresh gotchas + +The `refresh` entity state transition is meant to overwrite the entity attributes according to the info currently contained in the associated database record. + +However, you have to be very careful when cascading the refresh action to any transient entity. + +For instance, consider the following example: + +[[pc-refresh-child-entity-jpa-example]] +.Refreshing entity state gotcha +==== +[source, JAVA, indent=0] +---- +include::{sourcedir}/PersistenceContextTest.java[tags=pc-refresh-child-entity-jpa-example] +---- +==== + +In the aforementioned example, an `EntityNotFoundException` is thrown because the `Book` entity is still in a transient state. +When the refresh action is cascaded from the `Person` entity, Hibernate will not be able to locate the `Book` entity in the database. + +For this reason, you should be very careful when mixing the refresh action with transient child entity objects. + [[pc-detach]] === Working with detached data @@ -442,4 +465,4 @@ To verify if an entity instance is currently attached to the running persistence ---- include::{sourcedir-caching}/FirstLevelCacheTest.java[tags=caching-management-contains-example] ---- -==== \ No newline at end of file +==== diff --git a/documentation/src/test/java/org/hibernate/userguide/pc/PersistenceContextTest.java b/documentation/src/test/java/org/hibernate/userguide/pc/PersistenceContextTest.java index d5a112bdd6..71ed74b7d8 100644 --- a/documentation/src/test/java/org/hibernate/userguide/pc/PersistenceContextTest.java +++ b/documentation/src/test/java/org/hibernate/userguide/pc/PersistenceContextTest.java @@ -9,7 +9,9 @@ package org.hibernate.userguide.pc; import java.sql.Statement; import java.util.ArrayList; import java.util.List; +import javax.persistence.CascadeType; import javax.persistence.Entity; +import javax.persistence.EntityNotFoundException; import javax.persistence.Id; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; @@ -28,6 +30,8 @@ import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.junit.Test; import static org.hibernate.userguide.util.TransactionUtil.doInJPA; +import org.jboss.logging.Logger; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -37,6 +41,8 @@ import static org.junit.Assert.assertTrue; */ public class PersistenceContextTest extends BaseEntityManagerFunctionalTestCase { + private static final Logger log = Logger.getLogger( PersistenceContextTest.class ); + @Override protected Class[] getAnnotatedClasses() { return new Class[] { @@ -172,6 +178,31 @@ public class PersistenceContextTest extends BaseEntityManagerFunctionalTestCase //end::pc-refresh-jpa-example[] } ); + try { + doInJPA( this::entityManagerFactory, entityManager -> { + Long personId = _personId; + + //tag::pc-refresh-child-entity-jpa-example[] + try { + Person person = entityManager.find( Person.class, personId ); + + Book book = new Book(); + book.setId( 100L ); + book.setTitle( "Hibernate User Guide" ); + book.setAuthor( person ); + person.getBooks().add( book ); + + entityManager.refresh( person ); + } + catch ( EntityNotFoundException expected ) { + log.info( "Beware when cascading the refresh associations to transient entities!" ); + } + //end::pc-refresh-child-entity-jpa-example[] + } ); + } + catch ( Exception expected ) { + } + doInJPA( this::entityManagerFactory, entityManager -> { Session session = entityManager.unwrap( Session.class ); Long personId = _personId; @@ -335,7 +366,7 @@ public class PersistenceContextTest extends BaseEntityManagerFunctionalTestCase private String name; - @OneToMany(mappedBy = "author") + @OneToMany(mappedBy = "author", cascade = CascadeType.ALL) private List books = new ArrayList<>( ); public Long getId() {