diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java index 092141d485..3bea815fc3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java @@ -425,6 +425,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces || entityDescriptor.getJavaTypeDescriptor().getJavaTypeClass().isInstance( proxy ) ) ) { if ( this instanceof EntityResultInitializer && entityInstanceFromExecutionContext != null ) { this.entityInstance = entityInstanceFromExecutionContext; + registerLoadingEntity( rowProcessingState, entityInstance ); } else { this.entityInstance = proxy; @@ -435,9 +436,11 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces if ( existingEntity != null ) { this.entityInstance = existingEntity; + registerLoadingEntity( rowProcessingState, entityInstance ); } else if ( this instanceof EntityResultInitializer && entityInstanceFromExecutionContext != null ) { this.entityInstance = entityInstanceFromExecutionContext; + registerLoadingEntity( rowProcessingState, entityInstance ); } else { // look to see if another initializer from a parent load context or an earlier @@ -578,6 +581,12 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces } } + registerLoadingEntity( rowProcessingState, instance ); + + return instance; + } + + private void registerLoadingEntity(RowProcessingState rowProcessingState, Object instance) { final LoadingEntityEntry loadingEntry = new LoadingEntityEntry( this, entityKey, @@ -588,8 +597,6 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces entityKey, loadingEntry ); - - return instance; } @Override @@ -625,6 +632,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces } notifyResolutionListeners( entityInstanceForNotify ); + isInitialized = true; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java index b8fdfa5dbc..0c527ade39 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/jdbc/internal/JdbcValuesSourceProcessingStateStandardImpl.java @@ -21,7 +21,6 @@ import org.hibernate.event.spi.EventSource; import org.hibernate.event.spi.PostLoadEvent; import org.hibernate.event.spi.PostLoadEventListener; import org.hibernate.event.spi.PreLoadEvent; -import org.hibernate.mapping.UniqueKey; import org.hibernate.persister.entity.Loadable; import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.results.graph.Initializer; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/collection/PostLoadTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/collection/PostLoadTest.java index fa8a851097..ec87c5b49d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/collection/PostLoadTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/collection/PostLoadTest.java @@ -28,32 +28,26 @@ public class PostLoadTest { */ @Test public void testAccessAssociatedSetInPostLoad(EntityManagerFactoryScope scope) { + + scope.inTransaction( + entityManager -> { + Child child = new Child(); + child.setId( 1 ); + Parent daddy = new Parent(); + daddy.setId( 1 ); + child.setDaddy( daddy ); + Set children = new HashSet<>(); + children.add( child ); + daddy.setChildren( children ); + + entityManager.persist( daddy ); + } + ); + scope.inEntityManager( entityManager -> { - try { - Child child = new Child(); - child.setId( 1 ); - Parent daddy = new Parent(); - daddy.setId( 1 ); - child.setDaddy( daddy ); - Set children = new HashSet<>(); - children.add( child ); - daddy.setChildren( children ); - - entityManager.getTransaction().begin(); - entityManager.persist( daddy ); - entityManager.getTransaction().commit(); - entityManager.clear(); - - daddy = entityManager.find( Parent.class, 1 ); - assertEquals( 1, daddy.getNrOfChildren() ); - } - catch (Exception e) { - if ( entityManager.getTransaction().isActive() ) { - entityManager.getTransaction().rollback(); - } - throw e; - } + Parent daddy = entityManager.find( Parent.class, 1 ); + assertEquals( 1, daddy.getNrOfChildren() ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/PostLoadCallbackTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/PostLoadCallbackTest.java new file mode 100644 index 0000000000..d27222e732 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/callback/PostLoadCallbackTest.java @@ -0,0 +1,213 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.jpa.compliance.callback; + +import java.math.BigDecimal; + +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.DiscriminatorType; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.PostLoad; +import jakarta.persistence.PostPersist; +import jakarta.persistence.PrePersist; +import jakarta.persistence.Query; + +import static org.junit.jupiter.api.Assertions.fail; + +@Jpa( + annotatedClasses = { + PostLoadCallbackTest.Account.class, + PostLoadCallbackTest.DebitAccount.class, + PostLoadCallbackTest.CreditAccount.class, + PostLoadCallbackTest.Person.class + } +) +public class PostLoadCallbackTest { + + @Test + public void testEntityListenerPostLoadMethodIsCalled(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + CreditAccount account = new CreditAccount( + 1, + "drea", + BigDecimal.valueOf( 190 ), + BigDecimal.valueOf( 10000 ) + ); + + entityManager.persist( account ); + entityManager.flush(); + entityManager.refresh( account ); + + Query query = entityManager.createQuery( "select ca from CreditAccount ca" ); + query.getResultList(); + + if ( !account.isPostLoadCalled() ) { + fail( "PostLoad has not been called" ); + } + } + ); + } + + @Test + public void testPostLoadMethodIsCalled(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + Person person = new Person( 1, "Fab" ); + entityManager.persist( person ); + entityManager.flush(); + entityManager.refresh( person ); + + Query query = entityManager + .createQuery( "select p from Person p" ); + query.getResultList(); + if ( person.isPostLoadCalled() ) { + fail( "PostLoad has not been called" ); + } + } + ); + } + + @MappedSuperclass + public static class PersonCallback { + + boolean isPrePersistCalled; + boolean isPostPersistCalled; + + @PrePersist + public void prePersist() { + isPrePersistCalled = true; + } + + @PostPersist + public void postPersist() { + if ( !isPrePersistCalled ) { + throw new IllegalStateException( + "The prePersist method has not been called." ); + } + this.isPostPersistCalled = true; + } + + public boolean isPostLoadCalled() { + return isPostPersistCalled; + } + } + + @Entity(name = "Person") + public static class Person extends PersonCallback { + + Person() { + } + + public Person(Integer id, String name) { + this.id = id; + this.name = name; + } + + @Id + private Integer id; + + private String name; + } + + @Entity(name = "Account") + @Inheritance(strategy = InheritanceType.SINGLE_TABLE) + @DiscriminatorColumn(name = "ACCOUNT_TYPE", discriminatorType = DiscriminatorType.STRING) + @DiscriminatorValue("Account") + @EntityListeners(AccountListener.class) + public static class Account extends CallbackStatus { + @Id + private Integer id; + + private String owner; + + private BigDecimal balance; + + public Account() { + } + + public Account(Integer id, String owner, BigDecimal balance) { + this.id = id; + this.owner = owner; + this.balance = balance; + } + + public Integer getId() { + return id; + } + + } + + @Entity(name = "DebitAccount") + @DiscriminatorValue("DebitAccount") + @EntityListeners(AccountListener.class) + public static class DebitAccount extends Account { + private BigDecimal overdraftFee; + + public DebitAccount(Integer id, String owner, BigDecimal balance, BigDecimal overdraftFee) { + super( id, owner, balance ); + this.overdraftFee = overdraftFee; + } + + } + + @Entity(name = "CreditAccount") + @DiscriminatorValue("CreditAccount") + @EntityListeners(AccountListener.class) + public static class CreditAccount extends Account { + private BigDecimal creditLimit; + + public CreditAccount(Integer id, String owner, BigDecimal balance, BigDecimal creditLimit) { + super( id, owner, balance ); + this.creditLimit = creditLimit; + } + } + + public static class AccountListener { + + @PostPersist + public void postPersist(CallbackStatus callbackStatus) { + callbackStatus.setPostPersistCall(); + } + + @PostLoad + public void postLoad(CallbackStatus callbackStatus) { + callbackStatus.setPostLoadCalled(); + } + + } + + public static class CallbackStatus { + + private boolean postPersistCalled; + + private boolean postLoadCalled; + + public boolean isPostLoadCalled() { + return postLoadCalled; + } + + public void setPostLoadCalled() { + postLoadCalled = true; + } + + public void setPostPersistCall() { + postPersistCalled = true; + } + } + +}