From c0c628f16c8829822ccd19af9093649ee8f28ee3 Mon Sep 17 00:00:00 2001 From: Marco Belladelli Date: Wed, 20 Sep 2023 11:03:06 +0200 Subject: [PATCH] HHH-17188 Eagerly select values when query cache is enabled --- .../internal/CompoundNaturalIdMapping.java | 7 +++++++ .../results/graph/DomainResultAssembler.java | 8 ++++++++ .../graph/basic/BasicResultAssembler.java | 5 +++++ .../AbstractEmbeddableInitializer.java | 9 +++++++++ .../embeddable/EmbeddableInitializer.java | 2 ++ ...ggregatedIdentifierMappingInitializer.java | 20 ++++++++++++++++--- .../internal/EmbeddableAssembler.java | 6 ++++++ .../entity/AbstractEntityInitializer.java | 12 +++++++++++ 8 files changed, 66 insertions(+), 3 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CompoundNaturalIdMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CompoundNaturalIdMapping.java index 866b2f9a97..554c7ebdff 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CompoundNaturalIdMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CompoundNaturalIdMapping.java @@ -668,6 +668,13 @@ public class CompoundNaturalIdMapping extends AbstractNaturalIdMapping implement return result; } + @Override + public void resolveState(RowProcessingState rowProcessingState) { + for ( DomainResultAssembler subAssembler : subAssemblers ) { + subAssembler.resolveState( rowProcessingState ); + } + } + @Override public JavaType getAssembledJavaType() { return jtd; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultAssembler.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultAssembler.java index 7ea49962c7..c4b935e2d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultAssembler.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultAssembler.java @@ -38,4 +38,12 @@ public interface DomainResultAssembler { * assembles. */ JavaType getAssembledJavaType(); + + /** + * This method is used to resolve the assembler's state, i.e. reading the result values, + * with some performance optimization when we don't need the result object itself + */ + default void resolveState(RowProcessingState rowProcessingState) { + assemble( rowProcessingState ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicResultAssembler.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicResultAssembler.java index 9c836328bf..51ec44a169 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicResultAssembler.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicResultAssembler.java @@ -84,6 +84,11 @@ public class BasicResultAssembler implements DomainResultAssembler { return (J) jdbcValue; } + @Override + public void resolveState(RowProcessingState rowProcessingState) { + extractRawValue( rowProcessingState ); + } + @Override public JavaType getAssembledJavaType() { if ( valueConverter != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java index d5f0f67e07..625e3727b7 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java @@ -323,6 +323,15 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA state = stateAllNull ? State.NULL : State.EXTRACTED; } + @Override + public void resolveState(RowProcessingState rowProcessingState) { + if ( determinInitialState() == State.INITIAL ) { + for ( final DomainResultAssembler assembler : assemblers ) { + assembler.resolveState( rowProcessingState ); + } + } + } + private Object createCompositeInstance(NavigablePath navigablePath, SessionFactoryImplementor sessionFactory) { if ( state == State.NULL ) { // todo (6.0) : should we initialize the composite instance if it has a parent attribute? diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/EmbeddableInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/EmbeddableInitializer.java index 37af35e7b9..9b533e3ae1 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/EmbeddableInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/EmbeddableInitializer.java @@ -48,4 +48,6 @@ public interface EmbeddableInitializer extends FetchParentAccess { default EmbeddableInitializer asEmbeddableInitializer() { return this; } + + void resolveState(RowProcessingState rowProcessingState); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/AbstractNonAggregatedIdentifierMappingInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/AbstractNonAggregatedIdentifierMappingInitializer.java index 2d5ceddbc2..f7c77b606c 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/AbstractNonAggregatedIdentifierMappingInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/AbstractNonAggregatedIdentifierMappingInitializer.java @@ -28,6 +28,7 @@ import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Initializer; +import org.hibernate.sql.results.graph.embeddable.AbstractEmbeddableInitializer; import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer; import org.hibernate.sql.results.graph.embeddable.EmbeddableLoadingLogger; import org.hibernate.sql.results.graph.embeddable.EmbeddableResultGraphNode; @@ -158,9 +159,7 @@ public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends return; case INITIAL: // If we don't have an id class and this is a find by id lookup, we just use that instance - if ( !hasIdClass && processingState.getEntityId() != null - && navigablePath.getParent().getParent() == null - && navigablePath instanceof EntityIdentifierNavigablePath ) { + if ( isFindByIdLookup( processingState ) ) { compositeInstance = processingState.getEntityId(); state = State.INJECTED; return; @@ -216,6 +215,12 @@ public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends } } + private boolean isFindByIdLookup(RowProcessingState processingState) { + return !hasIdClass && processingState.getEntityId() != null + && navigablePath.getParent().getParent() == null + && navigablePath instanceof EntityIdentifierNavigablePath; + } + private void extractRowState(RowProcessingState processingState) { state = State.NULL; for ( int i = 0; i < assemblers.length; i++ ) { @@ -256,6 +261,15 @@ public abstract class AbstractNonAggregatedIdentifierMappingInitializer extends state = State.EXTRACTED; } + @Override + public void resolveState(RowProcessingState rowProcessingState) { + if ( !isFindByIdLookup( rowProcessingState ) ) { + for ( final DomainResultAssembler assembler : assemblers ) { + assembler.resolveState( rowProcessingState ); + } + } + } + @Override public Object[] getValues() { return state == State.NULL ? null : idClassState; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableAssembler.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableAssembler.java index c26a8e2e34..b10ae1f2ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableAssembler.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableAssembler.java @@ -35,4 +35,10 @@ public class EmbeddableAssembler implements DomainResultAssembler { return initializer.getCompositeInstance(); } + @Override + public void resolveState(RowProcessingState rowProcessingState) { + // use resolveState instead of initialize instance to avoid + // unneeded embeddable instantiation and injection + initializer.resolveState( rowProcessingState ); + } } 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 4bea7442dd..0a984640c3 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 @@ -517,6 +517,10 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces this.isInitialized = true; registerReloadedEntity( rowProcessingState, existingEntity ); notifyResolutionListeners( entityInstance ); + if ( rowProcessingState.getQueryOptions().isResultCachingEnabled() == Boolean.TRUE ) { + // We still need to read result set values to correctly populate the query cache + resolveState( rowProcessingState ); + } } else { registerLoadingEntityInstanceFromExecutionContext( rowProcessingState, entityInstance ); @@ -1062,6 +1066,14 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces return values; } + private void resolveState(RowProcessingState rowProcessingState) { + for ( final DomainResultAssembler assembler : assemblers[concreteDescriptor.getSubclassId()] ) { + if ( assembler != null ) { + assembler.resolveState( rowProcessingState ); + } + } + } + protected boolean skipInitialization(Object toInitialize, RowProcessingState rowProcessingState) { if ( !isOwningInitializer ) { return true;