From 3fec3b930b78a702f34b5f0c6f5ad6ae6d694d1d Mon Sep 17 00:00:00 2001 From: Guillaume Smet Date: Mon, 15 Oct 2018 17:24:53 +0200 Subject: [PATCH] HHH-12425 Move afterInitialize() phase after the collection initialization --- .../engine/internal/TwoPhaseLoad.java | 20 +++- .../java/org/hibernate/loader/Loader.java | 6 ++ .../process/internal/AbstractRowReader.java | 14 +++ .../lazy/LazyCollectionHandlingTest.java | 102 ++++++++++++++++++ .../test/legacy/CustomPersister.java | 3 +- 5 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyCollectionHandlingTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/internal/TwoPhaseLoad.java b/hibernate-core/src/main/java/org/hibernate/engine/internal/TwoPhaseLoad.java index 3b426effe5..4f94a8456d 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/internal/TwoPhaseLoad.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/internal/TwoPhaseLoad.java @@ -284,8 +284,6 @@ public final class TwoPhaseLoad { persistenceContext.setEntryStatus( entityEntry, Status.MANAGED ); } - persister.afterInitialize( entity, session ); - if ( debugEnabled ) { LOG.debugf( "Done materializing entity %s", @@ -298,6 +296,22 @@ public final class TwoPhaseLoad { } } + /** + * Perform the afterInitialize() step. This needs to be done after the collections have been properly initialized + * thus a separate step. + * + * @param entity The entity being loaded + * @param session The Session + */ + public static void afterInitialize( + final Object entity, + final SharedSessionContractImplementor session) { + final PersistenceContext persistenceContext = session.getPersistenceContext(); + final EntityEntry entityEntry = persistenceContext.getEntry( entity ); + + entityEntry.getPersister().afterInitialize( entity, session ); + } + /** * Check if eager of the association is overriden by anything. * @@ -341,7 +355,7 @@ public final class TwoPhaseLoad { return true; } } - + return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java index a3ebcd2069..e0b4fb5a19 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/Loader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/Loader.java @@ -1164,6 +1164,12 @@ public abstract class Loader { } } + if ( hydratedObjects != null ) { + for ( Object hydratedObject : hydratedObjects ) { + TwoPhaseLoad.afterInitialize( hydratedObject, session ); + } + } + // Until this entire method is refactored w/ polymorphism, postLoad was // split off from initializeEntity. It *must* occur after // endCollectionLoad to ensure the collection is in the diff --git a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/AbstractRowReader.java b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/AbstractRowReader.java index b1823ec453..cc6e5386ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/AbstractRowReader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/plan/exec/process/internal/AbstractRowReader.java @@ -211,6 +211,9 @@ public abstract class AbstractRowReader implements RowReader { // now we can finalize loading collections finishLoadingCollections( context ); + // and trigger the afterInitialize() hooks + afterInitialize( context, hydratedEntityRegistrations ); + // finally, perform post-load operations postLoad( postLoadEvent, context, hydratedEntityRegistrations, afterLoadActionList ); } @@ -250,6 +253,17 @@ public abstract class AbstractRowReader implements RowReader { } } + private void afterInitialize(ResultSetProcessingContextImpl context, + List hydratedEntityRegistrations) { + if ( hydratedEntityRegistrations == null ) { + return; + } + + for ( HydratedEntityRegistration registration : hydratedEntityRegistrations ) { + TwoPhaseLoad.afterInitialize( registration.getInstance(), context.getSession() ); + } + } + private void postLoad( PostLoadEvent postLoadEvent, ResultSetProcessingContextImpl context, diff --git a/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyCollectionHandlingTest.java b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyCollectionHandlingTest.java new file mode 100644 index 0000000000..3ec6be12b5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/bytecode/enhancement/lazy/LazyCollectionHandlingTest.java @@ -0,0 +1,102 @@ +/* + * 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 . + */ +package org.hibernate.test.bytecode.enhancement.lazy; + +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; + +import java.util.LinkedHashSet; +import java.util.Set; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToMany; +import javax.persistence.MappedSuperclass; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; +import org.junit.runner.RunWith; + +@TestForIssue(jiraKey = "") +@RunWith(BytecodeEnhancerRunner.class) +public class LazyCollectionHandlingTest extends BaseCoreFunctionalTestCase { + + private Integer id; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{ + JafSid.class, UserGroup.class + }; + } + + @Test + public void test() { + doInHibernate( this::sessionFactory, s -> { + JafSid sid = new JafSid(); + s.save( sid ); + + s.flush(); + s.clear(); + + this.id = sid.getId(); + }); + + doInHibernate( this::sessionFactory, s -> { + s.get( JafSid.class, this.id ); + } ); + } + + @MappedSuperclass + public abstract static class DatabaseEntity { + private int id; + + @Id + @GeneratedValue + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + } + + @Entity(name = "JafSid") + public static class JafSid extends DatabaseEntity { + + private Set groups = new LinkedHashSet<>(); + + @ManyToMany(mappedBy = "members", fetch = FetchType.EAGER) + public Set getGroups() { + return groups; + } + + public void setGroups(Set groups) { + this.groups = groups; + } + } + + @Entity(name = "UserGroup") + public static class UserGroup extends DatabaseEntity { + + private Set members = new LinkedHashSet<>(); + + @ManyToMany + public Set getMembers() { + return members; + } + + public void setMembers(Set members) { + this.members = members; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index 68d676d84a..9615248f4f 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -356,6 +356,7 @@ public class CustomPersister implements EntityPersister { session, new PreLoadEvent( (EventSource) session ) ); + TwoPhaseLoad.afterInitialize( clone, session ); TwoPhaseLoad.postLoad( clone, session, new PostLoadEvent( (EventSource) session ) ); } return clone; @@ -481,7 +482,7 @@ public class CustomPersister implements EntityPersister { public EntityDataAccess getCacheAccessStrategy() { return null; } - + public boolean hasNaturalIdCache() { return false; }