From f497638131463f8e45dad3d91cdfe995348b9888 Mon Sep 17 00:00:00 2001 From: Dan Kristensen Date: Thu, 25 Sep 2025 15:22:55 +0200 Subject: [PATCH 01/12] Added proper support for DB2zDialect sequences. It is important to take sequence alias' into consideration when reading these --- .../src/main/java/org/hibernate/dialect/DB2zDialect.java | 6 +++++- .../unit/sequence/DB2zSequenceInformationExtractorTest.java | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index 29135c068c74..566a05cb5c6a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -113,7 +113,11 @@ public SequenceSupport getSequenceSupport() { @Override public String getQuerySequencesString() { - return "select * from sysibm.syssequences"; + return """ + select '' as sequence_catalog, seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' + union + select '' as sequence_catalog, schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' + """; } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java index c9d196883d59..57fc9817f1d0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java @@ -26,7 +26,11 @@ public Dialect getDialect() { @Override public String expectedQuerySequencesString() { - return "select * from sysibm.syssequences"; + return """ + select '' as sequence_catalog, seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' + union + select '' as sequence_catalog, schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' + """; } @Override From baca5adf674b5b5eb6d0da52855be04a9d33ab48 Mon Sep 17 00:00:00 2001 From: Dan Kristensen Date: Sun, 28 Sep 2025 10:28:20 +0200 Subject: [PATCH 02/12] Do not include catalog. It is not supported --- .../src/main/java/org/hibernate/dialect/DB2zDialect.java | 4 ++-- .../unit/sequence/DB2zSequenceInformationExtractorTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index 566a05cb5c6a..7705917ffad0 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -114,9 +114,9 @@ public SequenceSupport getSequenceSupport() { @Override public String getQuerySequencesString() { return """ - select '' as sequence_catalog, seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' + select seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' union - select '' as sequence_catalog, schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' + select schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' """; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java index 57fc9817f1d0..9018293d88d8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java @@ -27,9 +27,9 @@ public Dialect getDialect() { @Override public String expectedQuerySequencesString() { return """ - select '' as sequence_catalog, seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' + select seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' union - select '' as sequence_catalog, schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' + select schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' """; } From abc93504d1cebeecaef6cdaf2faa01aaa134eb26 Mon Sep 17 00:00:00 2001 From: Dan Kristensen Date: Sun, 28 Sep 2025 10:34:28 +0200 Subject: [PATCH 03/12] Simplified query --- .../src/main/java/org/hibernate/dialect/DB2zDialect.java | 6 +----- .../unit/sequence/DB2zSequenceInformationExtractorTest.java | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index 7705917ffad0..f4f6a9fffb4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -113,11 +113,7 @@ public SequenceSupport getSequenceSupport() { @Override public String getQuerySequencesString() { - return """ - select seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' - union - select schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' - """; + return "select case when seqtype='A' then seqschema else schema end as seqschema, case when seqtype='A' then seqname else name end as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences"; } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java index 9018293d88d8..c411bbc21eb6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java @@ -26,11 +26,7 @@ public Dialect getDialect() { @Override public String expectedQuerySequencesString() { - return """ - select seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' - union - select schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' - """; + return "select case when seqtype='A' then seqschema else schema end as seqschema, case when seqtype='A' then seqname else name end as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences"; } @Override From f4f67c9788e6b816317b7176121cabb13ae4cd90 Mon Sep 17 00:00:00 2001 From: Dan Kristensen Date: Thu, 25 Sep 2025 15:22:55 +0200 Subject: [PATCH 04/12] Added proper support for DB2zDialect sequences. It is important to take sequence alias' into consideration when reading these --- .../src/main/java/org/hibernate/dialect/DB2zDialect.java | 6 +++++- .../unit/sequence/DB2zSequenceInformationExtractorTest.java | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index 29135c068c74..566a05cb5c6a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -113,7 +113,11 @@ public SequenceSupport getSequenceSupport() { @Override public String getQuerySequencesString() { - return "select * from sysibm.syssequences"; + return """ + select '' as sequence_catalog, seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' + union + select '' as sequence_catalog, schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' + """; } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java index c9d196883d59..57fc9817f1d0 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java @@ -26,7 +26,11 @@ public Dialect getDialect() { @Override public String expectedQuerySequencesString() { - return "select * from sysibm.syssequences"; + return """ + select '' as sequence_catalog, seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' + union + select '' as sequence_catalog, schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' + """; } @Override From 23e6533910924200ee96fa1c16be898794d865a4 Mon Sep 17 00:00:00 2001 From: Dan Kristensen Date: Sun, 28 Sep 2025 10:28:20 +0200 Subject: [PATCH 05/12] Do not include catalog. It is not supported --- .../src/main/java/org/hibernate/dialect/DB2zDialect.java | 4 ++-- .../unit/sequence/DB2zSequenceInformationExtractorTest.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index 566a05cb5c6a..7705917ffad0 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -114,9 +114,9 @@ public SequenceSupport getSequenceSupport() { @Override public String getQuerySequencesString() { return """ - select '' as sequence_catalog, seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' + select seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' union - select '' as sequence_catalog, schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' + select schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' """; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java index 57fc9817f1d0..9018293d88d8 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java @@ -27,9 +27,9 @@ public Dialect getDialect() { @Override public String expectedQuerySequencesString() { return """ - select '' as sequence_catalog, seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' + select seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' union - select '' as sequence_catalog, schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' + select schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' """; } From bb9cfb12d7fc946243664623e684fdae44062755 Mon Sep 17 00:00:00 2001 From: Dan Kristensen Date: Sun, 28 Sep 2025 10:34:28 +0200 Subject: [PATCH 06/12] Simplified query --- .../src/main/java/org/hibernate/dialect/DB2zDialect.java | 6 +----- .../unit/sequence/DB2zSequenceInformationExtractorTest.java | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java index 7705917ffad0..f4f6a9fffb4a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2zDialect.java @@ -113,11 +113,7 @@ public SequenceSupport getSequenceSupport() { @Override public String getQuerySequencesString() { - return """ - select seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' - union - select schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' - """; + return "select case when seqtype='A' then seqschema else schema end as seqschema, case when seqtype='A' then seqname else name end as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences"; } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java index 9018293d88d8..c411bbc21eb6 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/sequence/DB2zSequenceInformationExtractorTest.java @@ -26,11 +26,7 @@ public Dialect getDialect() { @Override public String expectedQuerySequencesString() { - return """ - select seqschema, seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype='A' - union - select schema as seqschema, name as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences where seqtype!='A' - """; + return "select case when seqtype='A' then seqschema else schema end as seqschema, case when seqtype='A' then seqname else name end as seqname, start, minvalue, maxvalue, increment from sysibm.syssequences"; } @Override From 6e13b771e5614832a917672b497d37e650ccf40f Mon Sep 17 00:00:00 2001 From: Gavin King Date: Mon, 29 Sep 2025 12:48:13 +0200 Subject: [PATCH 07/12] superficial code cleanups in initializers mostly just use of 'var' --- .../spi/InitializerProducerBuilder.java | 121 +- .../internal/PluralAttributeMetadataImpl.java | 21 +- .../persister/entity/EntityPersister.java | 3 +- .../results/graph/DomainResultAssembler.java | 10 + .../AbstractCollectionInitializer.java | 21 +- ...bstractImmediateCollectionInitializer.java | 413 ++++--- .../AbstractNonJoinCollectionInitializer.java | 161 ++- .../collection/internal/ArrayInitializer.java | 42 +- .../collection/internal/BagInitializer.java | 54 +- .../DelayedCollectionInitializer.java | 5 +- .../collection/internal/ListInitializer.java | 51 +- .../collection/internal/MapInitializer.java | 48 +- .../SelectEagerCollectionInitializer.java | 21 +- .../collection/internal/SetInitializer.java | 24 +- .../AggregateEmbeddableInitializerImpl.java | 2 +- .../internal/EmbeddableInitializerImpl.java | 205 ++-- ...ggregatedIdentifierMappingInitializer.java | 65 +- ...ractBatchEntitySelectFetchInitializer.java | 208 ++-- ...nsideEmbeddableSelectFetchInitializer.java | 96 +- .../BatchEntitySelectFetchInitializer.java | 70 +- ...nitializeEntitySelectFetchInitializer.java | 32 +- .../DiscriminatedEntityInitializer.java | 240 ++-- .../EntityDelayedFetchInitializer.java | 294 ++--- .../internal/EntityInitializerImpl.java | 1030 +++++++++-------- ...titySelectFetchByUniqueKeyInitializer.java | 62 +- .../EntitySelectFetchInitializer.java | 128 +- .../graph/internal/AbstractInitializer.java | 3 +- 27 files changed, 1706 insertions(+), 1724 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/collection/spi/InitializerProducerBuilder.java b/hibernate-core/src/main/java/org/hibernate/collection/spi/InitializerProducerBuilder.java index bdd2d14b6b70..cf7c7c887cce 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/spi/InitializerProducerBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/spi/InitializerProducerBuilder.java @@ -33,25 +33,18 @@ public static CollectionInitializerProducer createInitializerProducer( Fetch indexFetch, Fetch elementFetch, DomainResultCreationState creationState) { - switch ( classification ) { - case ARRAY: - return createArrayInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, indexFetch, elementFetch, creationState ); - case BAG: - case ID_BAG: - return createBagInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, elementFetch, creationState ); - case LIST: - return createListInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, indexFetch, elementFetch, creationState ); - case MAP: - case ORDERED_MAP: - case SORTED_MAP: - return createMapInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, indexFetch, elementFetch, creationState ); - case SET: - case ORDERED_SET: - case SORTED_SET: - return createSetInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, elementFetch, creationState ); - default: - throw new IllegalArgumentException( "Unknown CollectionClassification : " + classification ); - } + return switch ( classification ) { + case ARRAY -> + createArrayInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, indexFetch, elementFetch, creationState ); + case BAG, ID_BAG -> + createBagInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, elementFetch, creationState ); + case LIST -> + createListInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, indexFetch, elementFetch, creationState ); + case MAP, ORDERED_MAP, SORTED_MAP -> + createMapInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, indexFetch, elementFetch, creationState ); + case SET, ORDERED_SET, SORTED_SET -> + createSetInitializerProducer( navigablePath, attributeMapping, fetchParent, selected, elementFetch, creationState ); + }; } public static CollectionInitializerProducer createArrayInitializerProducer( @@ -219,54 +212,19 @@ public static CollectionInitializerProducer createCollectionTypeWrapperInitializ Fetch indexFetch, Fetch elementFetch, DomainResultCreationState creationState) { - switch ( classification ) { - case ARRAY: - return createArrayInitializerProducer( - navigablePath, - attributeMapping, - fetchParent, - selected, - indexFetch, - elementFetch, - creationState - ); - case BAG: - case ID_BAG: + return switch ( classification ) { + case ARRAY -> createArrayInitializerProducer( + navigablePath, + attributeMapping, + fetchParent, + selected, + indexFetch, + elementFetch, + creationState + ); + case BAG, ID_BAG -> { assert indexFetch == null; - return createBagInitializerProducer( - navigablePath, - attributeMapping, - fetchParent, - selected, - elementFetch, - creationState - ); - case LIST: - return createListInitializerProducer( - navigablePath, - attributeMapping, - fetchParent, - selected, - indexFetch, - elementFetch, - creationState - ); - case MAP: - case ORDERED_MAP: - case SORTED_MAP: - return createMapInitializerProducer( - navigablePath, - attributeMapping, - fetchParent, - selected, - indexFetch, - elementFetch, - creationState - ); - case SET: - case ORDERED_SET: - case SORTED_SET: - return createSetInitializerProducer( + yield createBagInitializerProducer( navigablePath, attributeMapping, fetchParent, @@ -274,9 +232,34 @@ public static CollectionInitializerProducer createCollectionTypeWrapperInitializ elementFetch, creationState ); - default: - throw new IllegalArgumentException( "Unknown CollectionClassification : " + classification ); - } + } + case LIST -> createListInitializerProducer( + navigablePath, + attributeMapping, + fetchParent, + selected, + indexFetch, + elementFetch, + creationState + ); + case MAP, ORDERED_MAP, SORTED_MAP -> createMapInitializerProducer( + navigablePath, + attributeMapping, + fetchParent, + selected, + indexFetch, + elementFetch, + creationState + ); + case SET, ORDERED_SET, SORTED_SET -> createSetInitializerProducer( + navigablePath, + attributeMapping, + fetchParent, + selected, + elementFetch, + creationState + ); + }; } private InitializerProducerBuilder() { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/PluralAttributeMetadataImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/PluralAttributeMetadataImpl.java index 3a3d25a1c3c0..ad3758e30352 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/PluralAttributeMetadataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/PluralAttributeMetadataImpl.java @@ -45,7 +45,7 @@ class PluralAttributeMetadataImpl this.elementClassification = elementClassification; this.listIndexOrMapKeyClassification = listIndexOrMapKeyClassification; - final ParameterizedType signatureType = AttributeFactory.getSignatureType( member ); + final var signatureType = AttributeFactory.getSignatureType( member ); switch ( collectionClassification ) { case MAP: case SORTED_MAP: @@ -53,21 +53,17 @@ class PluralAttributeMetadataImpl keyJavaType = signatureType != null ? getClassFromGenericArgument( signatureType.getActualTypeArguments()[0] ) : Object.class; - elementJavaType = signatureType != null ? getClassFromGenericArgument( signatureType.getActualTypeArguments()[1] ) : Object.class; - break; } case ARRAY: case LIST: { keyJavaType = Integer.class; - elementJavaType = signatureType != null ? getClassFromGenericArgument( signatureType.getActualTypeArguments()[0] ) : Object.class; - break; } default: { @@ -130,14 +126,11 @@ public AttributeMetadata getAttributeMetadata() { } private static ValueClassification toValueClassification(AttributeClassification classification) { - switch ( classification ) { - case EMBEDDED: - return ValueClassification.EMBEDDABLE; - case BASIC: - return ValueClassification.BASIC; - default: - return ValueClassification.ENTITY; - } + return switch ( classification ) { + case EMBEDDED -> ValueClassification.EMBEDDABLE; + case BASIC -> ValueClassification.BASIC; + default -> ValueClassification.ENTITY; + }; } private Class getClassFromGenericArgument(java.lang.reflect.Type type) { @@ -165,7 +158,7 @@ else if ( type instanceof WildcardType wildcardType ) { } public static CollectionClassification determineCollectionType(Class javaType, Property property) { - final Collection collection = (Collection) property.getValue(); + final var collection = (Collection) property.getValue(); if ( java.util.List.class.isAssignableFrom( javaType ) ) { return CollectionClassification.LIST; diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index f9e1afb00473..736330398a7a 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -316,8 +316,7 @@ default String[] getSynchronizedQuerySpaces() { } default void visitQuerySpaces(Consumer querySpaceConsumer) { - final String[] spaces = getSynchronizedQuerySpaces(); - for (String space : spaces) { + for ( String space : getSynchronizedQuerySpaces() ) { querySpaceConsumer.accept(space); } } 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 5cef5b01df5f..3df35ffd7e8c 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 @@ -50,7 +50,17 @@ default void resolveState(RowProcessingState rowProcessingState) { * {@link Initializer#isResultInitializer()}. */ default void forEachResultAssembler(BiConsumer, X> consumer, X arg) { + } + + default boolean isEager() { + final var initializer = getInitializer(); + return initializer != null && initializer.isEager(); + } + default boolean hasLazySubInitializers() { + final var initializer = getInitializer(); + return initializer != null + && ( !initializer.isEager() || initializer.hasLazySubInitializers() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractCollectionInitializer.java index aac074e8b48f..23232264aa22 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractCollectionInitializer.java @@ -9,7 +9,6 @@ import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CollectionKey; import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; @@ -150,8 +149,8 @@ protected void setMissing(Data data) { } protected void resolveCollectionKey(Data data, boolean checkPreviousRow) { - final CollectionKey oldKey = data.collectionKey; - final PersistentCollection oldCollectionInstance = data.getCollectionInstance(); + final var oldKey = data.collectionKey; + final var oldCollectionInstance = data.getCollectionInstance(); data.collectionKey = null; data.setCollectionInstance( null ); @@ -170,7 +169,7 @@ protected void resolveCollectionKey(Data data, boolean checkPreviousRow) { return; } } - final CollectionPersister persister = collectionAttributeMapping.getCollectionDescriptor(); + final var persister = collectionAttributeMapping.getCollectionDescriptor(); // Try to reuse the previous collection key and collection if possible if ( checkPreviousRow && oldKey != null && areKeysEqual( oldKey.getKey(), data.collectionKeyValue ) ) { data.collectionKey = oldKey; @@ -187,10 +186,17 @@ private boolean areKeysEqual(Object key1, Object key2) { return keyTypeForEqualsHashCode == null ? key1.equals( key2 ) : keyTypeForEqualsHashCode.isEqual( key1, key2 ); } + PersistentCollection getCollection(CollectionInitializerData data, Object instance) { + return collectionAttributeMapping.getCollectionDescriptor().isArray() + ? data.getRowProcessingState().getSession().getPersistenceContextInternal() + .getCollectionHolder( instance ) + : (PersistentCollection) instance; + } + @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { if ( collectionKeyResultAssembler != null ) { - final Initializer initializer = collectionKeyResultAssembler.getInitializer(); + final var initializer = collectionKeyResultAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, data.getRowProcessingState() ); } @@ -199,8 +205,9 @@ protected void forEachSubInitializer(BiConsumer, RowProcessingSta @Override public @Nullable PersistentCollection getCollectionInstance(Data data) { - return data.getState() == State.UNINITIALIZED || data.getState() == State.MISSING ? null : - data.getCollectionInstance(); + return data.getState() == State.UNINITIALIZED || data.getState() == State.MISSING + ? null + : data.getCollectionInstance(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java index a28f746c9777..c9b700fcd633 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractImmediateCollectionInitializer.java @@ -8,14 +8,11 @@ import java.util.function.BiConsumer; import org.hibernate.LockMode; -import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.CollectionClassification; import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; @@ -81,9 +78,10 @@ public AbstractImmediateCollectionInitializer( isResultInitializer, creationState ); - this.collectionValueKeyResultAssembler = collectionKeyResult == collectionValueKeyResult - ? null - : collectionValueKeyResult.createResultAssembler( this, creationState ); + collectionValueKeyResultAssembler = + collectionKeyResult == collectionValueKeyResult + ? null + : collectionValueKeyResult.createResultAssembler( this, creationState ); } @Override @@ -95,7 +93,7 @@ protected ImmediateCollectionInitializerData createInitializerData(RowProcessing protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { super.forEachSubInitializer( consumer, data ); if ( collectionValueKeyResultAssembler != null ) { - final Initializer initializer = collectionValueKeyResultAssembler.getInitializer(); + final var initializer = collectionValueKeyResultAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, data.getRowProcessingState() ); } @@ -129,11 +127,11 @@ public void resolveState(Data data) { if ( collectionValueKeyResultAssembler != null ) { collectionValueKeyResultAssembler.resolveState( data.getRowProcessingState() ); } - final DomainResultAssembler indexAssembler = getIndexAssembler(); + final var indexAssembler = getIndexAssembler(); if ( indexAssembler != null ) { indexAssembler.resolveState( data.getRowProcessingState() ); } - final DomainResultAssembler elementAssembler = getElementAssembler(); + final var elementAssembler = getElementAssembler(); if ( elementAssembler != null ) { elementAssembler.resolveState( data.getRowProcessingState() ); } @@ -152,11 +150,13 @@ public void resolveFromPreviousRow(Data data) { */ private boolean resolveCollectionContentKey(Data data) { assert collectionValueKeyResultAssembler != null; - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); //noinspection unchecked - final Initializer initializer = (Initializer) collectionValueKeyResultAssembler.getInitializer(); + final var initializer = + (Initializer) + collectionValueKeyResultAssembler.getInitializer(); if ( initializer != null ) { - InitializerData subData = initializer.getData( rowProcessingState ); + final var subData = initializer.getData( rowProcessingState ); initializer.resolveKey( subData ); if ( subData.getState() == State.MISSING ) { return true; @@ -176,13 +176,15 @@ private boolean resolveCollectionContentKey(Data data) { } private void resolveKeySubInitializers(Data data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final DomainResultAssembler indexAssembler = getIndexAssembler(); - final Initializer indexInitializer; - if ( indexAssembler != null && ( indexInitializer = indexAssembler.getInitializer() ) != null ) { - indexInitializer.resolveKey( rowProcessingState ); + final var rowProcessingState = data.getRowProcessingState(); + final var indexAssembler = getIndexAssembler(); + if ( indexAssembler != null ) { + final var indexInitializer = indexAssembler.getInitializer(); + if ( indexInitializer != null ) { + indexInitializer.resolveKey( rowProcessingState ); + } } - final Initializer elementInitializer = getElementAssembler().getInitializer(); + final var elementInitializer = getElementAssembler().getInitializer(); if ( elementInitializer != null ) { elementInitializer.resolveKey( rowProcessingState ); } @@ -190,122 +192,117 @@ private void resolveKeySubInitializers(Data data) { @Override public void resolveInstance(Data data) { - if ( data.getState() != State.KEY_RESOLVED ) { - // already resolved - return; - } - - // Being a result initializer means that this collection initializer is for lazy loading, - // which has a very high chance that a collection resolved of the previous row is the same for the current row, - // so pass that flag as indicator whether to check previous row state. - // Note that we don't need to check previous rows in other cases, - // because the previous row checks are done by the owner of the collection initializer already. - resolveCollectionKey( data, isResultInitializer ); - if ( data.getState() != State.KEY_RESOLVED ) { - return; - } - - data.setState( State.RESOLVED ); - data.responsibility = null; + if ( data.getState() == State.KEY_RESOLVED ) {// Being a result initializer means that this collection initializer is for lazy loading, + // which has a very high chance that a collection resolved of the previous row is the same for the current row, + // so pass that flag as indicator whether to check previous row state. + // Note that we don't need to check previous rows in other cases, + // because the previous row checks are done by the owner of the collection initializer already. + resolveCollectionKey( data, isResultInitializer ); + if ( data.getState() == State.KEY_RESOLVED ) { + data.setState( State.RESOLVED ); + data.responsibility = null; + + // determine the PersistentCollection instance to use and whether + // we (this initializer) is responsible for loading its state + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // First, look for a LoadingCollectionEntry + final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final SharedSessionContractImplementor session = rowProcessingState.getSession(); + final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); + final CollectionKey collectionKey = data.collectionKey; + assert collectionKey != null; + final LoadingCollectionEntry existingLoadingEntry = persistenceContext.getLoadContexts() + .findLoadingCollectionEntry( collectionKey ); + final PersistentCollection existing; + final PersistentCollection existingUnowned; + if ( existingLoadingEntry != null ) { + data.setCollectionInstance( existingLoadingEntry.getCollectionInstance() ); + + if ( existingLoadingEntry.getInitializer() == this ) { + assert !data.shallowCached; + // we are responsible for loading the collection values + data.responsibility = (LoadingCollectionEntryImpl) existingLoadingEntry; + } + else { + // the entity is already being loaded elsewhere + data.setState( State.INITIALIZED ); + } + } + else if ( (existing = persistenceContext.getCollection( collectionKey )) != null ) { + data.setCollectionInstance( existing ); - // determine the PersistentCollection instance to use and whether - // we (this initializer) is responsible for loading its state - - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // First, look for a LoadingCollectionEntry - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final SharedSessionContractImplementor session = rowProcessingState.getSession(); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - final CollectionKey collectionKey = data.collectionKey; - assert collectionKey != null; - final LoadingCollectionEntry existingLoadingEntry = persistenceContext.getLoadContexts() - .findLoadingCollectionEntry( collectionKey ); - final PersistentCollection existing; - final PersistentCollection existingUnowned; - if ( existingLoadingEntry != null ) { - data.setCollectionInstance( existingLoadingEntry.getCollectionInstance() ); - - if ( existingLoadingEntry.getInitializer() == this ) { - assert !data.shallowCached; - // we are responsible for loading the collection values - data.responsibility = (LoadingCollectionEntryImpl) existingLoadingEntry; - } - else { - // the entity is already being loaded elsewhere - data.setState( State.INITIALIZED ); - } - } - else if ( ( existing = persistenceContext.getCollection( collectionKey ) ) != null ) { - data.setCollectionInstance( existing ); + // we found the corresponding collection instance on the Session. If + // it is already initialized we have nothing to do - // we found the corresponding collection instance on the Session. If - // it is already initialized we have nothing to do + if ( existing.wasInitialized() ) { + data.setState( State.INITIALIZED ); + } + else if ( !data.shallowCached ) { + takeResponsibility( data ); + } + } + else if ( (existingUnowned = persistenceContext.useUnownedCollection( collectionKey )) != null ) { + data.setCollectionInstance( existingUnowned ); - if ( existing.wasInitialized() ) { - data.setState( State.INITIALIZED ); - } - else if ( !data.shallowCached ) { - takeResponsibility( data ); - } - } - else if ( ( existingUnowned = persistenceContext.useUnownedCollection( collectionKey ) ) != null ) { - data.setCollectionInstance( existingUnowned ); + // we found the corresponding collection instance as unowned on the Session. If + // it is already initialized we have nothing to do - // we found the corresponding collection instance as unowned on the Session. If - // it is already initialized we have nothing to do + if ( existingUnowned.wasInitialized() ) { + data.setState( State.INITIALIZED ); + } + else if ( !data.shallowCached ) { + takeResponsibility( data ); + } + } + else { + final var collectionDescriptor = getCollectionAttributeMapping().getCollectionDescriptor(); + final var collectionSemantics = collectionDescriptor.getCollectionSemantics(); + final var persistentCollection = + collectionSemantics.instantiateWrapper( + collectionKey.getKey(), + getInitializingCollectionDescriptor(), + session + ); + data.setCollectionInstance( persistentCollection ); + + if ( owningEntityInitializer != null ) { + final Object targetInstance = + owningEntityInitializer.getTargetInstance( rowProcessingState ); + assert targetInstance != null; + data.getCollectionInstance().setOwner( targetInstance ); + } - if ( existingUnowned.wasInitialized() ) { - data.setState( State.INITIALIZED ); - } - else if ( !data.shallowCached ) { - takeResponsibility( data ); - } - } - else { - final CollectionPersister collectionDescriptor = getCollectionAttributeMapping().getCollectionDescriptor(); - final CollectionSemantics collectionSemantics = collectionDescriptor.getCollectionSemantics(); - final PersistentCollection persistentCollection = collectionSemantics.instantiateWrapper( - collectionKey.getKey(), - getInitializingCollectionDescriptor(), - session - ); - data.setCollectionInstance( persistentCollection ); - - if ( owningEntityInitializer != null ) { - assert owningEntityInitializer.getTargetInstance( rowProcessingState ) != null; - data.getCollectionInstance().setOwner( owningEntityInitializer.getTargetInstance( rowProcessingState ) ); - } + persistenceContext.addUninitializedCollection( + collectionDescriptor, + persistentCollection, + collectionKey.getKey() + ); - persistenceContext.addUninitializedCollection( - collectionDescriptor, - persistentCollection, - collectionKey.getKey() - ); + if ( !data.shallowCached ) { + takeResponsibility( data ); + } + } - if ( !data.shallowCached ) { - takeResponsibility( data ); + if ( data.shallowCached ) { + assert data.responsibility == null; + initializeShallowCached( data ); + } } } - - if ( data.shallowCached ) { - assert data.responsibility == null; - initializeShallowCached( data ); - } } protected void initializeShallowCached(Data data) { assert data.shallowCached; - final PersistenceContext persistenceContext = data.getRowProcessingState().getSession() - .getPersistenceContextInternal(); // If this is a query cache hit with the shallow query cache layout, // we have to lazy load the collection instead - final PersistentCollection collectionInstance = data.getCollectionInstance(); + final var collectionInstance = data.getCollectionInstance(); assert collectionInstance != null; collectionInstance.forceInitialization(); - if ( collectionAttributeMapping.getCollectionDescriptor() - .getCollectionSemantics() - .getCollectionClassification() == CollectionClassification.ARRAY ) { - persistenceContext.addCollectionHolder( collectionInstance ); + if ( collectionAttributeMapping.getCollectionDescriptor().isArray()) { + data.getRowProcessingState().getSession() + .getPersistenceContextInternal() + .addCollectionHolder( collectionInstance ); } data.setState( State.INITIALIZED ); initializeSubInstancesFromParent( data ); @@ -323,89 +320,78 @@ public void resolveInstance(Object instance, Data data) { assert data.getState() == State.UNINITIALIZED || instance == data.getCollectionInstance(); if ( instance == null ) { setMissing( data ); - return; - } - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final PersistentCollection persistentCollection; - // Check if the given instance is different from the previous row state to avoid creating CollectionKey - if ( data.getCollectionInstance() != instance ) { - final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal(); - if ( collectionAttributeMapping.getCollectionDescriptor() - .getCollectionSemantics() - .getCollectionClassification() == CollectionClassification.ARRAY ) { - persistentCollection = persistenceContext.getCollectionHolder( instance ); - } - else { - persistentCollection = (PersistentCollection) instance; - } - data.collectionKeyValue = persistentCollection.getKey(); - resolveCollectionKey( data, false ); - data.setCollectionInstance( persistentCollection ); - data.responsibility = null; } else { - persistentCollection = (PersistentCollection) instance; - } - data.collectionValueKey = null; - if ( persistentCollection.wasInitialized() ) { - data.setState( State.INITIALIZED ); - if ( data.shallowCached ) { - initializeShallowCached( data ); + final var rowProcessingState = data.getRowProcessingState(); + final PersistentCollection collection; + // Check if the given instance is different from the previous row state to avoid creating CollectionKey + if ( data.getCollectionInstance() != instance ) { + collection = getCollection( data, instance ); + data.collectionKeyValue = collection.getKey(); + resolveCollectionKey( data, false ); + data.setCollectionInstance( collection ); + data.responsibility = null; } else { - resolveInstanceSubInitializers( data ); + collection = (PersistentCollection) instance; } - if ( rowProcessingState.needsResolveState() ) { - // Resolve the state of the identifier if result caching is enabled and this is not a query cache hit - if ( collectionKeyResultAssembler != null ) { - collectionKeyResultAssembler.resolveState( rowProcessingState ); + data.collectionValueKey = null; + if ( collection.wasInitialized() ) { + data.setState( State.INITIALIZED ); + if ( data.shallowCached ) { + initializeShallowCached( data ); + } + else { + resolveInstanceSubInitializers( data ); } - if ( !getInitializingCollectionDescriptor().useShallowQueryCacheLayout() ) { - if ( collectionValueKeyResultAssembler != null ) { - collectionValueKeyResultAssembler.resolveState( rowProcessingState ); + if ( rowProcessingState.needsResolveState() ) { + // Resolve the state of the identifier if result caching is enabled and this is not a query cache hit + if ( collectionKeyResultAssembler != null ) { + collectionKeyResultAssembler.resolveState( rowProcessingState ); + } + if ( !getInitializingCollectionDescriptor().useShallowQueryCacheLayout() ) { + if ( collectionValueKeyResultAssembler != null ) { + collectionValueKeyResultAssembler.resolveState( rowProcessingState ); + } + resolveCollectionContentState( rowProcessingState ); } - resolveCollectionContentState( rowProcessingState ); } } - } - else { - if ( data.shallowCached ) { - data.setState( State.INITIALIZED ); - initializeShallowCached( data ); - } else { - data.setState( State.RESOLVED ); - final boolean rowContainsCollectionContent; - if ( collectionValueKeyResultAssembler != null ) { - rowContainsCollectionContent = resolveCollectionContentKey( data ); + if ( data.shallowCached ) { + data.setState( State.INITIALIZED ); + initializeShallowCached( data ); } else { - rowContainsCollectionContent = true; - } - if ( data.responsibility == null ) { - final LoadingCollectionEntry existingLoadingEntry = rowProcessingState.getSession() - .getPersistenceContextInternal() - .getLoadContexts() - .findLoadingCollectionEntry( data.collectionKey ); - if ( existingLoadingEntry != null ) { - if ( existingLoadingEntry.getInitializer() == this ) { - // we are responsible for loading the collection values - data.responsibility = (LoadingCollectionEntryImpl) existingLoadingEntry; + data.setState( State.RESOLVED ); + final boolean rowContainsCollectionContent = + collectionValueKeyResultAssembler == null + || resolveCollectionContentKey( data ); + if ( data.responsibility == null ) { + final var existingLoadingEntry = + rowProcessingState.getSession().getPersistenceContextInternal() + .getLoadContexts().findLoadingCollectionEntry( data.collectionKey ); + if ( existingLoadingEntry != null ) { + if ( existingLoadingEntry.getInitializer() == this ) { + // we are responsible for loading the collection values + data.responsibility = (LoadingCollectionEntryImpl) existingLoadingEntry; + } + else { + // the collection is already being loaded elsewhere + data.setState( State.INITIALIZED ); + if ( rowContainsCollectionContent && rowProcessingState.needsResolveState() + && !getInitializingCollectionDescriptor().useShallowQueryCacheLayout() ) { + // Resolve the state of the content if result caching is enabled, + // and this is not a query cache hit, and the collection doesn't + // use a shallow query cache layout + resolveCollectionContentState( rowProcessingState ); + } + } } else { - // the collection is already being loaded elsewhere - data.setState( State.INITIALIZED ); - if ( rowContainsCollectionContent && rowProcessingState.needsResolveState() - && !getInitializingCollectionDescriptor().useShallowQueryCacheLayout() ) { - // Resolve the state of the content if result caching is enabled and this is not a query cache hit - // and the collection doesn't use a shallow query cache layout - resolveCollectionContentState( rowProcessingState ); - } + takeResponsibility( data ); } } - else { - takeResponsibility( data ); - } } } } @@ -414,7 +400,7 @@ public void resolveInstance(Object instance, Data data) { protected abstract void resolveInstanceSubInitializers(Data data); private void resolveCollectionContentState(RowProcessingState rowProcessingState) { - final DomainResultAssembler indexAssembler = getIndexAssembler(); + final var indexAssembler = getIndexAssembler(); if ( indexAssembler != null ) { indexAssembler.resolveState( rowProcessingState ); } @@ -422,36 +408,35 @@ private void resolveCollectionContentState(RowProcessingState rowProcessingState } protected void takeResponsibility(Data data) { - data.responsibility = new LoadingCollectionEntryImpl( - getCollectionAttributeMapping().getCollectionDescriptor(), - this, - data.collectionKey.getKey(), - data.getCollectionInstance() - ); - data.getRowProcessingState().getJdbcValuesSourceProcessingState().registerLoadingCollection( - data.collectionKey, - data.responsibility - ); + data.responsibility = + new LoadingCollectionEntryImpl( + getCollectionAttributeMapping().getCollectionDescriptor(), + this, + data.collectionKey.getKey(), + data.getCollectionInstance() + ); + data.getRowProcessingState().getJdbcValuesSourceProcessingState() + .registerLoadingCollection( data.collectionKey, data.responsibility ); } @Override public void initializeInstance(Data data) { - if ( data.getState() != State.RESOLVED || data.responsibility == null ) { - return; - } - data.setState( State.INITIALIZED ); + if ( data.getState() == State.RESOLVED && data.responsibility != null ) { + data.setState( State.INITIALIZED ); - if ( data.collectionValueKey == null && collectionValueKeyResultAssembler != null ) { - final Initializer initializer = collectionValueKeyResultAssembler.getInitializer(); - if ( initializer != null ) { - data.collectionValueKey = collectionValueKeyResultAssembler.assemble( data.getRowProcessingState() ); + if ( data.collectionValueKey == null && collectionValueKeyResultAssembler != null ) { + final var initializer = collectionValueKeyResultAssembler.getInitializer(); + if ( initializer != null ) { + data.collectionValueKey = collectionValueKeyResultAssembler.assemble( + data.getRowProcessingState() ); + } } - } - // the RHS key value of the association - determines if the row contains an element of the initializing collection - if ( collectionValueKeyResultAssembler == null || data.collectionValueKey != null ) { - // the row contains an element in the collection... - data.responsibility.load( data, this ); + // the RHS key value of the association - determines if the row contains an element of the initializing collection + if ( collectionValueKeyResultAssembler == null || data.collectionValueKey != null ) { + // the row contains an element in the collection... + data.responsibility.load( data, this ); + } } } @@ -464,15 +449,9 @@ public void initializeInstanceFromParent(Object parentInstance, Data data) { @Override public boolean hasLazySubInitializers() { - final DomainResultAssembler indexAssembler = getIndexAssembler(); - if ( indexAssembler != null ) { - final Initializer initializer = indexAssembler.getInitializer(); - if ( initializer != null && ( !initializer.isEager() || initializer.hasLazySubInitializers() ) ) { - return true; - } - } - final Initializer initializer = getElementAssembler().getInitializer(); - return initializer != null && ( !initializer.isEager() || initializer.hasLazySubInitializers() ); + final var indexAssembler = getIndexAssembler(); + return indexAssembler != null && indexAssembler.hasLazySubInitializers() + || getElementAssembler().hasLazySubInitializers(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractNonJoinCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractNonJoinCollectionInitializer.java index b2aea7947a5c..82e88f3469fc 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractNonJoinCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/AbstractNonJoinCollectionInitializer.java @@ -4,21 +4,11 @@ */ package org.hibernate.sql.results.graph.collection.internal; -import org.hibernate.collection.spi.CollectionSemantics; -import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.spi.CollectionKey; -import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.CollectionClassification; import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; -import org.hibernate.sql.results.graph.InitializerData; import org.hibernate.sql.results.graph.InitializerParent; -import org.hibernate.sql.results.graph.collection.LoadingCollectionEntry; -import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.checkerframework.checker.nullness.qual.Nullable; @@ -48,82 +38,69 @@ public AbstractNonJoinCollectionInitializer( } protected void resolveInstance(Data data, boolean isEager) { - if ( data.getState() != State.KEY_RESOLVED ) { - // already resolved - return; - } - - resolveCollectionKey( data, false ); if ( data.getState() == State.KEY_RESOLVED ) { - assert owningEntityInitializer != null; - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - // We can avoid processing further if the parent is already initialized, - // as the value produced by this initializer will never be used anyway. - final InitializerData owningEntityData = owningEntityInitializer.getData( rowProcessingState ); - if ( owningEntityData.getState() == State.INITIALIZED ) { - // It doesn't matter if it's eager or lazy, the collection object can not be referred to, - // so it doesn't make sense to create or initialize it - data.setState( State.MISSING ); - return; - } - // This initializer is done initializing, since this is only invoked for delayed or select initializers - data.setState( State.INITIALIZED ); - - final SharedSessionContractImplementor session = rowProcessingState.getSession(); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - final CollectionKey collectionKey = data.collectionKey; - assert collectionKey != null; - - final LoadingCollectionEntry loadingEntry = persistenceContext.getLoadContexts() - .findLoadingCollectionEntry( collectionKey ); - - if ( loadingEntry != null ) { - final PersistentCollection collectionInstance = loadingEntry.getCollectionInstance(); - data.setCollectionInstance( collectionInstance ); - if ( collectionInstance.getOwner() == null ) { - assert owningEntityInitializer.getTargetInstance( owningEntityData ) != null; - collectionInstance.setOwner( owningEntityInitializer.getTargetInstance( owningEntityData ) ); + resolveCollectionKey( data, false ); + if ( data.getState() == State.KEY_RESOLVED ) { + assert owningEntityInitializer != null; + final var rowProcessingState = data.getRowProcessingState(); + // We can avoid processing further if the parent is already initialized, + // as the value produced by this initializer will never be used anyway. + final var owningEntityData = owningEntityInitializer.getData( rowProcessingState ); + if ( owningEntityData.getState() == State.INITIALIZED ) { + // It doesn't matter if it's eager or lazy, the collection object can not be referred to, + // so it doesn't make sense to create or initialize it + data.setState( State.MISSING ); } - return; - } - - final PersistentCollection existing = persistenceContext.getCollection( collectionKey ); - - if ( existing != null ) { - data.setCollectionInstance( existing ); - if ( existing.getOwner() == null ) { - assert owningEntityInitializer.getTargetInstance( owningEntityData ) != null; - existing.setOwner( owningEntityInitializer.getTargetInstance( owningEntityData ) ); + else { + // This initializer is done initializing, since this is only invoked for delayed or select initializers + data.setState( State.INITIALIZED ); + + final var session = rowProcessingState.getSession(); + final var persistenceContext = session.getPersistenceContextInternal(); + final var collectionKey = data.collectionKey; + assert collectionKey != null; + + final var loadingEntry = + persistenceContext.getLoadContexts() + .findLoadingCollectionEntry( collectionKey ); + if ( loadingEntry != null ) { + final var collectionInstance = loadingEntry.getCollectionInstance(); + data.setCollectionInstance( collectionInstance ); + if ( collectionInstance.getOwner() == null ) { + assert owningEntityInitializer.getTargetInstance( owningEntityData ) != null; + collectionInstance.setOwner( + owningEntityInitializer.getTargetInstance( owningEntityData ) ); + } + } + else { + final var existing = persistenceContext.getCollection( collectionKey ); + if ( existing != null ) { + data.setCollectionInstance( existing ); + if ( existing.getOwner() == null ) { + assert owningEntityInitializer.getTargetInstance( owningEntityData ) != null; + existing.setOwner( owningEntityInitializer.getTargetInstance( owningEntityData ) ); + } + } + else { + final var collectionDescriptor = collectionAttributeMapping.getCollectionDescriptor(); + final Object key = collectionKey.getKey(); + final var collection = + collectionDescriptor.getCollectionSemantics() + .instantiateWrapper( key, collectionDescriptor, session ); + data.setCollectionInstance( collection ); + final Object targetInstance = owningEntityInitializer.getTargetInstance( owningEntityData ); + assert targetInstance != null; + collection.setOwner( targetInstance ); + persistenceContext.addUninitializedCollection( collectionDescriptor, collection, key ); + if ( isEager ) { + persistenceContext.addNonLazyCollection( collection ); + } + if ( collectionDescriptor.isArray() ) { + persistenceContext.addCollectionHolder( collection ); + } + } + } } - return; - } - - final CollectionPersister collectionDescriptor = collectionAttributeMapping.getCollectionDescriptor(); - final CollectionSemantics collectionSemantics = collectionDescriptor.getCollectionSemantics(); - final Object key = collectionKey.getKey(); - - final PersistentCollection persistentCollection = collectionSemantics.instantiateWrapper( - key, - collectionDescriptor, - session - ); - data.setCollectionInstance( persistentCollection ); - - assert owningEntityInitializer.getTargetInstance( owningEntityData ) != null; - persistentCollection.setOwner( owningEntityInitializer.getTargetInstance( owningEntityData ) ); - - persistenceContext.addUninitializedCollection( - collectionDescriptor, - persistentCollection, - key - ); - - if ( isEager ) { - persistenceContext.addNonLazyCollection( persistentCollection ); - } - - if ( collectionSemantics.getCollectionClassification() == CollectionClassification.ARRAY ) { - persistenceContext.addCollectionHolder( persistentCollection ); } } } @@ -133,17 +110,8 @@ protected void resolveInstance(Object instance, Data data, boolean isEager) { setMissing( data ); } else { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal(); - final PersistentCollection persistentCollection; - if ( collectionAttributeMapping.getCollectionDescriptor() - .getCollectionSemantics() - .getCollectionClassification() == CollectionClassification.ARRAY ) { - persistentCollection = persistenceContext.getCollectionHolder( instance ); - } - else { - persistentCollection = (PersistentCollection) instance; - } + final var rowProcessingState = data.getRowProcessingState(); + final var persistentCollection = getCollection( data, instance ); // resolving the collection key seems unnecessary // collectionKeyValue = persistentCollection.getKey(); // resolveCollectionKey( rowProcessingState, false ); @@ -151,10 +119,11 @@ protected void resolveInstance(Object instance, Data data, boolean isEager) { // This initializer is done initializing, since this is only invoked for delayed or select initializers data.setState( State.INITIALIZED ); if ( isEager && !persistentCollection.wasInitialized() ) { - persistenceContext.addNonLazyCollection( persistentCollection ); + rowProcessingState.getSession().getPersistenceContextInternal() + .addNonLazyCollection( persistentCollection ); } if ( collectionKeyResultAssembler != null && rowProcessingState.needsResolveState() ) { - // Resolve the state of the identifier if result caching is enabled and this is not a query cache hit + // Resolve the state of the identifier if result caching is enabled, and this is not a query cache hit collectionKeyResultAssembler.resolveState( rowProcessingState ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/ArrayInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/ArrayInitializer.java index 15ea69534c33..d6a45d87c071 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/ArrayInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/ArrayInitializer.java @@ -4,15 +4,12 @@ */ package org.hibernate.sql.results.graph.collection.internal; -import java.lang.reflect.Array; -import java.util.Iterator; import java.util.List; import java.util.function.BiConsumer; import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.collection.spi.PersistentArrayHolder; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; @@ -26,6 +23,9 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static java.lang.reflect.Array.get; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + /** * @author Chris Cranford */ @@ -57,15 +57,15 @@ public ArrayInitializer( creationState ); //noinspection unchecked - this.listIndexAssembler = (DomainResultAssembler) listIndexFetch.createAssembler( this, creationState ); - this.elementAssembler = elementFetch.createAssembler( this, creationState ); - this.indexBase = getCollectionAttributeMapping().getIndexMetadata().getListIndexBase(); + listIndexAssembler = (DomainResultAssembler) listIndexFetch.createAssembler( this, creationState ); + elementAssembler = elementFetch.createAssembler( this, creationState ); + indexBase = getCollectionAttributeMapping().getIndexMetadata().getListIndexBase(); } @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { super.forEachSubInitializer( consumer, data ); - final Initializer initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, data.getRowProcessingState() ); } @@ -78,7 +78,7 @@ protected void forEachSubInitializer(BiConsumer, RowProcessingSta @Override protected void readCollectionRow(ImmediateCollectionInitializerData data, List loadingState) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); final Integer indexValue = listIndexAssembler.assemble( rowProcessingState ); if ( indexValue == null ) { throw new HibernateException( "Illegal null value for array index encountered while reading: " @@ -104,21 +104,23 @@ protected void readCollectionRow(ImmediateCollectionInitializerData data, List initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final Iterator iter = getCollectionInstance( data ).elements(); + final var rowProcessingState = data.getRowProcessingState(); + final var iter = getCollectionInstance( data ).elements(); while ( iter.hasNext() ) { initializer.initializeInstanceFromParent( iter.next(), rowProcessingState ); } @@ -127,17 +129,17 @@ protected void initializeSubInstancesFromParent(ImmediateCollectionInitializerDa @Override protected void resolveInstanceSubInitializers(ImmediateCollectionInitializerData data) { - final Initializer initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); Integer index = listIndexAssembler.assemble( rowProcessingState ); if ( index != null ) { - final PersistentArrayHolder arrayHolder = getCollectionInstance( data ); + final var arrayHolder = getCollectionInstance( data ); assert arrayHolder != null; if ( indexBase != 0 ) { index -= indexBase; } - initializer.resolveInstance( Array.get( arrayHolder.getArray(), index ), rowProcessingState ); + initializer.resolveInstance( get( arrayHolder.getArray(), index ), rowProcessingState ); } } } @@ -154,6 +156,6 @@ public DomainResultAssembler getElementAssembler() { @Override public String toString() { - return "ArrayInitializer{" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "ArrayInitializer{" + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/BagInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/BagInitializer.java index fb6b0e7a31c3..d60520480d13 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/BagInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/BagInitializer.java @@ -7,11 +7,10 @@ import java.util.List; import java.util.function.BiConsumer; +import org.hibernate.AssertionFailure; import org.hibernate.LockMode; import org.hibernate.collection.spi.PersistentBag; -import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.collection.spi.PersistentIdentifierBag; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; @@ -25,6 +24,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + /** * Initializer for both {@link PersistentBag} and {@link PersistentIdentifierBag} * collections @@ -57,16 +58,17 @@ public BagInitializer( isResultInitializer, creationState ); - this.elementAssembler = elementFetch.createAssembler( this, creationState ); - this.collectionIdAssembler = collectionIdFetch == null - ? null - : collectionIdFetch.createAssembler( this, creationState ); + elementAssembler = elementFetch.createAssembler( this, creationState ); + collectionIdAssembler = + collectionIdFetch == null + ? null + : collectionIdFetch.createAssembler( this, creationState ); } @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { super.forEachSubInitializer( consumer, data ); - final Initializer initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, data.getRowProcessingState() ); } @@ -74,19 +76,16 @@ protected void forEachSubInitializer(BiConsumer, RowProcessingSta @Override protected void readCollectionRow(ImmediateCollectionInitializerData data, List loadingState) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); if ( collectionIdAssembler != null ) { final Object collectionId = collectionIdAssembler.assemble( rowProcessingState ); - if ( collectionId == null ) { - return; - } - final Object element = elementAssembler.assemble( rowProcessingState ); - if ( element == null ) { - // If element is null, then NotFoundAction must be IGNORE - return; + if ( collectionId != null ) { + final Object element = elementAssembler.assemble( rowProcessingState ); + if ( element != null ) { + loadingState.add( new Object[] {collectionId, element} ); + } + // otherwise, if element is null, then NotFoundAction must be IGNORE } - - loadingState.add( new Object[]{ collectionId, element } ); } else { final Object element = elementAssembler.assemble( rowProcessingState ); @@ -99,27 +98,30 @@ protected void readCollectionRow(ImmediateCollectionInitializerData data, List initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final PersistentCollection persistentCollection = getCollectionInstance( data ); + final var rowProcessingState = data.getRowProcessingState(); + final var persistentCollection = getCollectionInstance( data ); assert persistentCollection != null; - if ( persistentCollection instanceof PersistentBag ) { - for ( Object element : ( (PersistentBag) persistentCollection ) ) { + if ( persistentCollection instanceof PersistentBag bag ) { + for ( Object element : bag ) { initializer.initializeInstanceFromParent( element, rowProcessingState ); } } - else { - for ( Object element : ( (PersistentIdentifierBag) persistentCollection ) ) { + else if ( persistentCollection instanceof PersistentIdentifierBag idbag ) { + for ( Object element : idbag ) { initializer.initializeInstanceFromParent( element, rowProcessingState ); } } + else { + throw new AssertionFailure( "Unexpected collection type" ); + } } } @Override protected void resolveInstanceSubInitializers(ImmediateCollectionInitializerData data) { - final Initializer initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { initializer.resolveKey( data.getRowProcessingState() ); } @@ -137,6 +139,6 @@ public DomainResultAssembler getElementAssembler() { @Override public String toString() { - return "BagInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "BagInitializer(" + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java index 79d911f5d72a..9d2fb738119a 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/DelayedCollectionInitializer.java @@ -4,13 +4,14 @@ */ package org.hibernate.sql.results.graph.collection.internal; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.InitializerParent; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + /** * @author Steve Ebersole */ @@ -48,7 +49,7 @@ public boolean hasLazySubInitializers() { @Override public String toString() { - return "DelayedCollectionInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "DelayedCollectionInitializer(" + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/ListInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/ListInitializer.java index 78382cf1de83..e1f3a0aed7b5 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/ListInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/ListInitializer.java @@ -10,7 +10,6 @@ import org.hibernate.HibernateException; import org.hibernate.LockMode; import org.hibernate.collection.spi.PersistentList; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; @@ -24,6 +23,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + /** * CollectionInitializer for PersistentList loading * @@ -58,15 +59,15 @@ public ListInitializer( creationState ); //noinspection unchecked - this.listIndexAssembler = (DomainResultAssembler) listIndexFetch.createAssembler( this, creationState ); - this.elementAssembler = elementFetch.createAssembler( this, creationState ); - this.listIndexBase = attributeMapping.getIndexMetadata().getListIndexBase(); + listIndexAssembler = (DomainResultAssembler) listIndexFetch.createAssembler( this, creationState ); + elementAssembler = elementFetch.createAssembler( this, creationState ); + listIndexBase = attributeMapping.getIndexMetadata().getListIndexBase(); } @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { super.forEachSubInitializer( consumer, data ); - final Initializer initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, data.getRowProcessingState() ); } @@ -79,36 +80,32 @@ protected void forEachSubInitializer(BiConsumer, RowProcessingSta @Override protected void readCollectionRow(ImmediateCollectionInitializerData data, List loadingState) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); final Integer indexValue = listIndexAssembler.assemble( rowProcessingState ); if ( indexValue == null ) { throw new HibernateException( "Illegal null value for list index encountered while reading: " + getCollectionAttributeMapping().getNavigableRole() ); } final Object element = elementAssembler.assemble( rowProcessingState ); - if ( element == null ) { - // If element is null, then NotFoundAction must be IGNORE - return; - } - int index = indexValue; - - if ( listIndexBase != 0 ) { - index -= listIndexBase; - } - - for ( int i = loadingState.size(); i <= index; ++i ) { - loadingState.add( i, null ); + if ( element != null ) { + int index = indexValue; + if ( listIndexBase != 0 ) { + index -= listIndexBase; + } + for ( int i = loadingState.size(); i <= index; ++i ) { + loadingState.add( i, null ); + } + loadingState.set( index, element ); } - - loadingState.set( index, element ); + // else if the element is null, then NotFoundAction must be IGNORE } @Override protected void initializeSubInstancesFromParent(ImmediateCollectionInitializerData data) { - final Initializer initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final PersistentList list = getCollectionInstance( data ); + final var rowProcessingState = data.getRowProcessingState(); + final var list = getCollectionInstance( data ); assert list != null; for ( Object element : list ) { initializer.initializeInstanceFromParent( element, rowProcessingState ); @@ -118,12 +115,12 @@ protected void initializeSubInstancesFromParent(ImmediateCollectionInitializerDa @Override protected void resolveInstanceSubInitializers(ImmediateCollectionInitializerData data) { - final Initializer initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); Integer index = listIndexAssembler.assemble( rowProcessingState ); if ( index != null ) { - final PersistentList list = getCollectionInstance( data ); + final var list = getCollectionInstance( data ); assert list != null; if ( listIndexBase != 0 ) { index -= listIndexBase; @@ -145,6 +142,6 @@ public DomainResultAssembler getElementAssembler() { @Override public String toString() { - return "ListInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "ListInitializer(" + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/MapInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/MapInitializer.java index 75874bd1ad5c..08034a54de30 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/MapInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/MapInitializer.java @@ -5,12 +5,10 @@ package org.hibernate.sql.results.graph.collection.internal; import java.util.List; -import java.util.Map; import java.util.function.BiConsumer; import org.hibernate.LockMode; import org.hibernate.collection.spi.PersistentMap; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; @@ -24,6 +22,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + /** * Represents an immediate initialization of some sort (join, select, batch, sub-select) * of a persistent Map valued attribute. @@ -58,18 +58,18 @@ public MapInitializer( isResultInitializer, creationState ); - this.mapKeyAssembler = mapKeyFetch.createAssembler( this, creationState ); - this.mapValueAssembler = mapValueFetch.createAssembler( this, creationState ); + mapKeyAssembler = mapKeyFetch.createAssembler( this, creationState ); + mapValueAssembler = mapValueFetch.createAssembler( this, creationState ); } @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { super.forEachSubInitializer( consumer, data ); - final Initializer keyInitializer = mapKeyAssembler.getInitializer(); + final var keyInitializer = mapKeyAssembler.getInitializer(); if ( keyInitializer != null ) { consumer.accept( keyInitializer, data.getRowProcessingState() ); } - final Initializer valueInitializer = mapValueAssembler.getInitializer(); + final var valueInitializer = mapValueAssembler.getInitializer(); if ( valueInitializer != null ) { consumer.accept( valueInitializer, data.getRowProcessingState() ); } @@ -82,29 +82,27 @@ protected void forEachSubInitializer(BiConsumer, RowProcessingSta @Override protected void readCollectionRow(ImmediateCollectionInitializerData data, List loadingState) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); final Object key = mapKeyAssembler.assemble( rowProcessingState ); - if ( key == null ) { - // If element is null, then NotFoundAction must be IGNORE - return; - } - final Object value = mapValueAssembler.assemble( rowProcessingState ); - if ( value == null ) { - // If element is null, then NotFoundAction must be IGNORE - return; + if ( key != null ) { + final Object value = mapValueAssembler.assemble( rowProcessingState ); + if ( value != null ) { + loadingState.add( new Object[] {key, value} ); + } + // else if the element is null, then NotFoundAction must be IGNORE } - loadingState.add( new Object[] { key, value } ); + // else if the key is null, then NotFoundAction must be IGNORE } @Override protected void initializeSubInstancesFromParent(ImmediateCollectionInitializerData data) { - final Initializer keyInitializer = mapKeyAssembler.getInitializer(); - final Initializer valueInitializer = mapValueAssembler.getInitializer(); + final var keyInitializer = mapKeyAssembler.getInitializer(); + final var valueInitializer = mapValueAssembler.getInitializer(); if ( keyInitializer != null || valueInitializer != null ) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final PersistentMap map = getCollectionInstance( data ); + final var rowProcessingState = data.getRowProcessingState(); + final var map = getCollectionInstance( data ); assert map != null; - for ( Map.Entry entry : map.entrySet() ) { + for ( var entry : map.entrySet() ) { if ( keyInitializer != null ) { keyInitializer.initializeInstanceFromParent( entry.getKey(), rowProcessingState ); } @@ -117,9 +115,9 @@ protected void initializeSubInstancesFromParent(ImmediateCollectionInitializerDa @Override protected void resolveInstanceSubInitializers(ImmediateCollectionInitializerData data) { - final Initializer keyInitializer = mapKeyAssembler.getInitializer(); - final Initializer valueInitializer = mapValueAssembler.getInitializer(); - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var keyInitializer = mapKeyAssembler.getInitializer(); + final var valueInitializer = mapValueAssembler.getInitializer(); + final var rowProcessingState = data.getRowProcessingState(); if ( keyInitializer == null && valueInitializer != null ) { // For now, we only support resolving the value initializer instance when keys have no initializer, // though we could also support map keys with an initializer given that the initialized java type: @@ -156,6 +154,6 @@ public DomainResultAssembler getElementAssembler() { @Override public String toString() { - return "MapInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "MapInitializer(" + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SelectEagerCollectionInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SelectEagerCollectionInitializer.java index 64ed29d2729a..4d59266d8231 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SelectEagerCollectionInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SelectEagerCollectionInitializer.java @@ -4,9 +4,6 @@ */ package org.hibernate.sql.results.graph.collection.internal; -import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.internal.log.LoggingHelper; -import org.hibernate.metamodel.CollectionClassification; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; @@ -15,10 +12,13 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + /** * @author Andrea Boriero */ -public class SelectEagerCollectionInitializer extends AbstractNonJoinCollectionInitializer { +public class SelectEagerCollectionInitializer + extends AbstractNonJoinCollectionInitializer { public SelectEagerCollectionInitializer( NavigablePath fetchedPath, @@ -46,16 +46,7 @@ public void initializeInstanceFromParent(Object parentInstance, CollectionInitia setMissing( data ); } else { - final PersistentCollection collection; - if ( collectionAttributeMapping.getCollectionDescriptor() - .getCollectionSemantics() - .getCollectionClassification() == CollectionClassification.ARRAY ) { - collection = data.getRowProcessingState().getSession().getPersistenceContextInternal() - .getCollectionHolder( instance ); - } - else { - collection = (PersistentCollection) instance; - } + final var collection = getCollection( data, instance ); data.setState( State.INITIALIZED ); data.setCollectionInstance( collection ); collection.forceInitialization(); @@ -64,6 +55,6 @@ public void initializeInstanceFromParent(Object parentInstance, CollectionInitia @Override public String toString() { - return "SelectEagerCollectionInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "SelectEagerCollectionInitializer(" + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SetInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SetInitializer.java index 9d3fea87edc8..a9d9163000a3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SetInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/SetInitializer.java @@ -9,7 +9,6 @@ import org.hibernate.LockMode; import org.hibernate.collection.spi.PersistentSet; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; @@ -23,6 +22,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + /** * @author Steve Ebersole */ @@ -56,7 +57,7 @@ public SetInitializer( @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { super.forEachSubInitializer( consumer, data ); - final Initializer initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, data.getRowProcessingState() ); } @@ -69,21 +70,20 @@ protected void forEachSubInitializer(BiConsumer, RowProcessingSta @Override protected void readCollectionRow(ImmediateCollectionInitializerData data, List loadingState) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); final Object element = elementAssembler.assemble( rowProcessingState ); - if ( element == null ) { - // If element is null, then NotFoundAction must be IGNORE - return; + if ( element != null ) { + loadingState.add( element ); } - loadingState.add( element ); + // else if the element is null, then NotFoundAction must be IGNORE } @Override protected void initializeSubInstancesFromParent(ImmediateCollectionInitializerData data) { - final Initializer initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final PersistentSet set = getCollectionInstance( data ); + final var rowProcessingState = data.getRowProcessingState(); + final var set = getCollectionInstance( data ); assert set != null; for ( Object element : set ) { initializer.initializeInstanceFromParent( element, rowProcessingState ); @@ -93,7 +93,7 @@ protected void initializeSubInstancesFromParent(ImmediateCollectionInitializerDa @Override protected void resolveInstanceSubInitializers(ImmediateCollectionInitializerData data) { - final Initializer initializer = elementAssembler.getInitializer(); + final var initializer = elementAssembler.getInitializer(); if ( initializer != null ) { initializer.resolveKey( data.getRowProcessingState() ); } @@ -111,6 +111,6 @@ public DomainResultAssembler getElementAssembler() { @Override public String toString() { - return "SetInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "SetInitializer(" + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/AggregateEmbeddableInitializerImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/AggregateEmbeddableInitializerImpl.java index 6681381a9304..0ac3e986e1cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/AggregateEmbeddableInitializerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/AggregateEmbeddableInitializerImpl.java @@ -27,7 +27,7 @@ public AggregateEmbeddableInitializerImpl( AssemblerCreationState creationState, boolean isResultInitializer) { super( resultDescriptor, discriminatorFetch, null, parent, creationState, isResultInitializer ); - this.aggregateValuesArrayPositions = resultDescriptor.getAggregateValuesArrayPositions(); + aggregateValuesArrayPositions = resultDescriptor.getAggregateValuesArrayPositions(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableInitializerImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableInitializerImpl.java index 35fd930e83ee..358a22afe591 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableInitializerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableInitializerImpl.java @@ -5,11 +5,9 @@ package org.hibernate.sql.results.graph.embeddable.internal; import java.util.Arrays; -import java.util.Collection; import java.util.function.BiConsumer; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; -import org.hibernate.engine.internal.ManagedTypeHelper; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; @@ -21,9 +19,6 @@ import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultAssembler; -import org.hibernate.sql.results.graph.Fetch; -import org.hibernate.sql.results.graph.FetchParent; -import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.InitializerData; import org.hibernate.sql.results.graph.InitializerParent; @@ -38,13 +33,15 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptableType; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; import static org.hibernate.sql.results.graph.entity.internal.BatchEntityInsideEmbeddableSelectFetchInitializer.BATCH_PROPERTY; /** * @author Steve Ebersole */ -public class EmbeddableInitializerImpl extends AbstractInitializer +public class EmbeddableInitializerImpl + extends AbstractInitializer implements EmbeddableInitializer { private final NavigablePath navigablePath; @@ -70,9 +67,8 @@ public static class EmbeddableInitializerData extends InitializerData implements public EmbeddableInitializerData(EmbeddableInitializerImpl initializer, RowProcessingState rowProcessingState) { super( rowProcessingState ); - this.parentData = initializer.parent == null ? null : initializer.parent.getData( rowProcessingState ); - final int size = initializer.embeddableMappingType.getNumberOfFetchables(); - this.rowState = new Object[ size ]; + parentData = initializer.parent == null ? null : initializer.parent.getData( rowProcessingState ); + rowState = new Object[ initializer.embeddableMappingType.getNumberOfFetchables() ]; } @Override @@ -103,25 +99,24 @@ public EmbeddableInitializerImpl( AssemblerCreationState creationState, boolean isResultInitializer) { super( creationState ); - this.navigablePath = resultDescriptor.getNavigablePath(); - this.embedded = resultDescriptor.getReferencedMappingContainer(); this.parent = (InitializerParent) parent; this.isResultInitializer = isResultInitializer; - this.embeddableMappingType = embedded.getEmbeddableTypeDescriptor(); + navigablePath = resultDescriptor.getNavigablePath(); + embedded = resultDescriptor.getReferencedMappingContainer(); + embeddableMappingType = embedded.getEmbeddableTypeDescriptor(); + isPartOfKey = embedded.isEntityIdentifierMapping() || Initializer.isPartOfKey( navigablePath, parent ); - this.isPartOfKey = embedded.isEntityIdentifierMapping() || Initializer.isPartOfKey( navigablePath, parent ); // We never want to create empty composites for the FK target or PK, otherwise collections would break - final Collection concreteEmbeddableTypes = embeddableMappingType.getConcreteEmbeddableTypes(); - final DomainResultAssembler[][] assemblers = new DomainResultAssembler[concreteEmbeddableTypes.isEmpty() ? 1 : concreteEmbeddableTypes.size()][]; + final var concreteEmbeddableTypes = embeddableMappingType.getConcreteEmbeddableTypes(); + final var assemblers = new DomainResultAssembler[concreteEmbeddableTypes.isEmpty() ? 1 : concreteEmbeddableTypes.size()][]; final @Nullable Initializer[][] subInitializers = new Initializer[assemblers.length][]; final @Nullable Initializer[][] eagerSubInitializers = new Initializer[subInitializers.length][]; final @Nullable Initializer[][] collectionContainingSubInitializers = new Initializer[subInitializers.length][]; + fill( subInitializers ); + fill( eagerSubInitializers ); + fill( collectionContainingSubInitializers ); final int numberOfFetchables = embeddableMappingType.getNumberOfFetchables(); - - Arrays.fill( subInitializers, Initializer.EMPTY_ARRAY ); - Arrays.fill( eagerSubInitializers, Initializer.EMPTY_ARRAY ); - Arrays.fill( collectionContainingSubInitializers, Initializer.EMPTY_ARRAY ); for (int i = 0; i < assemblers.length; i++ ) { assemblers[i] = new DomainResultAssembler[numberOfFetchables]; } @@ -129,18 +124,18 @@ public EmbeddableInitializerImpl( boolean lazyCapable = false; boolean hasLazySubInitializers = false; for ( int stateArrayPosition = 0; stateArrayPosition < numberOfFetchables; stateArrayPosition++ ) { - final Fetchable stateArrayContributor = embeddableMappingType.getFetchable( stateArrayPosition ); - final Fetch fetch = resultDescriptor.findFetch( stateArrayContributor ); - - final DomainResultAssembler stateAssembler = fetch == null - ? new NullValueAssembler<>( stateArrayContributor.getJavaType() ) - : fetch.createAssembler( this, creationState ); + final var stateArrayContributor = embeddableMappingType.getFetchable( stateArrayPosition ); + final var fetch = resultDescriptor.findFetch( stateArrayContributor ); + final var stateAssembler = + fetch == null + ? new NullValueAssembler<>( stateArrayContributor.getJavaType() ) + : fetch.createAssembler( this, creationState ); if ( concreteEmbeddableTypes.isEmpty() ) { assemblers[0][stateArrayPosition] = stateAssembler; } else { - for ( EmbeddableMappingType.ConcreteEmbeddableType concreteEmbeddableType : concreteEmbeddableTypes ) { + for ( var concreteEmbeddableType : concreteEmbeddableTypes ) { if ( concreteEmbeddableType.declaresAttribute( stateArrayPosition ) ) { assemblers[concreteEmbeddableType.getSubclassId()][stateArrayPosition] = stateAssembler; } @@ -148,7 +143,7 @@ public EmbeddableInitializerImpl( } //noinspection unchecked - final Initializer subInitializer = (Initializer) stateAssembler.getInitializer(); + final var subInitializer = (Initializer) stateAssembler.getInitializer(); if ( subInitializer != null ) { for (int subclassId = 0; subclassId < assemblers.length; subclassId++ ) { if ( subInitializers[subclassId] == Initializer.EMPTY_ARRAY ) { @@ -164,8 +159,8 @@ public EmbeddableInitializerImpl( hasLazySubInitializers = hasLazySubInitializers || subInitializer.hasLazySubInitializers(); assert fetch != null; - final FetchParent fetchParent; - if ( ( fetchParent = fetch.asFetchParent() ) != null && fetchParent.containsCollectionFetches() + final var fetchParent = fetch.asFetchParent(); + if ( fetchParent != null && fetchParent.containsCollectionFetches() || subInitializer.isCollectionInitializer() ) { if ( collectionContainingSubInitializers[subclassId] == Initializer.EMPTY_ARRAY ) { collectionContainingSubInitializers[subclassId] = new Initializer[numberOfFetchables]; @@ -181,22 +176,29 @@ public EmbeddableInitializerImpl( } } this.assemblers = assemblers; - this.discriminatorAssembler = discriminatorFetch != null - ? (BasicResultAssembler) discriminatorFetch.createAssembler( this, creationState ) - : null; + this.discriminatorAssembler = + discriminatorFetch == null + ? null + : (BasicResultAssembler) + discriminatorFetch.createAssembler( this, creationState ); this.nullIndicatorAssembler = nullIndicatorResult == null ? null : nullIndicatorResult.createResultAssembler( this, creationState ); this.subInitializers = subInitializers; - this.subInitializersForResolveFromInitialized = isEnhancedForLazyLoading( embeddableMappingType ) - ? subInitializers - : eagerSubInitializers; + this.subInitializersForResolveFromInitialized = + isEnhancedForLazyLoading( embeddableMappingType ) + ? subInitializers + : eagerSubInitializers; this.collectionContainingSubInitializers = collectionContainingSubInitializers; this.lazyCapable = lazyCapable; this.hasLazySubInitializer = hasLazySubInitializers; } + private static void fill(@Nullable Initializer[][] initializers) { + Arrays.fill( initializers, Initializer.EMPTY_ARRAY ); + } + private static boolean isEnhancedForLazyLoading(EmbeddableMappingType embeddableMappingType) { - return ManagedTypeHelper.isPersistentAttributeInterceptableType( + return isPersistentAttributeInterceptableType( embeddableMappingType.getRepresentationStrategy().getMappedJavaType().getJavaTypeClass() ); } @@ -249,55 +251,58 @@ protected InitializerData createInitializerData(RowProcessingState rowProcessing @Override public void resolveKey(EmbeddableInitializerData data) { - if ( data.getState() != State.UNINITIALIZED ) { - return; - } - data.setInstance( null ); - if ( discriminatorAssembler != null ) { - assert embeddableMappingType.getDiscriminatorMapping() != null; - // todo: add more info into EmbeddableDiscriminatorConverter to extract this details object directly - final Object discriminatorValue = discriminatorAssembler.extractRawValue( data.getRowProcessingState() ); - data.concreteEmbeddableType = discriminatorValue == null - ? null - : embeddableMappingType.findSubtypeByDiscriminator( discriminatorValue ); - } - if ( isPartOfKey ) { - data.setState( State.KEY_RESOLVED ); - if ( subInitializers[data.getSubclassId()].length == 0 ) { - // Resolve the component early to know if the key is missing or not - resolveInstance( data ); + if ( data.getState() == State.UNINITIALIZED ) { + data.setInstance( null ); + if ( discriminatorAssembler != null ) { + assert embeddableMappingType.getDiscriminatorMapping() != null; + // todo: add more info into EmbeddableDiscriminatorConverter to extract this details object directly + final Object discriminatorValue = + discriminatorAssembler.extractRawValue( data.getRowProcessingState() ); + data.concreteEmbeddableType = + discriminatorValue == null + ? null + : embeddableMappingType.findSubtypeByDiscriminator( discriminatorValue ); + } + if ( isPartOfKey ) { + data.setState( State.KEY_RESOLVED ); + if ( subInitializers[data.getSubclassId()].length == 0 ) { + // Resolve the component early to know if the key is missing or not + resolveInstance( data ); + } + else { + resolveKeySubInitializers( data ); + } } else { - resolveKeySubInitializers( data ); + super.resolveKey( data ); } } - else { - super.resolveKey( data ); - } } @Override public void resetResolvedEntityRegistrations(RowProcessingState rowProcessingState) { - final EmbeddableInitializerData data = getData( rowProcessingState ); - for ( Initializer initializer : subInitializers[data.getSubclassId()] ) { + final var data = getData( rowProcessingState ); + for ( var initializer : subInitializers[data.getSubclassId()] ) { if ( initializer != null ) { - final EntityInitializer entityInitializer = initializer.asEntityInitializer(); - final EmbeddableInitializer embeddableInitializer; + final var entityInitializer = initializer.asEntityInitializer(); if ( entityInitializer != null ) { entityInitializer.resetResolvedEntityRegistrations( rowProcessingState ); } - else if ( ( embeddableInitializer = initializer.asEmbeddableInitializer() ) != null ) { - embeddableInitializer.resetResolvedEntityRegistrations( rowProcessingState ); + else { + final var embeddableInitializer = initializer.asEmbeddableInitializer(); + if ( embeddableInitializer != null ) { + embeddableInitializer.resetResolvedEntityRegistrations( rowProcessingState ); + } } } } } private void resolveKeySubInitializers(EmbeddableInitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - for ( Initializer initializer : subInitializers[data.getSubclassId()] ) { + final var rowProcessingState = data.getRowProcessingState(); + for ( var initializer : subInitializers[data.getSubclassId()] ) { if ( initializer != null ) { - final InitializerData subData = initializer.getData( rowProcessingState ); + final var subData = initializer.getData( rowProcessingState ); initializer.resolveKey( subData ); if ( subData.getState() == State.MISSING ) { data.setState( State.MISSING ); @@ -314,9 +319,9 @@ public void resolveFromPreviousRow(EmbeddableInitializerData data) { data.setState( State.MISSING ); } else { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); // When a previous row initialized this entity already, we only need to process collections - for ( Initializer initializer : collectionContainingSubInitializers[data.getSubclassId()] ) { + for ( var initializer : collectionContainingSubInitializers[data.getSubclassId()] ) { if ( initializer != null ) { initializer.resolveFromPreviousRow( rowProcessingState ); } @@ -328,13 +333,11 @@ public void resolveFromPreviousRow(EmbeddableInitializerData data) { @Override public void resolveInstance(EmbeddableInitializerData data) { - if ( data.getState() != State.KEY_RESOLVED ) { - return; + if ( data.getState() == State.KEY_RESOLVED ) { + data.setState( State.RESOLVED ); + extractRowState( data ); + prepareCompositeInstance( data ); } - - data.setState( State.RESOLVED ); - extractRowState( data ); - prepareCompositeInstance( data ); } @Override @@ -347,10 +350,10 @@ public void resolveInstance(@Nullable Object instance, EmbeddableInitializerData data.setState( State.INITIALIZED ); data.setInstance( instance ); final int subclassId = data.getSubclassId(); - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); resolveInstanceSubInitializers( subclassId, instance, rowProcessingState ); if ( rowProcessingState.needsResolveState() ) { - for ( DomainResultAssembler assembler : assemblers[subclassId] ) { + for ( var assembler : assemblers[subclassId] ) { assembler.resolveState( rowProcessingState ); } } @@ -376,37 +379,37 @@ private void resolveInstanceSubInitializers(int subclassId, Object instance, Row @Override public void initializeInstance(EmbeddableInitializerData data) { - if ( data.getState() != State.RESOLVED ) { - return; - } - data.setState( State.INITIALIZED ); + if ( data.getState() == State.RESOLVED ) { + data.setState( State.INITIALIZED ); - if ( embedded.getParentInjectionAttributePropertyAccess() != null || embedded instanceof VirtualModelPart ) { - handleParentInjection( data ); + if ( embedded.getParentInjectionAttributePropertyAccess() != null + || embedded instanceof VirtualModelPart ) { + handleParentInjection( data ); - final var lazyInitializer = extractLazyInitializer( data.getInstance() ); - // If the composite instance has a lazy initializer attached, this means that the embeddable is actually virtual - // and the compositeInstance == entity, so we have to inject the row state into the entity when it finishes resolution - if ( lazyInitializer != null ) { - if ( parent != null ) { - embeddableMappingType.setValues( lazyInitializer.getImplementation(), data.rowState ); + final var lazyInitializer = extractLazyInitializer( data.getInstance() ); + // If the composite instance has a lazy initializer attached, this means that the embeddable is actually virtual + // and the compositeInstance == entity, so we have to inject the row state into the entity when it finishes resolution + if ( lazyInitializer != null ) { + if ( parent != null ) { + embeddableMappingType.setValues( lazyInitializer.getImplementation(), data.rowState ); + } + else { + // At this point, createEmptyCompositesEnabled is always true, so we generate + // the composite instance. + // + // NOTE: `valuesAccess` is set to null to indicate that all values are null, + // as opposed to returning the all-null value array. the instantiator + // interprets that as the values are not known or were all null. + final Object target = + embeddableMappingType.getRepresentationStrategy().getInstantiator() + .instantiate( data ); + lazyInitializer.setImplementation( target ); + } } else { - // At this point, createEmptyCompositesEnabled is always true, so we generate - // the composite instance. - // - // NOTE: `valuesAccess` is set to null to indicate that all values are null, - // as opposed to returning the all-null value array. the instantiator - // interprets that as the values are not known or were all null. - final Object target = - embeddableMappingType.getRepresentationStrategy().getInstantiator() - .instantiate( data ); - lazyInitializer.setImplementation( target ); + embeddableMappingType.setValues( data.getInstance(), data.rowState ); } } - else { - embeddableMappingType.setValues( data.getInstance(), data.rowState ); - } } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/NonAggregatedIdentifierMappingInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/NonAggregatedIdentifierMappingInitializer.java index 70c57c653ca6..766ea04b8168 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/NonAggregatedIdentifierMappingInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/NonAggregatedIdentifierMappingInitializer.java @@ -20,8 +20,6 @@ import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.Fetch; -import org.hibernate.sql.results.graph.FetchParent; -import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.InitializerData; import org.hibernate.sql.results.graph.InitializerParent; @@ -68,15 +66,21 @@ public NonAggregatedIdentifierMappingInitializerData( NonAggregatedIdentifierMappingInitializer initializer, RowProcessingState rowProcessingState) { super( rowProcessingState ); - isFindByIdLookup = - !initializer.hasIdClass && rowProcessingState.getEntityId() != null - && initializer.navigablePath.getParent().getParent() == null - && initializer.navigablePath instanceof EntityIdentifierNavigablePath; + isFindByIdLookup = isIsFindByIdLookup( initializer, rowProcessingState ); parentData = initializer.parent == null ? null : initializer.parent.getData( rowProcessingState ); final var virtualIdEmbeddable = initializer.embedded.getEmbeddableTypeDescriptor(); final int size = virtualIdEmbeddable.getNumberOfFetchables(); - virtualIdState = new Object[ size ]; - idClassState = new Object[ size ]; + virtualIdState = new Object[size]; + idClassState = new Object[size]; + } + + private boolean isIsFindByIdLookup( + NonAggregatedIdentifierMappingInitializer initializer, + RowProcessingState rowProcessingState) { + return !initializer.hasIdClass + && rowProcessingState.getEntityId() != null + && initializer.navigablePath.getParent().getParent() == null + && initializer.navigablePath instanceof EntityIdentifierNavigablePath; } @Override @@ -112,23 +116,24 @@ protected NonAggregatedIdentifierMappingInitializer( boolean isResultInitializer, Function fetchConverter) { super( creationState ); - this.navigablePath = resultDescriptor.getNavigablePath(); - this.embedded = - (NonAggregatedIdentifierMapping) - resultDescriptor.getReferencedMappingContainer(); this.parent = parent; this.isResultInitializer = isResultInitializer; - this.virtualIdEmbeddable = embedded.getEmbeddableTypeDescriptor(); - this.representationEmbeddable = embedded.getMappedIdEmbeddableTypeDescriptor(); - this.embeddableInstantiator = representationEmbeddable.getRepresentationStrategy().getInstantiator(); - this.hasIdClass = embedded.hasContainingClass() && virtualIdEmbeddable != representationEmbeddable; + navigablePath = resultDescriptor.getNavigablePath(); + embedded = + (NonAggregatedIdentifierMapping) + resultDescriptor.getReferencedMappingContainer(); + + virtualIdEmbeddable = embedded.getEmbeddableTypeDescriptor(); + representationEmbeddable = embedded.getMappedIdEmbeddableTypeDescriptor(); + embeddableInstantiator = representationEmbeddable.getRepresentationStrategy().getInstantiator(); + hasIdClass = embedded.hasContainingClass() && virtualIdEmbeddable != representationEmbeddable; final int size = virtualIdEmbeddable.getNumberOfFetchables(); final var assemblers = new DomainResultAssembler[size]; this.assemblers = assemblers; final Initializer[] initializers = new Initializer[assemblers.length]; - final Initializer[] eagerSubInitializers = new Initializer[assemblers.length]; +// final Initializer[] eagerSubInitializers = new Initializer[assemblers.length]; final Initializer[] collectionContainingSubInitializers = new Initializer[assemblers.length]; boolean empty = true; boolean emptyEager = true; @@ -136,24 +141,25 @@ protected NonAggregatedIdentifierMappingInitializer( boolean lazyCapable = false; boolean hasLazySubInitializers = false; for ( int i = 0; i < size; i++ ) { - final Fetchable stateArrayContributor = virtualIdEmbeddable.getFetchable( i ); - final Fetch fetch = fetchConverter.apply( resultDescriptor.findFetch( stateArrayContributor ) ); + final var stateArrayContributor = virtualIdEmbeddable.getFetchable( i ); + final var fetch = fetchConverter.apply( resultDescriptor.findFetch( stateArrayContributor ) ); - final var stateAssembler = fetch == null - ? new NullValueAssembler<>( stateArrayContributor.getJavaType() ) - : fetch.createAssembler( this, creationState ); + final var stateAssembler = + fetch == null + ? new NullValueAssembler<>( stateArrayContributor.getJavaType() ) + : fetch.createAssembler( this, creationState ); assemblers[i] = stateAssembler; final var initializer = stateAssembler.getInitializer(); if ( initializer != null ) { if ( initializer.isEager() ) { - eagerSubInitializers[i] = initializer; +// eagerSubInitializers[i] = initializer; hasLazySubInitializers = hasLazySubInitializers || initializer.hasLazySubInitializers(); emptyEager = false; assert fetch != null; - final FetchParent fetchParent; - if ( ( fetchParent = fetch.asFetchParent() ) != null && fetchParent.containsCollectionFetches() + final var fetchParent = fetch.asFetchParent(); + if ( fetchParent != null && fetchParent.containsCollectionFetches() || initializer.isCollectionInitializer() ) { collectionContainingSubInitializers[i] = initializer; emptyCollectionInitializers = false; @@ -303,7 +309,7 @@ public void resolveInstance(@Nullable Object instance, NonAggregatedIdentifierMa final var rowProcessingState = data.getRowProcessingState(); resolveInstanceSubInitializers( instance, rowProcessingState ); if ( rowProcessingState.needsResolveState() ) { - for ( DomainResultAssembler assembler : assemblers ) { + for ( var assembler : assemblers ) { assembler.resolveState( rowProcessingState ); } } @@ -351,7 +357,7 @@ public void initializeInstance(NonAggregatedIdentifierMappingInitializerData dat @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { final var rowProcessingState = data.getRowProcessingState(); - for ( Initializer initializer : initializers ) { + for ( var initializer : initializers ) { if ( initializer != null ) { consumer.accept( initializer, rowProcessingState ); } @@ -378,7 +384,7 @@ private void extractRowState(NonAggregatedIdentifierMappingInitializerData data) final var virtualIdAttribute = virtualIdEmbeddable.getAttributeMapping( i ); final var mappedIdAttribute = representationEmbeddable.getAttributeMapping( i ); if ( virtualIdAttribute instanceof ToOneAttributeMapping toOneAttributeMapping - && !( mappedIdAttribute instanceof ToOneAttributeMapping ) ) { + && !( mappedIdAttribute instanceof ToOneAttributeMapping ) ) { final Object associationKey = toOneAttributeMapping.getForeignKeyDescriptor() .getAssociationKeyFromSide( @@ -433,6 +439,7 @@ public boolean hasLazySubInitializers() { @Override public String toString() { - return "NonAggregatedIdentifierMappingInitializer(" + navigablePath + ") : `" + getInitializedPart().getJavaType().getJavaTypeClass() + "`"; + return "NonAggregatedIdentifierMappingInitializer(" + navigablePath + ") : `" + + getInitializedPart().getJavaType().getJavaTypeClass() + "`"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java index 4f3db569faf4..48260ae4e09d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/AbstractBatchEntitySelectFetchInitializer.java @@ -4,21 +4,14 @@ */ package org.hibernate.sql.results.graph.entity.internal; -import org.hibernate.EntityFilterException; -import org.hibernate.FetchNotFoundException; import org.hibernate.Hibernate; -import org.hibernate.annotations.NotFoundAction; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; -import org.hibernate.engine.spi.EntityHolder; import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.proxy.HibernateProxy; -import org.hibernate.proxy.LazyInitializer; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; @@ -49,9 +42,15 @@ public AbstractBatchEntitySelectFetchInitializerData( AbstractBatchEntitySelectFetchInitializer initializer, RowProcessingState rowProcessingState) { super( initializer, rowProcessingState ); + batchDisabled = isBatchDisabled( initializer, rowProcessingState ); + } - batchDisabled = rowProcessingState.isScrollResult() - || !rowProcessingState.getLoadQueryInfluencers().effectivelyBatchLoadable( initializer.toOneMapping.getEntityMappingType().getEntityPersister() ); + private static boolean isBatchDisabled( + AbstractBatchEntitySelectFetchInitializer initializer, + RowProcessingState rowProcessingState) { + return rowProcessingState.isScrollResult() + || !rowProcessingState.getLoadQueryInfluencers() + .effectivelyBatchLoadable( initializer.toOneMapping.getEntityMappingType().getEntityPersister() ); } } @@ -65,7 +64,9 @@ public AbstractBatchEntitySelectFetchInitializer( AssemblerCreationState creationState) { super( parent, toOneMapping, fetchedNavigable, concreteDescriptor, keyResult, affectedByFilter, creationState ); //noinspection unchecked - this.owningEntityInitializer = (EntityInitializer) Initializer.findOwningEntityInitializer( parent ); + owningEntityInitializer = + (EntityInitializer) + Initializer.findOwningEntityInitializer( parent ); assert owningEntityInitializer != null : "This initializer requires an owning parent entity initializer"; } @@ -73,46 +74,42 @@ public AbstractBatchEntitySelectFetchInitializer( @Override public void resolveKey(Data data) { - if ( data.getState() != State.UNINITIALIZED ) { - return; - } - - data.entityKey = null; - data.setInstance( null ); - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - //noinspection unchecked - final Initializer initializer = (Initializer) keyAssembler.getInitializer(); - if ( initializer != null ) { - final InitializerData subData = initializer.getData( rowProcessingState ); - initializer.resolveKey( subData ); - data.entityIdentifier = null; - data.setState( subData.getState() == State.MISSING ? State.MISSING : State.KEY_RESOLVED ); - } - else { - data.entityIdentifier = keyAssembler.assemble( rowProcessingState ); - data.setState( data.entityIdentifier == null ? State.MISSING : State.KEY_RESOLVED ); + if ( data.getState() == State.UNINITIALIZED ) { + data.entityKey = null; + data.setInstance( null ); + final var rowProcessingState = data.getRowProcessingState(); + //noinspection unchecked + final var initializer = (Initializer) keyAssembler.getInitializer(); + if ( initializer != null ) { + final var subData = initializer.getData( rowProcessingState ); + initializer.resolveKey( subData ); + data.entityIdentifier = null; + data.setState( subData.getState() == State.MISSING ? State.MISSING : State.KEY_RESOLVED ); + } + else { + data.entityIdentifier = keyAssembler.assemble( rowProcessingState ); + data.setState( data.entityIdentifier == null ? State.MISSING : State.KEY_RESOLVED ); + } } } @Override public void resolveInstance(Data data) { - if ( data.getState() != State.KEY_RESOLVED ) { - return; - } - - data.setState( State.RESOLVED ); - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - if ( data.entityIdentifier == null ) { - // entityIdentifier can be null if the identifier is based on an initializer - data.entityIdentifier = keyAssembler.assemble( rowProcessingState ); + if ( data.getState() == State.KEY_RESOLVED ) { + data.setState( State.RESOLVED ); + final var rowProcessingState = data.getRowProcessingState(); if ( data.entityIdentifier == null ) { - data.entityKey = null; - data.setInstance( null ); - data.setState( State.MISSING ); - return; + // entityIdentifier can be null if the identifier is based on an initializer + data.entityIdentifier = keyAssembler.assemble( rowProcessingState ); + if ( data.entityIdentifier == null ) { + data.entityKey = null; + data.setInstance( null ); + data.setState( State.MISSING ); + return; + } } + resolveInstanceFromIdentifier( data ); } - resolveInstanceFromIdentifier( data ); } protected void resolveInstanceFromIdentifier(Data data) { @@ -136,11 +133,16 @@ public void resolveInstance(Object instance, Data data) { data.setState( State.MISSING ); data.entityKey = null; data.setInstance( null ); - return; } - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + else { + resolve( instance, data ); + } + } + + private void resolve(Object instance, Data data) { + final var rowProcessingState = data.getRowProcessingState(); // Only need to extract the identifier if the identifier has a many to one - final LazyInitializer lazyInitializer = extractLazyInitializer( instance ); + final var lazyInitializer = extractLazyInitializer( instance ); data.entityKey = null; data.entityIdentifier = null; if ( lazyInitializer == null ) { @@ -157,15 +159,19 @@ && getAttributeInterceptor( instance ) data.setState( State.RESOLVED ); data.entityIdentifier = enhancementInterceptor.getIdentifier(); } + if ( keyIsEager && data.entityIdentifier == null ) { + data.entityIdentifier = + concreteDescriptor.getIdentifier( instance, + rowProcessingState.getSession() ); + } } else { - // If the entity initializer is null, we know the entity is fully initialized, - // otherwise it will be initialized by some other initializer + // If the entity initializer is null, we know the entity is fully initialized; + // otherwise, it will be initialized by some other initializer data.setState( State.RESOLVED ); - data.entityIdentifier = concreteDescriptor.getIdentifier( instance, rowProcessingState.getSession() ); - } - if ( keyIsEager && data.entityIdentifier == null ) { - data.entityIdentifier = concreteDescriptor.getIdentifier( instance, rowProcessingState.getSession() ); + data.entityIdentifier = + concreteDescriptor.getIdentifier( instance, + rowProcessingState.getSession() ); } } else if ( lazyInitializer.isUninitialized() ) { @@ -185,7 +191,7 @@ else if ( lazyInitializer.isUninitialized() ) { resolveInstanceFromIdentifier( data ); } if ( keyIsEager ) { - final Initializer initializer = keyAssembler.getInitializer(); + final var initializer = keyAssembler.getInitializer(); assert initializer != null; initializer.resolveInstance( data.entityIdentifier, rowProcessingState ); } @@ -197,21 +203,23 @@ else if ( rowProcessingState.needsResolveState() ) { @Override public void initializeInstance(Data data) { - if ( data.getState() != State.RESOLVED ) { - return; - } - data.setState( State.INITIALIZED ); - if ( data.batchDisabled ) { - Hibernate.initialize( data.getInstance() ); + if ( data.getState() == State.RESOLVED ) { + data.setState( State.INITIALIZED ); + if ( data.batchDisabled ) { + Hibernate.initialize( data.getInstance() ); + } } } protected Object getExistingInitializedInstance(Data data) { - final SharedSessionContractImplementor session = data.getRowProcessingState().getSession(); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - final EntityHolder holder = persistenceContext.getEntityHolder( data.entityKey ); - if ( holder != null && holder.getEntity() != null && holder.isEventuallyInitialized() ) { - return holder.getEntity(); + final var session = data.getRowProcessingState().getSession(); + final var persistenceContext = session.getPersistenceContextInternal(); + final var holder = persistenceContext.getEntityHolder( data.entityKey ); + if ( holder != null ) { + final Object entity = holder.getEntity(); + if ( entity != null && holder.isEventuallyInitialized() ) { + return entity; + } } // we need to register a resolution listener only if there is not an already initialized instance // or an instance that another initializer is loading @@ -227,10 +235,11 @@ protected void registerToBatchFetchQueue(Data data) { @Override public void initializeInstanceFromParent(Object parentInstance, Data data) { - final AttributeMapping attributeMapping = getInitializedPart().asAttributeMapping(); - final Object instance = attributeMapping != null - ? attributeMapping.getValue( parentInstance ) - : parentInstance; + final var attributeMapping = getInitializedPart().asAttributeMapping(); + final Object instance = + attributeMapping != null + ? attributeMapping.getValue( parentInstance ) + : parentInstance; // No need to initialize these fields data.entityKey = null; data.setInstance( null ); @@ -238,7 +247,7 @@ public void initializeInstanceFromParent(Object parentInstance, Data data) { data.setState( State.MISSING ); } else { - final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( instance ); + final var lazyInitializer = extractLazyInitializer( instance ); if ( lazyInitializer != null && lazyInitializer.isUninitialized() ) { data.entityKey = new EntityKey( lazyInitializer.getInternalIdentifier(), concreteDescriptor ); registerToBatchFetchQueue( data ); @@ -252,47 +261,28 @@ protected static Object loadInstance( ToOneAttributeMapping toOneMapping, boolean affectedByFilter, SharedSessionContractImplementor session) { - final Object instance = session.internalLoad( - entityKey.getEntityName(), - entityKey.getIdentifier(), - true, - toOneMapping.isInternalLoadNullable() - ); + final String entityName = entityKey.getEntityName(); + final Object identifier = entityKey.getIdentifier(); + final Object instance = + session.internalLoad( entityName, identifier, true, + toOneMapping.isInternalLoadNullable() ); if ( instance == null ) { - if ( toOneMapping.getNotFoundAction() != NotFoundAction.IGNORE ) { - if ( affectedByFilter ) { - throw new EntityFilterException( - entityKey.getEntityName(), - entityKey.getIdentifier(), - toOneMapping.getNavigableRole().getFullPath() - ); - } - if ( toOneMapping.getNotFoundAction() == NotFoundAction.EXCEPTION ) { - throw new FetchNotFoundException( entityKey.getEntityName(), entityKey.getIdentifier() ); - } - } + checkNotFound( toOneMapping, affectedByFilter, entityName, identifier ); } return instance; } protected AttributeMapping[] getParentEntityAttributes(String attributeName) { - final EntityPersister entityDescriptor = owningEntityInitializer.getEntityDescriptor(); - final AttributeMapping[] parentEntityAttributes = new AttributeMapping[ + final var entityDescriptor = owningEntityInitializer.getEntityDescriptor(); + final int size = entityDescriptor.getRootEntityDescriptor() - .getSubclassEntityNames() - .size() - ]; - parentEntityAttributes[entityDescriptor.getSubclassId()] = getParentEntityAttribute( - entityDescriptor, - toOneMapping, - attributeName - ); + .getSubclassEntityNames().size(); + final var parentEntityAttributes = new AttributeMapping[size]; + parentEntityAttributes[ entityDescriptor.getSubclassId() ] = + getParentEntityAttribute( entityDescriptor, toOneMapping, attributeName ); for ( EntityMappingType subMappingType : entityDescriptor.getSubMappingTypes() ) { - parentEntityAttributes[subMappingType.getSubclassId()] = getParentEntityAttribute( - subMappingType, - toOneMapping, - attributeName - ); + parentEntityAttributes[ subMappingType.getSubclassId() ] = + getParentEntityAttribute( subMappingType, toOneMapping, attributeName ); } return parentEntityAttributes; } @@ -301,14 +291,14 @@ protected static AttributeMapping getParentEntityAttribute( EntityMappingType subMappingType, ToOneAttributeMapping referencedModelPart, String attributeName) { - final AttributeMapping parentAttribute = subMappingType.findAttributeMapping( attributeName ); - if ( parentAttribute != null && parentAttribute.getDeclaringType() == referencedModelPart.getDeclaringType() - .findContainingEntityMapping() ) { - // These checks are needed to avoid setting the instance using the wrong (child's) model part or - // setting it multiple times in case parent and child share the same attribute name for the association. - return parentAttribute; - } - return null; + final var parentAttribute = subMappingType.findAttributeMapping( attributeName ); + // These checks are needed to avoid setting the instance using the wrong (child's) model part or + // setting it multiple times in case parent and child share the same attribute name for the association. + return parentAttribute != null + && parentAttribute.getDeclaringType() + == referencedModelPart.getDeclaringType().findContainingEntityMapping() + ? parentAttribute + : null; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntityInsideEmbeddableSelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntityInsideEmbeddableSelectFetchInitializer.java index 773651c50370..3752e3ce4b35 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntityInsideEmbeddableSelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntityInsideEmbeddableSelectFetchInitializer.java @@ -4,20 +4,14 @@ */ package org.hibernate.sql.results.graph.entity.internal; +import java.io.Serial; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.AttributeMapping; -import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.property.access.spi.Getter; @@ -31,6 +25,8 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.type.Type; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer { protected final Setter referencedModelPartSetter; protected final AttributeMapping[] rootEmbeddableAttributes; @@ -46,7 +42,7 @@ public class BatchEntityInsideEmbeddableSelectFetchInitializer extends AbstractB public String toString() { return ""; } - + @Serial public Object readResolve() { return BATCH_PROPERTY; } @@ -72,21 +68,18 @@ public BatchEntityInsideEmbeddableSelectFetchInitializer( AssemblerCreationState creationState) { super( parentAccess, referencedModelPart, fetchedNavigable, concreteDescriptor, keyResult, affectedByFilter, creationState ); - this.referencedModelPartSetter = referencedModelPart.getAttributeMetadata().getPropertyAccess().getSetter(); - final String rootEmbeddablePropertyName = getRootEmbeddablePropertyName( - owningEntityInitializer, - parentAccess, - referencedModelPart - ); - this.rootEmbeddableAttributes = getParentEntityAttributes( rootEmbeddablePropertyName ); - final Getter[] getters = new Getter[rootEmbeddableAttributes.length]; + referencedModelPartSetter = referencedModelPart.getAttributeMetadata().getPropertyAccess().getSetter(); + final String rootEmbeddablePropertyName = + getRootEmbeddablePropertyName( owningEntityInitializer, parentAccess, referencedModelPart ); + rootEmbeddableAttributes = getParentEntityAttributes( rootEmbeddablePropertyName ); + final var getters = new Getter[rootEmbeddableAttributes.length]; for ( int i = 0; i < rootEmbeddableAttributes.length; i++ ) { if ( rootEmbeddableAttributes[i] != null ) { getters[i] = rootEmbeddableAttributes[i].getAttributeMetadata().getPropertyAccess().getGetter(); } } - this.rootEmbeddableGetters = getters; - this.rootEmbeddablePropertyTypes = getParentEntityAttributeTypes( rootEmbeddablePropertyName ); + rootEmbeddableGetters = getters; + rootEmbeddablePropertyTypes = getParentEntityAttributeTypes( rootEmbeddablePropertyName ); } @Override @@ -95,22 +88,22 @@ protected InitializerData createInitializerData(RowProcessingState rowProcessing } protected Type[] getParentEntityAttributeTypes(String attributeName) { - final EntityPersister entityDescriptor = owningEntityInitializer.getEntityDescriptor(); - final Type[] attributeTypes = new Type[ + final var entityDescriptor = owningEntityInitializer.getEntityDescriptor(); + final int size = entityDescriptor.getRootEntityDescriptor() - .getSubclassEntityNames() - .size() - ]; + .getSubclassEntityNames().size(); + final var attributeTypes = new Type[size]; initializeAttributeType( attributeTypes, entityDescriptor, attributeName ); - for ( EntityMappingType subMappingType : entityDescriptor.getSubMappingTypes() ) { + for ( var subMappingType : entityDescriptor.getSubMappingTypes() ) { initializeAttributeType( attributeTypes, subMappingType.getEntityPersister(), attributeName ); } return attributeTypes; } protected void initializeAttributeType(Type[] attributeTypes, EntityPersister entityDescriptor, String attributeName) { - if ( rootEmbeddableAttributes[entityDescriptor.getSubclassId()] != null ) { - attributeTypes[entityDescriptor.getSubclassId()] = entityDescriptor.getPropertyType( attributeName ); + final int subclassId = entityDescriptor.getSubclassId(); + if ( rootEmbeddableAttributes[subclassId] != null ) { + attributeTypes[subclassId] = entityDescriptor.getPropertyType( attributeName ); } } @@ -125,23 +118,24 @@ public void initializeInstance(BatchEntityInsideEmbeddableSelectFetchInitializer super.initializeInstance( data ); // todo: check why this can't be moved to #registerToBatchFetchQueue if ( data.getInstance() == BATCH_PROPERTY ) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final InitializerData owningData = owningEntityInitializer.getData( rowProcessingState ); - final int owningEntitySubclassId = owningEntityInitializer.getConcreteDescriptor( owningData ).getSubclassId(); - final AttributeMapping rootEmbeddableAttribute = rootEmbeddableAttributes[owningEntitySubclassId]; + final var rowProcessingState = data.getRowProcessingState(); + final var owningData = owningEntityInitializer.getData( rowProcessingState ); + final int owningEntitySubclassId = + owningEntityInitializer.getConcreteDescriptor( owningData ) + .getSubclassId(); + final var rootEmbeddableAttribute = rootEmbeddableAttributes[owningEntitySubclassId]; if ( rootEmbeddableAttribute != null ) { - HashMap> toBatchLoad = data.toBatchLoad; + var toBatchLoad = data.toBatchLoad; if ( toBatchLoad == null ) { toBatchLoad = data.toBatchLoad = new HashMap<>(); } - toBatchLoad.computeIfAbsent( data.entityKey, key -> new ArrayList<>() ).add( - new ParentInfo( + toBatchLoad.computeIfAbsent( data.entityKey, key -> new ArrayList<>() ) + .add( new ParentInfo( owningEntityInitializer.getTargetInstance( owningData ), parent.getResolvedInstance( rowProcessingState ), rootEmbeddableAttribute.getStateArrayPosition(), owningEntitySubclassId - ) - ); + ) ); } } } @@ -171,20 +165,20 @@ public ParentInfo( @Override public void endLoading(BatchEntityInsideEmbeddableSelectFetchInitializerData data) { super.endLoading( data ); - final HashMap> toBatchLoad = data.toBatchLoad; + final var toBatchLoad = data.toBatchLoad; if ( toBatchLoad != null ) { - for ( Map.Entry> entry : toBatchLoad.entrySet() ) { - final EntityKey entityKey = entry.getKey(); - final List parentInfos = entry.getValue(); - final SharedSessionContractImplementor session = data.getRowProcessingState().getSession(); - final SessionFactoryImplementor factory = session.getFactory(); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); + for ( var entry : toBatchLoad.entrySet() ) { + final var entityKey = entry.getKey(); + final var parentInfos = entry.getValue(); + final var session = data.getRowProcessingState().getSession(); + final var factory = session.getFactory(); + final var persistenceContext = session.getPersistenceContextInternal(); final Object loadedInstance = loadInstance( entityKey, toOneMapping, affectedByFilter, session ); for ( ParentInfo parentInfo : parentInfos ) { final Object parentEntityInstance = parentInfo.parentEntityInstance; - final EntityEntry parentEntityEntry = persistenceContext.getEntry( parentEntityInstance ); + final var parentEntityEntry = persistenceContext.getEntry( parentEntityInstance ); referencedModelPartSetter.set( parentInfo.parentInstance, loadedInstance ); - final Object[] loadedState = parentEntityEntry.getLoadedState(); + final var loadedState = parentEntityEntry.getLoadedState(); if ( loadedState != null ) { /* E.g. @@ -194,11 +188,12 @@ public void endLoading(BatchEntityInsideEmbeddableSelectFetchInitializerData dat The value of RootEmbeddable is needed to update the ParentEntity loaded state */ final int parentEntitySubclassId = parentInfo.parentEntitySubclassId; - final Object rootEmbeddable = rootEmbeddableGetters[parentEntitySubclassId].get( parentEntityInstance ); - loadedState[parentInfo.propertyIndex] = rootEmbeddablePropertyTypes[parentEntitySubclassId].deepCopy( - rootEmbeddable, - factory - ); + final Object rootEmbeddable = + rootEmbeddableGetters[parentEntitySubclassId] + .get( parentEntityInstance ); + loadedState[parentInfo.propertyIndex] = + rootEmbeddablePropertyTypes[parentEntitySubclassId] + .deepCopy( rootEmbeddable, factory ); } } } @@ -223,7 +218,8 @@ protected static String getRootEmbeddablePropertyName( @Override public String toString() { - return "BatchEntityInsideEmbeddableSelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "BatchEntityInsideEmbeddableSelectFetchInitializer(" + + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java index 12e5a2e557ff..610c5dcf21ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchEntitySelectFetchInitializer.java @@ -7,13 +7,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Map; -import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; @@ -26,6 +21,8 @@ import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.type.Type; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + public class BatchEntitySelectFetchInitializer extends AbstractBatchEntitySelectFetchInitializer { protected final AttributeMapping[] parentAttributes; protected final Setter referencedModelPartSetter; @@ -50,10 +47,11 @@ public BatchEntitySelectFetchInitializer( boolean affectedByFilter, AssemblerCreationState creationState) { super( parentAccess, referencedModelPart, fetchedNavigable, concreteDescriptor, keyResult, affectedByFilter, creationState ); - this.parentAttributes = getParentEntityAttributes( referencedModelPart.getAttributeName() ); - this.referencedModelPartSetter = referencedModelPart.getPropertyAccess().getSetter(); - this.referencedModelPartType = referencedModelPart.findContainingEntityMapping().getEntityPersister() - .getPropertyType( referencedModelPart.getAttributeName() ); + parentAttributes = getParentEntityAttributes( referencedModelPart.getAttributeName() ); + referencedModelPartSetter = referencedModelPart.getPropertyAccess().getSetter(); + referencedModelPartType = + referencedModelPart.findContainingEntityMapping().getEntityPersister() + .getPropertyType( referencedModelPart.getAttributeName() ); } @Override @@ -63,23 +61,25 @@ protected InitializerData createInitializerData(RowProcessingState rowProcessing @Override protected void registerResolutionListener(BatchEntitySelectFetchInitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final InitializerData owningData = owningEntityInitializer.getData( rowProcessingState );HashMap> toBatchLoad = data.toBatchLoad; + final var rowProcessingState = data.getRowProcessingState(); + final var owningData = owningEntityInitializer.getData( rowProcessingState ); + var toBatchLoad = data.toBatchLoad; if ( toBatchLoad == null ) { toBatchLoad = data.toBatchLoad = new HashMap<>(); } // Always register the entity key for resolution - final List parentInfos = toBatchLoad.computeIfAbsent( data.entityKey, key -> new ArrayList<>() ); - final AttributeMapping parentAttribute; + final var parentInfos = toBatchLoad.computeIfAbsent( data.entityKey, key -> new ArrayList<>() ); // But only add the parent info if the parent entity is not already initialized - if ( owningData.getState() != State.INITIALIZED - && ( parentAttribute = parentAttributes[owningEntityInitializer.getConcreteDescriptor( owningData ).getSubclassId()] ) != null ) { - parentInfos.add( - new ParentInfo( - owningEntityInitializer.getTargetInstance( owningData ), - parentAttribute.getStateArrayPosition() - ) - ); + if ( owningData.getState() != State.INITIALIZED ) { + final var parentAttribute = + parentAttributes[owningEntityInitializer.getConcreteDescriptor( owningData ) + .getSubclassId()]; + if ( parentAttribute != null ) { + parentInfos.add( new ParentInfo( + owningEntityInitializer.getTargetInstance( owningData ), + parentAttribute.getStateArrayPosition() + ) ); + } } } @@ -96,24 +96,23 @@ public ParentInfo(Object parentInstance, int propertyIndex) { @Override public void endLoading(BatchEntitySelectFetchInitializerData data) { super.endLoading( data ); - final HashMap> toBatchLoad = data.toBatchLoad; + final var toBatchLoad = data.toBatchLoad; if ( toBatchLoad != null ) { - final SharedSessionContractImplementor session = data.getRowProcessingState().getSession(); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - for ( Map.Entry> entry : toBatchLoad.entrySet() ) { - final EntityKey entityKey = entry.getKey(); - final List parentInfos = entry.getValue(); + final var session = data.getRowProcessingState().getSession(); + final var factory = session.getFactory(); + final var persistenceContext = session.getPersistenceContextInternal(); + for ( var entry : toBatchLoad.entrySet() ) { + final var entityKey = entry.getKey(); + final var parentInfos = entry.getValue(); final Object instance = loadInstance( entityKey, toOneMapping, affectedByFilter, session ); - for ( ParentInfo parentInfo : parentInfos ) { + for ( var parentInfo : parentInfos ) { final Object parentInstance = parentInfo.parentInstance; - final EntityEntry entityEntry = persistenceContext.getEntry( parentInstance ); + final var entityEntry = persistenceContext.getEntry( parentInstance ); referencedModelPartSetter.set( parentInstance, instance ); - final Object[] loadedState = entityEntry.getLoadedState(); + final var loadedState = entityEntry.getLoadedState(); if ( loadedState != null ) { - loadedState[parentInfo.propertyIndex] = referencedModelPartType.deepCopy( - instance, - session.getFactory() - ); + loadedState[parentInfo.propertyIndex] = + referencedModelPartType.deepCopy( instance, factory ); } } } @@ -123,7 +122,8 @@ public void endLoading(BatchEntitySelectFetchInitializerData data) { @Override public String toString() { - return "BatchEntitySelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "BatchEntitySelectFetchInitializer(" + + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchInitializeEntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchInitializeEntitySelectFetchInitializer.java index dd8569529f25..3618bb134bce 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchInitializeEntitySelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/BatchInitializeEntitySelectFetchInitializer.java @@ -7,8 +7,6 @@ import java.util.HashSet; import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.spi.NavigablePath; @@ -18,6 +16,8 @@ import org.hibernate.sql.results.graph.InitializerParent; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + /** * Loads entities from the persistence context or creates proxies if not found there, * and initializes all proxies in a batch. @@ -59,27 +59,26 @@ protected void registerResolutionListener(BatchInitializeEntitySelectFetchInitia protected void registerToBatchFetchQueue(BatchInitializeEntitySelectFetchInitializerData data) { super.registerToBatchFetchQueue( data ); // Force creating a proxy - data.setInstance( data.getRowProcessingState().getSession().internalLoad( - data.entityKey.getEntityName(), - data.entityKey.getIdentifier(), - false, - false - ) ); - HashSet toBatchLoad = data.toBatchLoad; + final var entityKey = data.entityKey; + final Object instance = + data.getRowProcessingState().getSession() + .internalLoad( entityKey.getEntityName(), entityKey.getIdentifier(), false, false ); + data.setInstance( instance ); + var toBatchLoad = data.toBatchLoad; if ( toBatchLoad == null ) { toBatchLoad = data.toBatchLoad = new HashSet<>(); } - toBatchLoad.add( data.entityKey ); + toBatchLoad.add( entityKey ); } @Override public void endLoading(BatchInitializeEntitySelectFetchInitializerData data) { super.endLoading( data ); - final HashSet toBatchLoad = data.toBatchLoad; - if ( toBatchLoad != null ) { - final SharedSessionContractImplementor session = data.getRowProcessingState().getSession(); - for ( EntityKey key : toBatchLoad ) { - loadInstance( key, toOneMapping, affectedByFilter, session ); + final var keysToBatchLoad = data.toBatchLoad; + if ( keysToBatchLoad != null ) { + final var session = data.getRowProcessingState().getSession(); + for ( var entityKey : keysToBatchLoad ) { + loadInstance( entityKey, toOneMapping, affectedByFilter, session ); } data.toBatchLoad = null; } @@ -87,7 +86,8 @@ public void endLoading(BatchInitializeEntitySelectFetchInitializerData data) { @Override public String toString() { - return "BatchInitializeEntitySelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "BatchInitializeEntitySelectFetchInitializer(" + + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/DiscriminatedEntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/DiscriminatedEntityInitializer.java index ba21c6d7b458..33872753fa4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/DiscriminatedEntityInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/DiscriminatedEntityInitializer.java @@ -9,14 +9,9 @@ import org.hibernate.Hibernate; import org.hibernate.engine.spi.EntityHolder; import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.log.LoggingHelper; -import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.proxy.LazyInitializer; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResultAssembler; @@ -30,6 +25,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; /** @@ -73,20 +69,15 @@ public DiscriminatedEntityInitializer( this.parent = parent; this.fetchedPart = fetchedPart; this.navigablePath = fetchedNavigable; - this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parent ); - this.discriminatorValueAssembler = discriminatorFetch.createAssembler( this, creationState ); - this.keyValueAssembler = keyFetch.createAssembler( this, creationState ); this.eager = eager; this.resultInitializer = resultInitializer; - final Initializer initializer = keyValueAssembler.getInitializer(); - if ( initializer == null ) { - this.keyIsEager = false; - this.hasLazySubInitializer = false; - } - else { - this.keyIsEager = initializer.isEager(); - this.hasLazySubInitializer = !initializer.isEager() || initializer.hasLazySubInitializers(); - } + + isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parent ); + discriminatorValueAssembler = discriminatorFetch.createAssembler( this, creationState ); + keyValueAssembler = keyFetch.createAssembler( this, creationState ); + + keyIsEager = keyValueAssembler.isEager(); + hasLazySubInitializer = keyValueAssembler.hasLazySubInitializers(); } @Override @@ -110,33 +101,33 @@ public NavigablePath getNavigablePath() { @Override public void resolveKey(DiscriminatedEntityInitializerData data) { - if ( data.getState() != State.UNINITIALIZED ) { - return; - } - - // resolve the key and the discriminator, and then use those to load the indicated entity - - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState ); + if ( data.getState() == State.UNINITIALIZED ) { + // resolve the key and the discriminator, and then use those to load the indicated entity - if ( discriminatorValue == null ) { - data.setState( State.MISSING ); - data.concreteDescriptor = null; - data.entityIdentifier = null; - data.setInstance( null ); - // null association - assert keyValueAssembler.assemble( rowProcessingState ) == null; - } - else { - data.setState( State.KEY_RESOLVED ); - data.concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister(); - data.entityIdentifier = keyValueAssembler.assemble( rowProcessingState ); + final var rowProcessingState = data.getRowProcessingState(); + final Object discriminatorValue = + discriminatorValueAssembler.assemble( rowProcessingState ); + if ( discriminatorValue == null ) { + data.setState( State.MISSING ); + data.concreteDescriptor = null; + data.entityIdentifier = null; + data.setInstance( null ); + // null association + assert keyValueAssembler.assemble( rowProcessingState ) == null; + } + else { + data.setState( State.KEY_RESOLVED ); + data.concreteDescriptor = + fetchedPart.resolveDiscriminatorValue( discriminatorValue ) + .getEntityPersister(); + data.entityIdentifier = keyValueAssembler.assemble( rowProcessingState ); + } } } @Override public void resolveState(DiscriminatedEntityInitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); discriminatorValueAssembler.resolveState( rowProcessingState ); keyValueAssembler.resolveState( rowProcessingState ); } @@ -148,7 +139,7 @@ public void resolveFromPreviousRow(DiscriminatedEntityInitializerData data) { data.setState( State.MISSING ); } else { - final Initializer initializer = keyValueAssembler.getInitializer(); + final var initializer = keyValueAssembler.getInitializer(); if ( initializer != null ) { initializer.resolveFromPreviousRow( data.getRowProcessingState() ); } @@ -159,43 +150,56 @@ public void resolveFromPreviousRow(DiscriminatedEntityInitializerData data) { @Override public void resolveInstance(DiscriminatedEntityInitializerData data) { - if ( data.getState() != State.KEY_RESOLVED ) { - return; + if ( data.getState() == State.KEY_RESOLVED ) { + data.setState( State.INITIALIZED ); + final var session = data.getRowProcessingState().getSession(); + final Object identifier = data.entityIdentifier; + final var concreteDescriptor = data.concreteDescriptor; + final var entityKey = new EntityKey( identifier, concreteDescriptor ); + final var persistenceContext = session.getPersistenceContextInternal(); + final var holder = persistenceContext.getEntityHolder( entityKey ); + final Object instance; + if ( holder != null ) { + instance = holder.getEntity(); + data.setInstance( instance ); + } + else { + instance = null; + } + if ( !isResolved( holder, instance ) ) { + data.setInstance( session.internalLoad( + concreteDescriptor.getEntityName(), + identifier, + eager, + // should not be null since we checked already. null would indicate bad data (ala, not-found handling) + false + ) ); + } } + } - data.setState( State.INITIALIZED ); - - final SharedSessionContractImplementor session = data.getRowProcessingState().getSession(); - final EntityKey entityKey = new EntityKey( data.entityIdentifier, data.concreteDescriptor ); - - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - final EntityHolder holder = persistenceContext.getEntityHolder( entityKey ); - if ( holder != null ) { - final Object instance = holder.getEntity(); - data.setInstance( instance ); - if ( holder.getEntityInitializer() == null ) { - if ( instance != null && Hibernate.isInitialized( instance ) ) { - return; - } + private boolean isResolved(EntityHolder holder, Object instance) { + if ( holder == null ) { + return false; + } + else { + final var initializer = holder.getEntityInitializer(); + if ( initializer == null ) { + return instance != null && Hibernate.isInitialized( instance ); } - else if ( holder.getEntityInitializer() != this ) { + else if ( initializer != this ) { // the entity is already being loaded elsewhere - return; + return true; } else if ( instance == null ) { // todo: maybe mark this as resolved instead? assert holder.getProxy() == null : "How to handle this case?"; - return; + return true; + } + else { + return false; } } - - data.setInstance( session.internalLoad( - data.concreteDescriptor.getEntityName(), - data.entityIdentifier, - eager, - // should not be null since we checked already. null would indicate bad data (ala, not-found handling) - false - ) ); } @Override @@ -207,67 +211,80 @@ public void resolveInstance(Object instance, DiscriminatedEntityInitializerData data.setInstance( null ); } else { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final LazyInitializer lazyInitializer = extractLazyInitializer( instance ); - if ( lazyInitializer == null ) { - data.setState( State.INITIALIZED ); - if ( keyIsEager ) { - final SharedSessionContractImplementor session = rowProcessingState.getSession(); - data.concreteDescriptor = session.getEntityPersister( null, instance ); - data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session ); - } - } - else if ( lazyInitializer.isUninitialized() ) { - data.setState( eager ? State.RESOLVED : State.INITIALIZED ); - if ( keyIsEager ) { - // Read the discriminator from the result set if necessary - final Object discriminatorValue = discriminatorValueAssembler.assemble( rowProcessingState ); - data.concreteDescriptor = fetchedPart.resolveDiscriminatorValue( discriminatorValue ).getEntityPersister(); - data.entityIdentifier = lazyInitializer.getInternalIdentifier(); - } - } - else { - data.setState( State.INITIALIZED ); - if ( keyIsEager ) { - data.concreteDescriptor = rowProcessingState.getSession().getEntityPersister( null, lazyInitializer.getImplementation() ); - data.entityIdentifier = lazyInitializer.getInternalIdentifier(); - } - } - data.setInstance( instance ); + resolve( instance, data ); + final var rowProcessingState = data.getRowProcessingState(); if ( keyIsEager ) { - final Initializer initializer = keyValueAssembler.getInitializer(); + final var initializer = keyValueAssembler.getInitializer(); assert initializer != null; initializer.resolveInstance( data.entityIdentifier, rowProcessingState ); } else if ( rowProcessingState.needsResolveState() ) { - // Resolve the state of the identifier if result caching is enabled and this is not a query cache hit + // Resolve the state of the identifier if result caching is enabled, and this is not a query cache hit discriminatorValueAssembler.resolveState( rowProcessingState ); keyValueAssembler.resolveState( rowProcessingState ); } } } + private void resolve( + Object instance, + DiscriminatedEntityInitializerData data) { + final var rowProcessingState = data.getRowProcessingState(); + final var session = rowProcessingState.getSession(); + final var lazyInitializer = extractLazyInitializer( instance ); + if ( lazyInitializer == null ) { + data.setState( State.INITIALIZED ); + if ( keyIsEager ) { + data.concreteDescriptor = session.getEntityPersister( null, instance ); + data.entityIdentifier = data.concreteDescriptor.getIdentifier( instance, session ); + } + } + else if ( lazyInitializer.isUninitialized() ) { + data.setState( eager ? State.RESOLVED : State.INITIALIZED ); + if ( keyIsEager ) { + // Read the discriminator from the result set if necessary + final Object discriminatorValue = + discriminatorValueAssembler.assemble( rowProcessingState ); + data.concreteDescriptor = + fetchedPart.resolveDiscriminatorValue( discriminatorValue ) + .getEntityPersister(); + data.entityIdentifier = lazyInitializer.getInternalIdentifier(); + } + } + else { + data.setState( State.INITIALIZED ); + if ( keyIsEager ) { + data.concreteDescriptor = + session.getEntityPersister( null, lazyInitializer.getImplementation() ); + data.entityIdentifier = lazyInitializer.getInternalIdentifier(); + } + } + data.setInstance( instance ); + } + @Override public void initializeInstance(DiscriminatedEntityInitializerData data) { - if ( data.getState() != State.RESOLVED ) { - return; + if ( data.getState() == State.RESOLVED ) { + data.setState( State.INITIALIZED ); + data.setInstance( data.getRowProcessingState().getSession() + .internalLoad( + data.concreteDescriptor.getEntityName(), + data.entityIdentifier, + eager, + // should not be null since we checked already. + // null would indicate bad data (ala, not-found handling) + false + ) ); } - data.setState( State.INITIALIZED ); - data.setInstance( data.getRowProcessingState().getSession().internalLoad( - data.concreteDescriptor.getEntityName(), - data.entityIdentifier, - eager, - // should not be null since we checked already. null would indicate bad data (ala, not-found handling) - false - ) ); } @Override public void initializeInstanceFromParent(Object parentInstance, DiscriminatedEntityInitializerData data) { - final AttributeMapping attributeMapping = getInitializedPart().asAttributeMapping(); - final Object instance = attributeMapping != null - ? attributeMapping.getValue( parentInstance ) - : parentInstance; + final var attributeMapping = getInitializedPart().asAttributeMapping(); + final Object instance = + attributeMapping != null + ? attributeMapping.getValue( parentInstance ) + : parentInstance; if ( instance == null ) { data.setState( State.MISSING ); data.setInstance( null ); @@ -288,7 +305,7 @@ public void initializeInstanceFromParent(Object parentInstance, DiscriminatedEnt @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { - final Initializer initializer = keyValueAssembler.getInitializer(); + final var initializer = keyValueAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, data.getRowProcessingState() ); } @@ -331,7 +348,8 @@ public boolean isResultInitializer() { @Override public String toString() { - return "DiscriminatedEntityInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "DiscriminatedEntityInitializer(" + + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java index 92441140cc40..6dd33d918a6d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchInitializer.java @@ -9,21 +9,14 @@ import org.hibernate.FetchNotFoundException; import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor; import org.hibernate.engine.internal.ManagedTypeHelper; -import org.hibernate.engine.spi.EntityHolder; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.EntityUniqueKey; -import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistentAttributeInterceptable; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.graph.GraphSemantic; -import org.hibernate.graph.spi.AppliedGraph; -import org.hibernate.graph.spi.AttributeNodeImplementor; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.proxy.HibernateProxy; -import org.hibernate.proxy.LazyInitializer; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; @@ -41,6 +34,8 @@ import org.checkerframework.checker.nullness.qual.Nullable; import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; +import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; import static org.hibernate.sql.results.graph.entity.internal.EntityInitializerImpl.determineConcreteEntityDescriptor; /** @@ -79,26 +74,31 @@ public EntityDelayedFetchInitializer( @Nullable BasicFetch discriminatorResult, AssemblerCreationState creationState) { super( creationState ); - // associations marked with `@NotFound` are ALWAYS eagerly fetched, unless we're resolving the concrete type - assert !referencedModelPart.hasNotFoundAction() || referencedModelPart.getEntityMappingType().isConcreteProxy(); + // associations marked with '@NotFound' are ALWAYS eagerly fetched, + // unless we're resolving the concrete type + assert !referencedModelPart.hasNotFoundAction() + || referencedModelPart.getEntityMappingType().isConcreteProxy(); this.parent = parent; this.navigablePath = fetchedNavigable; - this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parent ); this.referencedModelPart = referencedModelPart; this.selectByUniqueKey = selectByUniqueKey; - this.identifierAssembler = keyResult.createResultAssembler( this, creationState ); - this.discriminatorAssembler = discriminatorResult == null - ? null - : (BasicResultAssembler) discriminatorResult.createResultAssembler( this, creationState ); - final Initializer initializer; - if ( identifierAssembler == null || ( initializer = identifierAssembler.getInitializer() ) == null ) { - this.keyIsEager = false; - this.hasLazySubInitializer = false; + + isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parent ); + identifierAssembler = keyResult.createResultAssembler( this, creationState ); + discriminatorAssembler = + discriminatorResult == null + ? null + : (BasicResultAssembler) + discriminatorResult.createResultAssembler( this, creationState ); + + if ( identifierAssembler == null ) { + keyIsEager = false; + hasLazySubInitializer = false; } else { - this.keyIsEager = initializer.isEager(); - this.hasLazySubInitializer = !initializer.isEager() || initializer.hasLazySubInitializers(); + keyIsEager = identifierAssembler.isEager(); + hasLazySubInitializer = identifierAssembler.hasLazySubInitializers(); } } @@ -124,7 +124,7 @@ public void resolveFromPreviousRow(EntityDelayedFetchInitializerData data) { data.setState( State.MISSING ); } else { - final Initializer initializer = identifierAssembler.getInitializer(); + final var initializer = identifierAssembler.getInitializer(); if ( initializer != null ) { initializer.resolveFromPreviousRow( data.getRowProcessingState() ); } @@ -135,145 +135,170 @@ public void resolveFromPreviousRow(EntityDelayedFetchInitializerData data) { @Override public void resolveInstance(EntityDelayedFetchInitializerData data) { - if ( data.getState() != State.KEY_RESOLVED ) { - return; - } - - // This initializer is done initializing, since this is only invoked for delayed or select initializers - data.setState( State.INITIALIZED ); + if ( data.getState() == State.KEY_RESOLVED ) { + // This initializer is done initializing, since this is only invoked for delayed or select initializers + data.setState( State.INITIALIZED ); - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - data.entityIdentifier = identifierAssembler.assemble( rowProcessingState ); + final var rowProcessingState = data.getRowProcessingState(); + data.entityIdentifier = identifierAssembler.assemble( rowProcessingState ); - if ( data.entityIdentifier == null ) { - data.setInstance( null ); - data.setState( State.MISSING ); - } - else { - final SharedSessionContractImplementor session = rowProcessingState.getSession(); - - final EntityPersister entityPersister = getEntityDescriptor(); - final EntityPersister concreteDescriptor; - if ( discriminatorAssembler != null ) { - concreteDescriptor = determineConcreteEntityDescriptor( - rowProcessingState, - discriminatorAssembler, - entityPersister - ); - if ( concreteDescriptor == null ) { - // If we find no discriminator it means there's no entity in the target table - if ( !referencedModelPart.isOptional() ) { - throw new FetchNotFoundException( entityPersister.getEntityName(), data.entityIdentifier ); + if ( data.entityIdentifier == null ) { + data.setInstance( null ); + data.setState( State.MISSING ); + } + else { + final var entityPersister = getEntityDescriptor(); + final EntityPersister concreteDescriptor; + if ( discriminatorAssembler != null ) { + concreteDescriptor = determineConcreteEntityDescriptor( + rowProcessingState, + discriminatorAssembler, + entityPersister + ); + if ( concreteDescriptor == null ) { + // If we find no discriminator, it means there's no entity in the target table + if ( !referencedModelPart.isOptional() ) { + throw new FetchNotFoundException( entityPersister.getEntityName(), data.entityIdentifier ); + } + data.setInstance( null ); + data.setState( State.MISSING ); + return; } - data.setInstance( null ); - data.setState( State.MISSING ); - return; + } + else { + concreteDescriptor = entityPersister; + } + + if ( selectByUniqueKey ) { + data.setInstance( instanceWithUniqueKey( data, concreteDescriptor ) ); + } + else { + data.setInstance( instanceWithId( data, concreteDescriptor ) ); } } - else { - concreteDescriptor = entityPersister; + } + } + + private Object instanceWithId( + EntityDelayedFetchInitializerData data, + EntityPersister concreteDescriptor) { + final var rowProcessingState = data.getRowProcessingState(); + final var session = rowProcessingState.getSession(); + final var persistenceContext = session.getPersistenceContextInternal(); + + final var entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor ); + final var holder = persistenceContext.getEntityHolder( entityKey ); + if ( holder != null && holder.getEntity() != null ) { + return persistenceContext.proxyFor( holder, concreteDescriptor ); + } + // For primary key-based mappings we only use bytecode-laziness if the attribute is optional, + // because the non-optionality implies that it is safe to have a proxy + else if ( referencedModelPart.isOptional() && referencedModelPart.isLazy() ) { + return UNFETCHED_PROPERTY; + } + else { + final Object instance = session.internalLoad( + concreteDescriptor.getEntityName(), + data.entityIdentifier, + false, + false + ); + final var lazyInitializer = extractLazyInitializer( instance ); + if ( lazyInitializer != null ) { + lazyInitializer.setUnwrap( + referencedModelPart.isUnwrapProxy() + && concreteDescriptor.isInstrumented() ); } + return instance; + } + } - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - if ( selectByUniqueKey ) { - final String uniqueKeyPropertyName = referencedModelPart.getReferencedPropertyName(); - final Type uniqueKeyPropertyType = ( referencedModelPart.getReferencedPropertyName() == null ) ? - concreteDescriptor.getIdentifierType() : - session.getFactory().getRuntimeMetamodels() - .getReferencedPropertyType( - concreteDescriptor.getEntityName(), - uniqueKeyPropertyName - ); - - final EntityUniqueKey euk = new EntityUniqueKey( + private Object instanceWithUniqueKey( + EntityDelayedFetchInitializerData data, + EntityPersister concreteDescriptor) { + final var rowProcessingState = data.getRowProcessingState(); + final var session = rowProcessingState.getSession(); + final var persistenceContext = session.getPersistenceContextInternal(); + + final String uniqueKeyPropertyName = referencedModelPart.getReferencedPropertyName(); + final var entityUniqueKey = + new EntityUniqueKey( concreteDescriptor.getEntityName(), uniqueKeyPropertyName, data.entityIdentifier, - uniqueKeyPropertyType, + getUniqueKeyPropertyType( concreteDescriptor, session, uniqueKeyPropertyName ), session.getFactory() ); - Object instance = persistenceContext.getEntity( euk ); - if ( instance == null ) { - // For unique-key mappings, we always use bytecode-laziness if possible, - // because we can't generate a proxy based on the unique key yet - if ( referencedModelPart.isLazy() ) { - instance = UNFETCHED_PROPERTY; - } - else { - // Try to load a PersistentAttributeInterceptable. If we get one, we can add the lazy - // field to the interceptor. If we don't get one, we load the entity by unique key. - PersistentAttributeInterceptable persistentAttributeInterceptable = null; - if ( getParent().isEntityInitializer() && isLazyByGraph( rowProcessingState ) ) { - final Object resolvedInstance = - getParent().asEntityInitializer().getResolvedInstance( rowProcessingState ); - persistentAttributeInterceptable = - ManagedTypeHelper.asPersistentAttributeInterceptableOrNull( resolvedInstance ); - } - if ( persistentAttributeInterceptable != null ) { - final LazyAttributeLoadingInterceptor persistentAttributeInterceptor = (LazyAttributeLoadingInterceptor) persistentAttributeInterceptable.$$_hibernate_getInterceptor(); - persistentAttributeInterceptor.addLazyFieldByGraph( navigablePath.getLocalName() ); - instance = UNFETCHED_PROPERTY; - } - else { - instance = concreteDescriptor.loadByUniqueKey( - uniqueKeyPropertyName, - data.entityIdentifier, - session - ); - - // If the entity was not in the Persistence Context, but was found now, - // add it to the Persistence Context - if ( instance != null ) { - persistenceContext.addEntity( euk, instance ); - } - } - } - } - if ( instance != null ) { - instance = persistenceContext.proxyFor( instance ); - } - data.setInstance( instance ); + Object instance = persistenceContext.getEntity( entityUniqueKey ); + if ( instance == null ) { + // For unique-key mappings, we always use bytecode-laziness if possible, + // because we can't generate a proxy based on the unique key yet + if ( referencedModelPart.isLazy() ) { + instance = UNFETCHED_PROPERTY; } else { - final EntityKey entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor ); - final EntityHolder holder = persistenceContext.getEntityHolder( entityKey ); - final Object instance; - if ( holder != null && holder.getEntity() != null ) { - instance = persistenceContext.proxyFor( holder, concreteDescriptor ); - } - // For primary key based mappings we only use bytecode-laziness if the attribute is optional, - // because the non-optionality implies that it is safe to have a proxy - else if ( referencedModelPart.isOptional() && referencedModelPart.isLazy() ) { + // Try to load a PersistentAttributeInterceptable. If we get one, we can add the lazy + // field to the interceptor. If we don't get one, we load the entity by unique key. + final var persistentAttributeInterceptable = + getPersistentAttributeInterceptable( rowProcessingState ); + if ( persistentAttributeInterceptable != null ) { + final var persistentAttributeInterceptor = + (LazyAttributeLoadingInterceptor) + persistentAttributeInterceptable.$$_hibernate_getInterceptor(); + persistentAttributeInterceptor.addLazyFieldByGraph( navigablePath.getLocalName() ); instance = UNFETCHED_PROPERTY; } else { - instance = session.internalLoad( - concreteDescriptor.getEntityName(), + instance = concreteDescriptor.loadByUniqueKey( + uniqueKeyPropertyName, data.entityIdentifier, - false, - false + session ); - final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( instance ); - if ( lazyInitializer != null ) { - lazyInitializer.setUnwrap( referencedModelPart.isUnwrapProxy() && concreteDescriptor.isInstrumented() ); + // If the entity was not in the Persistence Context, but was found now, + // add it to the Persistence Context + if ( instance != null ) { + persistenceContext.addEntity( entityUniqueKey, instance ); } } - data.setInstance( instance ); } } + if ( instance != null ) { + instance = persistenceContext.proxyFor( instance ); + } + return instance; + } + + private PersistentAttributeInterceptable getPersistentAttributeInterceptable(RowProcessingState rowProcessingState) { + if ( getParent().isEntityInitializer() && isLazyByGraph( rowProcessingState ) ) { + final Object resolvedInstance = + getParent().asEntityInitializer() + .getResolvedInstance( rowProcessingState ); + return ManagedTypeHelper.asPersistentAttributeInterceptableOrNull( resolvedInstance ); + } + else { + return null; + } + } + + private Type getUniqueKeyPropertyType(EntityPersister concreteDescriptor, SharedSessionContractImplementor session, String uniqueKeyPropertyName) { + return referencedModelPart.getReferencedPropertyName() == null + ? concreteDescriptor.getIdentifierType() + : session.getFactory().getRuntimeMetamodels() + .getReferencedPropertyType( concreteDescriptor.getEntityName(), uniqueKeyPropertyName ); } private boolean isLazyByGraph(RowProcessingState rowProcessingState) { - final AppliedGraph appliedGraph = rowProcessingState.getQueryOptions().getAppliedGraph(); + final var appliedGraph = rowProcessingState.getQueryOptions().getAppliedGraph(); if ( appliedGraph != null && appliedGraph.getSemantic() == GraphSemantic.FETCH ) { - final AttributeNodeImplementor attributeNode = - appliedGraph.getGraph().findAttributeNode( navigablePath.getLocalName() ); + final var attributeNode = appliedGraph.getGraph().findAttributeNode( navigablePath.getLocalName() ); return attributeNode == null || attributeNode.getAttributeDescriptor() != getInitializedPart().asAttributeMapping(); } - return false; + else { + return false; + } } @Override @@ -287,15 +312,15 @@ public void resolveInstance(Object instance, EntityDelayedFetchInitializerData d // This initializer is done initializing, since this is only invoked for delayed or select initializers data.setState( State.INITIALIZED ); data.setInstance( instance ); - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); if ( keyIsEager ) { data.entityIdentifier = getEntityDescriptor().getIdentifier( instance, rowProcessingState.getSession() ); - final Initializer initializer = identifierAssembler.getInitializer(); + final var initializer = identifierAssembler.getInitializer(); assert initializer != null; initializer.resolveInstance( data.entityIdentifier, rowProcessingState ); } else if ( rowProcessingState.needsResolveState() ) { - // Resolve the state of the identifier if result caching is enabled and this is not a query cache hit + // Resolve the state of the identifier if result caching is enabled, and this is not a query cache hit identifierAssembler.resolveState( rowProcessingState ); } } @@ -303,7 +328,7 @@ else if ( rowProcessingState.needsResolveState() ) { @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { - final Initializer initializer = identifierAssembler.getInitializer(); + final var initializer = identifierAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, data.getRowProcessingState() ); } @@ -346,7 +371,7 @@ public EntityPersister getConcreteDescriptor(EntityDelayedFetchInitializerData d @Override public void resolveState(EntityDelayedFetchInitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); identifierAssembler.resolveState( rowProcessingState ); if ( discriminatorAssembler != null ) { discriminatorAssembler.resolveState( rowProcessingState ); @@ -360,7 +385,8 @@ public void resolveState(EntityDelayedFetchInitializerData data) { @Override public String toString() { - return "EntityDelayedFetchInitializer(" + LoggingHelper.toLoggableString( navigablePath ) + ")"; + return "EntityDelayedFetchInitializer(" + + toLoggableString( navigablePath ) + ")"; } //######################### diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java index a5c6e9f514f6..dfb0ad123fe7 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityInitializerImpl.java @@ -6,7 +6,6 @@ import java.util.Arrays; import java.util.BitSet; -import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; @@ -22,7 +21,6 @@ import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.cache.spi.access.AccessType; import org.hibernate.cache.spi.access.EntityDataAccess; -import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.engine.internal.ForeignKeys; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityHolder; @@ -30,51 +28,34 @@ import org.hibernate.engine.spi.EntityUniqueKey; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistentAttributeInterceptor; -import org.hibernate.engine.spi.SessionEventListenerManager; -import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.event.monitor.spi.EventMonitor; -import org.hibernate.event.monitor.spi.DiagnosticEvent; -import org.hibernate.event.spi.PreLoadEvent; import org.hibernate.event.spi.PreLoadEventListener; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.internal.util.ImmutableBitSet; -import org.hibernate.metamodel.mapping.AttributeMapping; -import org.hibernate.metamodel.mapping.AttributeMetadata; import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; -import org.hibernate.metamodel.mapping.DiscriminatorValueDetails; -import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; -import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityValuedModelPart; -import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.persister.entity.UniqueKeyEntry; import org.hibernate.property.access.internal.PropertyAccessStrategyBackRefImpl; -import org.hibernate.proxy.LazyInitializer; import org.hibernate.proxy.map.MapProxy; import org.hibernate.spi.NavigablePath; -import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.Fetch; -import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.Initializer; import org.hibernate.sql.results.graph.InitializerData; import org.hibernate.sql.results.graph.InitializerParent; import org.hibernate.sql.results.graph.basic.BasicResultAssembler; import org.hibernate.sql.results.graph.collection.internal.AbstractImmediateCollectionInitializer; -import org.hibernate.sql.results.graph.embeddable.EmbeddableInitializer; import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.graph.entity.EntityResultGraphNode; import org.hibernate.sql.results.graph.internal.AbstractInitializer; import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; -import org.hibernate.stat.spi.StatisticsImplementor; import org.hibernate.type.ManyToOneType; import org.hibernate.type.Type; import org.hibernate.type.descriptor.java.MutabilityPlan; @@ -85,6 +66,7 @@ import static org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer.UNFETCHED_PROPERTY; import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable; import static org.hibernate.engine.internal.ManagedTypeHelper.isPersistentAttributeInterceptable; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; import static org.hibernate.internal.util.NullnessUtil.castNonNull; import static org.hibernate.loader.internal.CacheLoadHelper.loadFromSecondLevelCache; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; @@ -92,10 +74,11 @@ /** * @author Andrea Boriero */ -public class EntityInitializerImpl extends AbstractInitializer +public class EntityInitializerImpl + extends AbstractInitializer implements EntityInitializer { - // NOTE : even though we only keep the EntityDescriptor here, rather than EntityResultGraphNode + // NOTE: even though we only keep the EntityDescriptor here, rather than EntityResultGraphNode, // the "scope" of this initializer is a specific EntityReference. // // The full EntityResultGraphNode is simply not needed here, and so we just keep @@ -158,16 +141,18 @@ public static class EntityInitializerData extends InitializerData { public EntityInitializerData(EntityInitializerImpl initializer, RowProcessingState rowProcessingState) { super( rowProcessingState ); - final EntityPersister entityDescriptor = initializer.entityDescriptor; + final var entityDescriptor = initializer.entityDescriptor; shallowCached = rowProcessingState.isQueryCacheHit() && entityDescriptor.useShallowQueryCacheLayout(); lockMode = rowProcessingState.determineEffectiveLockMode( initializer.sourceAlias ); if ( initializer.isResultInitializer() ) { uniqueKeyAttributePath = rowProcessingState.getEntityUniqueKeyAttributePath(); - uniqueKeyPropertyTypes = uniqueKeyAttributePath != null - ? initializer.getParentEntityAttributeTypes( uniqueKeyAttributePath ) - : null; - canUseEmbeddedIdentifierInstanceAsEntity = rowProcessingState.getEntityId() != null - && initializer.couldUseEmbeddedIdentifierInstanceAsEntity; + uniqueKeyPropertyTypes = + uniqueKeyAttributePath != null + ? initializer.getParentEntityAttributeTypes( uniqueKeyAttributePath ) + : null; + canUseEmbeddedIdentifierInstanceAsEntity = + rowProcessingState.getEntityId() != null + && initializer.couldUseEmbeddedIdentifierInstanceAsEntity; } else { uniqueKeyAttributePath = null; @@ -223,27 +208,28 @@ public EntityInitializerImpl( boolean isResultInitializer, AssemblerCreationState creationState) { super( creationState ); + this.sourceAlias = sourceAlias; + this.parent = parent; + this.isResultInitializer = isResultInitializer; referencedModelPart = resultDescriptor.getEntityValuedModelPart(); entityDescriptor = (EntityPersister) referencedModelPart.getEntityMappingType(); final String rootEntityName = entityDescriptor.getRootEntityName(); - rootEntityDescriptor = rootEntityName == null || rootEntityName.equals( entityDescriptor.getEntityName() ) - ? entityDescriptor - : entityDescriptor.getRootEntityDescriptor().getEntityPersister(); + rootEntityDescriptor = + rootEntityName == null || rootEntityName.equals( entityDescriptor.getEntityName() ) + ? entityDescriptor + : entityDescriptor.getRootEntityDescriptor().getEntityPersister(); keyTypeForEqualsHashCode = entityDescriptor.getIdentifierType().getTypeForEqualsHashCode(); // The id can only be the entity instance if this is a non-aggregated id that has no containing class couldUseEmbeddedIdentifierInstanceAsEntity = entityDescriptor.getIdentifierMapping() instanceof CompositeIdentifierMapping composite && !composite.hasContainingClass(); - this.navigablePath = resultDescriptor.getNavigablePath(); - this.sourceAlias = sourceAlias; - this.parent = parent; - this.isResultInitializer = isResultInitializer; - this.isPartOfKey = Initializer.isPartOfKey( navigablePath, parent ); + navigablePath = resultDescriptor.getNavigablePath(); + isPartOfKey = Initializer.isPartOfKey( navigablePath, parent ); // If the parent already has previous row reuse enabled, we can skip that here - this.previousRowReuse = !isPreviousRowReuse( parent ) && ( + previousRowReuse = !isPreviousRowReuse( parent ) && ( // If this entity domain result contains a collection join fetch, this usually means that the entity data is // duplicate in the result data for every collection element. Since collections usually have more than one element, // optimizing the resolving of the entity data is very beneficial. @@ -259,19 +245,21 @@ public EntityInitializerImpl( } else { identifierAssembler = identifierFetch.createAssembler( this, creationState ); - final Initializer initializer = identifierAssembler.getInitializer(); + final var initializer = identifierAssembler.getInitializer(); hasKeyManyToOne = initializer != null && initializer.isLazyCapable(); } assert entityDescriptor.hasSubclasses() == (discriminatorFetch != null) : "Discriminator should only be fetched if the entity has subclasses"; - discriminatorAssembler = discriminatorFetch != null - ? (BasicResultAssembler) discriminatorFetch.createAssembler( this, creationState ) - : null; + discriminatorAssembler = + discriminatorFetch == null + ? null + : (BasicResultAssembler) + discriminatorFetch.createAssembler( this, creationState ); - final EntityVersionMapping versionMapping = entityDescriptor.getVersionMapping(); + final var versionMapping = entityDescriptor.getVersionMapping(); if ( versionMapping != null ) { - final Fetch versionFetch = resultDescriptor.findFetch( versionMapping ); + final var versionFetch = resultDescriptor.findFetch( versionMapping ); // If there is a version mapping, there must be a fetch for it assert versionFetch != null; versionAssembler = versionFetch.createAssembler( this, creationState ); @@ -280,45 +268,48 @@ public EntityInitializerImpl( versionAssembler = null; } - rowIdAssembler = rowIdResult != null - ? rowIdResult.createResultAssembler( this, creationState ) - : null; + rowIdAssembler = + rowIdResult == null + ? null : + rowIdResult.createResultAssembler( this, creationState ); final int fetchableCount = entityDescriptor.getNumberOfFetchables(); - final Collection subMappingTypes = rootEntityDescriptor.getSubMappingTypes(); - final DomainResultAssembler[][] assemblers = new DomainResultAssembler[subMappingTypes.size() + 1][]; - final Initializer[] allInitializers = new Initializer[fetchableCount]; - final Initializer[][] subInitializers = new Initializer[subMappingTypes.size() + 1][]; - final Initializer[][] eagerSubInitializers = new Initializer[subMappingTypes.size() + 1][]; - final Initializer[][] collectionContainingSubInitializers = new Initializer[subMappingTypes.size() + 1][]; - final BitSet[] lazySets = new BitSet[subMappingTypes.size() + 1]; - final BitSet[] maybeLazySets = new BitSet[subMappingTypes.size() + 1]; - final MutabilityPlan[][] updatableAttributeMutabilityPlans = new MutabilityPlan[subMappingTypes.size() + 1][]; + final var subMappingTypes = rootEntityDescriptor.getSubMappingTypes(); + final var assemblers = new DomainResultAssembler[subMappingTypes.size() + 1][]; + final var allInitializers = new Initializer[fetchableCount]; + final var subInitializers = new Initializer[subMappingTypes.size() + 1][]; + final var eagerSubInitializers = new Initializer[subMappingTypes.size() + 1][]; + final var collectionContainingSubInitializers = new Initializer[subMappingTypes.size() + 1][]; + final var lazySets = new BitSet[subMappingTypes.size() + 1]; + final var maybeLazySets = new BitSet[subMappingTypes.size() + 1]; + final var updatableAttributeMutabilityPlans = new MutabilityPlan[subMappingTypes.size() + 1][]; assemblers[rootEntityDescriptor.getSubclassId()] = new DomainResultAssembler[rootEntityDescriptor.getNumberOfFetchables()]; updatableAttributeMutabilityPlans[rootEntityDescriptor.getSubclassId()] = new MutabilityPlan[rootEntityDescriptor.getNumberOfAttributeMappings()]; - for ( EntityMappingType subMappingType : subMappingTypes ) { - assemblers[subMappingType.getSubclassId()] = + for ( var subMappingType : subMappingTypes ) { + final int subclassId = subMappingType.getSubclassId(); + assemblers[subclassId] = new DomainResultAssembler[subMappingType.getNumberOfFetchables()]; - updatableAttributeMutabilityPlans[subMappingType.getSubclassId()] = + updatableAttributeMutabilityPlans[subclassId] = new MutabilityPlan[subMappingType.getNumberOfAttributeMappings()]; } boolean hasLazySubInitializers = false; for ( int i = 0; i < fetchableCount; i++ ) { - final AttributeMapping attributeMapping = entityDescriptor.getFetchable( i ).asAttributeMapping(); - final Fetch fetch = resultDescriptor.findFetch( attributeMapping ); - final DomainResultAssembler stateAssembler = fetch == null - ? new NullValueAssembler<>( attributeMapping.getMappedType().getMappedJavaType() ) - : fetch.createAssembler( this, creationState ); + final var attributeMapping = entityDescriptor.getFetchable( i ).asAttributeMapping(); + final var fetch = resultDescriptor.findFetch( attributeMapping ); + final var stateAssembler = + fetch == null + ? new NullValueAssembler<>( attributeMapping.getMappedType().getMappedJavaType() ) + : fetch.createAssembler( this, creationState ); final int stateArrayPosition = attributeMapping.getStateArrayPosition(); - final EntityMappingType declaringType = attributeMapping.getDeclaringType().asEntityMappingType(); + final var declaringType = attributeMapping.getDeclaringType().asEntityMappingType(); final int subclassId = declaringType.getSubclassId(); - final Initializer subInitializer = stateAssembler.getInitializer(); + final var subInitializer = stateAssembler.getInitializer(); if ( subInitializer != null ) { allInitializers[i] = subInitializer; if ( subInitializers[subclassId] == null ) { @@ -336,8 +327,8 @@ public EntityInitializerImpl( hasLazySubInitializers = true; } assert fetch != null; - final FetchParent fetchParent; - if ( ( fetchParent = fetch.asFetchParent() ) != null && fetchParent.containsCollectionFetches() + final var fetchParent = fetch.asFetchParent(); + if ( fetchParent != null && fetchParent.containsCollectionFetches() || subInitializer.isCollectionInitializer() ) { collectionContainingSubInitializers[subclassId][stateArrayPosition] = subInitializer; } @@ -351,11 +342,11 @@ public EntityInitializerImpl( } assemblers[subclassId][stateArrayPosition] = stateAssembler; - final AttributeMetadata attributeMetadata = attributeMapping.getAttributeMetadata(); + final var attributeMetadata = attributeMapping.getAttributeMetadata(); if ( attributeMetadata.isUpdatable() ) { updatableAttributeMutabilityPlans[subclassId][stateArrayPosition] = attributeMetadata.getMutabilityPlan(); } - for ( EntityMappingType subMappingType : declaringType.getSubMappingTypes() ) { + for ( var subMappingType : declaringType.getSubMappingTypes() ) { assemblers[subMappingType.getSubclassId()][stateArrayPosition] = stateAssembler; updatableAttributeMutabilityPlans[subMappingType.getSubclassId()][stateArrayPosition] = updatableAttributeMutabilityPlans[subclassId][stateArrayPosition]; @@ -381,11 +372,11 @@ public EntityInitializerImpl( } } } - final BitSet emptyBitSet = new BitSet(); + final var emptyBitSet = new BitSet(); for ( int i = 0; i < subInitializers.length; i++ ) { boolean emptySubInitializers = true; if ( subInitializers[i] != null ) { - for ( Initializer initializer : subInitializers[i] ) { + for ( var initializer : subInitializers[i] ) { if ( initializer != null ) { emptySubInitializers = false; break; @@ -432,10 +423,8 @@ public EntityInitializerImpl( ? subInitializers : eagerSubInitializers; this.collectionContainingSubInitializers = collectionContainingSubInitializers; - this.lazySets = Arrays.stream( lazySets ).map( ImmutableBitSet::valueOf ).toArray( ImmutableBitSet[]::new ); - this.maybeLazySets = Arrays.stream( maybeLazySets ) - .map( ImmutableBitSet::valueOf ) - .toArray( ImmutableBitSet[]::new ); + this.lazySets = toBitSetArray( lazySets ); + this.maybeLazySets = toBitSetArray( maybeLazySets ); this.hasLazySubInitializers = hasLazySubInitializers; this.updatableAttributeMutabilityPlans = updatableAttributeMutabilityPlans; this.notFoundAction = notFoundAction; @@ -444,6 +433,12 @@ public EntityInitializerImpl( this.affectedByFilter = affectedByFilter; } + private static ImmutableBitSet[] toBitSetArray(BitSet[] lazySets) { + return Arrays.stream( lazySets ) + .map( ImmutableBitSet::valueOf ) + .toArray( ImmutableBitSet[]::new ); + } + private static boolean isPreviousRowReuse(@Nullable InitializerParent parent) { // Traverse up the parents to find out if one of our parents has row reuse enabled while ( parent != null ) { @@ -477,7 +472,7 @@ public void resolveKey(EntityInitializerData data) { @Override public @Nullable EntityKey resolveEntityKeyOnly(RowProcessingState rowProcessingState) { assert identifierAssembler != null; - final EntityInitializerData data = getData( rowProcessingState ); + final var data = getData( rowProcessingState ); resolveKey( data, true ); try { if ( data.getState() == State.MISSING ) { @@ -494,9 +489,9 @@ public void resolveKey(EntityInitializerData data) { return data.entityKey; } finally { - final Initializer initializer = identifierAssembler.getInitializer(); + final var initializer = identifierAssembler.getInitializer(); if ( hasKeyManyToOne && initializer != null ) { - final EmbeddableInitializer embeddableInitializer = initializer.asEmbeddableInitializer(); + final var embeddableInitializer = initializer.asEmbeddableInitializer(); assert embeddableInitializer != null; embeddableInitializer.resetResolvedEntityRegistrations( rowProcessingState ); } @@ -505,7 +500,7 @@ public void resolveKey(EntityInitializerData data) { @Override public void resetResolvedEntityRegistrations(RowProcessingState rowProcessingState) { - final EntityInitializerData data = getData( rowProcessingState ); + final var data = getData( rowProcessingState ); if ( data.getState() == State.RESOLVED ) { rowProcessingState.getSession() .getPersistenceContextInternal() @@ -524,110 +519,115 @@ protected void resolveKey(EntityInitializerData data, boolean entityKeyOnly) { // todo (6.0) : atm we do not handle sequential selects // - see AbstractEntityPersister#hasSequentialSelect and // AbstractEntityPersister#getSequentialSelect in 5.2 - if ( data.getState() != State.UNINITIALIZED ) { - return; - } - data.setState( State.KEY_RESOLVED ); - - final EntityKey oldEntityKey = data.entityKey; - final Object oldEntityInstance = data.getInstance(); - final Object oldEntityInstanceForNotify = data.entityInstanceForNotify; - final EntityHolder oldEntityHolder = data.entityHolder; - // reset row state - final EntityPersister concreteDescriptor = data.concreteDescriptor = data.defaultConcreteDescriptor; - data.entityKey = null; - data.setInstance( null ); - data.entityInstanceForNotify = null; - data.entityHolder = null; + if ( data.getState() == State.UNINITIALIZED ) { + data.setState( State.KEY_RESOLVED ); + + final var oldEntityKey = data.entityKey; + final Object oldEntityInstance = data.getInstance(); + final Object oldEntityInstanceForNotify = data.entityInstanceForNotify; + final var oldEntityHolder = data.entityHolder; + // reset row state + final var concreteDescriptor = data.concreteDescriptor = data.defaultConcreteDescriptor; + data.entityKey = null; + data.setInstance( null ); + data.entityInstanceForNotify = null; + data.entityHolder = null; - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final Object id; - if ( identifierAssembler == null ) { - id = rowProcessingState.getEntityId(); - assert id != null : "Initializer requires a not null id for loading"; - } - else { - //noinspection unchecked - final Initializer initializer = - (Initializer) identifierAssembler.getInitializer(); - if ( initializer != null ) { - final InitializerData subData = initializer.getData( rowProcessingState ); - initializer.resolveKey( subData ); - if ( subData.getState() == State.MISSING ) { - setMissing( data ); - return; - } - else { - if ( concreteDescriptor == null ) { - data.concreteDescriptor = determineConcreteEntityDescriptor( - rowProcessingState, - discriminatorAssembler, - entityDescriptor - ); - assert data.concreteDescriptor != null; + final var rowProcessingState = data.getRowProcessingState(); + final Object id; + if ( identifierAssembler == null ) { + id = rowProcessingState.getEntityId(); + assert id != null : "Initializer requires a not null id for loading"; + } + else { + //noinspection unchecked + final var initializer = + (Initializer) + identifierAssembler.getInitializer(); + if ( initializer != null ) { + final var subData = initializer.getData( rowProcessingState ); + initializer.resolveKey( subData ); + if ( subData.getState() == State.MISSING ) { + setMissing( data ); + return; } - if ( hasKeyManyToOne ) { - if ( !data.shallowCached && !entityKeyOnly ) { - resolveKeySubInitializers( data ); + else { + if ( concreteDescriptor == null ) { + data.concreteDescriptor = determineConcreteEntityDescriptor( + rowProcessingState, + discriminatorAssembler, + entityDescriptor + ); + assert data.concreteDescriptor != null; + } + if ( hasKeyManyToOne ) { + if ( !data.shallowCached && !entityKeyOnly ) { + resolveKeySubInitializers( data ); + } + return; } - return; } } + id = identifierAssembler.assemble( rowProcessingState ); + if ( id == null ) { + setMissing( data ); + return; + } } - id = identifierAssembler.assemble( rowProcessingState ); - if ( id == null ) { - setMissing( data ); + + if ( oldEntityKey != null + && previousRowReuse + && oldEntityInstance != null + && areKeysEqual( oldEntityKey.getIdentifier(), id ) + && !oldEntityHolder.isDetached() ) { + data.setState( State.INITIALIZED ); + data.entityKey = oldEntityKey; + data.setInstance( oldEntityInstance ); + data.entityInstanceForNotify = oldEntityInstanceForNotify; + data.concreteDescriptor = oldEntityKey.getPersister(); + data.entityHolder = oldEntityHolder; + if ( !entityKeyOnly ) { + notifySubInitializersToReusePreviousRowInstance( data ); + } return; } - } - - if ( oldEntityKey != null && previousRowReuse && oldEntityInstance != null - && areKeysEqual( oldEntityKey.getIdentifier(), id ) && !oldEntityHolder.isDetached() ) { - data.setState( State.INITIALIZED ); - data.entityKey = oldEntityKey; - data.setInstance( oldEntityInstance ); - data.entityInstanceForNotify = oldEntityInstanceForNotify; - data.concreteDescriptor = oldEntityKey.getPersister(); - data.entityHolder = oldEntityHolder; + resolveEntityKey( data, id ); if ( !entityKeyOnly ) { - notifySubInitializersToReusePreviousRowInstance( data ); - } - return; - } - resolveEntityKey( data, id ); - if ( !entityKeyOnly ) { - // Resolve the entity instance early as we have no key many-to-one - resolveInstance( data ); - if ( !data.shallowCached ) { - if ( data.getState() == State.INITIALIZED ) { - if ( data.entityHolder.getEntityInitializer() == null ) { - // The entity is already part of the persistence context, - // so let's figure out the loaded state and only run sub-initializers if necessary - resolveInstanceSubInitializers( data ); + // Resolve the entity instance early as we have no key many-to-one + resolveInstance( data ); + if ( !data.shallowCached ) { + if ( data.getState() == State.INITIALIZED ) { + if ( data.entityHolder.getEntityInitializer() == null ) { + // The entity is already part of the persistence context, + // so let's figure out the loaded state and only run sub-initializers if necessary + resolveInstanceSubInitializers( data ); + } + // If the entity is initialized and getEntityInitializer() == this, + // we already processed a row for this entity before, + // but we still have to call resolveKeySubInitializers to activate sub-initializers, + // because a row might contain data that sub-initializers want to consume + else { + // todo: try to diff the eagerness of the sub-initializers to avoid further processing + resolveKeySubInitializers( data ); + } } - // If the entity is initialized and getEntityInitializer() == this, - // we already processed a row for this entity before, - // but we still have to call resolveKeySubInitializers to activate sub-initializers, - // because a row might contain data that sub-initializers want to consume else { - // todo: try to diff the eagerness of the sub-initializers to avoid further processing resolveKeySubInitializers( data ); } } - else { - resolveKeySubInitializers( data ); - } } } } private boolean areKeysEqual(Object key1, Object key2) { - return keyTypeForEqualsHashCode == null ? key1.equals( key2 ) : keyTypeForEqualsHashCode.isEqual( key1, key2 ); + return keyTypeForEqualsHashCode == null + ? key1.equals( key2 ) + : keyTypeForEqualsHashCode.isEqual( key1, key2 ); } protected void resolveInstanceSubInitializers(EntityInitializerData data) { final int subclassId = data.concreteDescriptor.getSubclassId(); - final EntityEntry entityEntry = data.entityHolder.getEntityEntry(); + final var entityEntry = data.entityHolder.getEntityEntry(); assert entityEntry != null : "This method should only be called if the entity is already initialized"; final Initializer[] initializers; @@ -642,15 +642,15 @@ protected void resolveInstanceSubInitializers(EntityInitializerData data) { maybeLazySet = entityEntry.getMaybeLazySet(); // Skip resolving if this initializer has no sub-initializers // or the lazy set of this initializer is a superset/contains the entity entry maybeLazySet - if ( initializers.length == 0 || maybeLazySet != null && lazySets[subclassId].contains( maybeLazySet ) ) { + if ( initializers.length == 0 + || maybeLazySet != null && lazySets[subclassId].contains( maybeLazySet ) ) { return; } } - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final PersistenceContext persistenceContext = rowProcessingState.getSession() - .getPersistenceContextInternal(); + final var rowProcessingState = data.getRowProcessingState(); + final var persistenceContext = rowProcessingState.getSession().getPersistenceContextInternal(); assert entityEntry == persistenceContext.getEntry( data.entityInstanceForNotify ); - final Object[] loadedState = entityEntry.getLoadedState(); + final var loadedState = entityEntry.getLoadedState(); final Object[] state; if ( loadedState == null ) { if ( entityEntry.getStatus() == Status.READ_ONLY ) { @@ -668,8 +668,9 @@ protected void resolveInstanceSubInitializers(EntityInitializerData data) { state = loadedState; } for ( int i = 0; i < initializers.length; i++ ) { - final Initializer initializer = initializers[i]; - if ( initializer != null && ( maybeLazySet == null || maybeLazySet.get( i ) ) ) { + final var initializer = initializers[i]; + if ( initializer != null + && ( maybeLazySet == null || maybeLazySet.get( i ) ) ) { final Object subInstance = state[i]; if ( subInstance == UNFETCHED_PROPERTY ) { // Go through the normal initializer process @@ -683,7 +684,7 @@ protected void resolveInstanceSubInitializers(EntityInitializerData data) { } private void notifySubInitializersToReusePreviousRowInstance(EntityInitializerData data) { - final EntityEntry entityEntry = data.entityHolder.getEntityEntry(); + final var entityEntry = data.entityHolder.getEntityEntry(); final Initializer[] subInitializer; final ImmutableBitSet maybeLazySet; if ( data.entityHolder.getEntityInitializer() == this ) { @@ -695,20 +696,21 @@ private void notifySubInitializersToReusePreviousRowInstance(EntityInitializerDa subInitializer = subInitializersForResolveFromInitialized[data.concreteDescriptor.getSubclassId()]; maybeLazySet = entityEntry == null ? null : entityEntry.getMaybeLazySet(); } - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); for ( int i = 0; i < subInitializer.length; i++ ) { - final Initializer initializer = subInitializer[i]; + final var initializer = subInitializer[i]; // It is vital to only resolveFromPreviousRow only for the initializers where the state is maybe lazy, // as the initialization process for the previous row also only called those initializers - if ( initializer != null && ( maybeLazySet == null || maybeLazySet.get( i ) ) ) { + if ( initializer != null + && ( maybeLazySet == null || maybeLazySet.get( i ) ) ) { initializer.resolveFromPreviousRow( rowProcessingState ); } } } protected void resolveKeySubInitializers(EntityInitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - for ( Initializer initializer : subInitializers[data.concreteDescriptor.getSubclassId()] ) { + final var rowProcessingState = data.getRowProcessingState(); + for ( var initializer : subInitializers[data.concreteDescriptor.getSubclassId()] ) { if ( initializer != null ) { initializer.resolveKey( rowProcessingState ); } @@ -717,13 +719,11 @@ protected void resolveKeySubInitializers(EntityInitializerData data) { @EnsuresNonNull( "data.entityKey" ) protected void resolveEntityKey(EntityInitializerData data, Object id) { - EntityPersister concreteDescriptor = data.concreteDescriptor; + var concreteDescriptor = data.concreteDescriptor; if ( concreteDescriptor == null ) { - concreteDescriptor = data.concreteDescriptor = determineConcreteEntityDescriptor( - data.getRowProcessingState(), - discriminatorAssembler, - entityDescriptor - ); + concreteDescriptor = data.concreteDescriptor = + determineConcreteEntityDescriptor( data.getRowProcessingState(), + discriminatorAssembler, entityDescriptor ); assert concreteDescriptor != null; } data.entityKey = new EntityKey( id, concreteDescriptor ); @@ -764,7 +764,7 @@ protected void setMissing(EntityInitializerData data) { @Override public void resolveFromPreviousRow(EntityInitializerData data) { if ( data.getState() == State.UNINITIALIZED ) { - final EntityKey entityKey = data.entityKey; + final var entityKey = data.entityKey; if ( entityKey == null ) { setMissing( data ); } @@ -777,11 +777,12 @@ public void resolveFromPreviousRow(EntityInitializerData data) { @Override public void initializeInstanceFromParent(Object parentInstance, EntityInitializerData data) { - final AttributeMapping attributeMapping = getInitializedPart().asAttributeMapping(); - final Object instance = attributeMapping != null - ? attributeMapping.getValue( parentInstance ) - : parentInstance; - final SharedSessionContractImplementor session = data.getRowProcessingState().getSession(); + final var attributeMapping = getInitializedPart().asAttributeMapping(); + final Object instance = + attributeMapping != null + ? attributeMapping.getValue( parentInstance ) + : parentInstance; + final var session = data.getRowProcessingState().getSession(); if ( instance == null ) { setMissing( data ); } @@ -789,10 +790,8 @@ public void initializeInstanceFromParent(Object parentInstance, EntityInitialize data.setInstance( instance ); final Object entityInstanceForNotify = data.entityInstanceForNotify = Hibernate.unproxy( instance ); data.concreteDescriptor = session.getEntityPersister( null, entityInstanceForNotify ); - resolveEntityKey( - data, - data.concreteDescriptor.getIdentifier( entityInstanceForNotify, session ) - ); + resolveEntityKey( data, + data.concreteDescriptor.getIdentifier( entityInstanceForNotify, session ) ); data.entityHolder = session.getPersistenceContextInternal().getEntityHolder( data.entityKey ); data.setState( State.INITIALIZED ); initializeSubInstancesFromParent( data ); @@ -805,7 +804,7 @@ public boolean isResultInitializer() { } private void deepCopy(EntityPersister containerDescriptor, Object[] source, Object[] target) { - final MutabilityPlan[] updatableAttributeMutabilityPlan = + final var updatableAttributeMutabilityPlan = updatableAttributeMutabilityPlans[containerDescriptor.getSubclassId()]; for ( int i = 0; i < updatableAttributeMutabilityPlan.length; i++ ) { final Object sourceValue = source[i]; @@ -849,11 +848,11 @@ public Object getTargetInstance(EntityInitializerData data) { private final ConcurrentHashMap parentEntityAttributeTypes = new ConcurrentHashMap<>(); protected Type[] getParentEntityAttributeTypes(String attributeName) { - Type[] types = parentEntityAttributeTypes.get( attributeName ); + var types = parentEntityAttributeTypes.get( attributeName ); if ( types == null ) { types = new Type[entityDescriptor.getRootEntityDescriptor().getSubclassEntityNames().size()]; initializeAttributeType( types, entityDescriptor, attributeName ); - for ( EntityMappingType subMappingType : entityDescriptor.getSubMappingTypes() ) { + for ( var subMappingType : entityDescriptor.getSubMappingTypes() ) { initializeAttributeType( types, subMappingType.getEntityPersister(), attributeName ); } parentEntityAttributeTypes.putIfAbsent( attributeName, types ); @@ -878,17 +877,16 @@ protected void initializeAttributeType(Type[] attributeTypes, EntityPersister en else { assert entityDescriptor.hasSubclasses() : "Reading a discriminator from a result set should only happen if the entity has subclasses"; - final EntityDiscriminatorMapping discriminatorMapping = entityDescriptor.getDiscriminatorMapping(); + final var discriminatorMapping = entityDescriptor.getDiscriminatorMapping(); assert discriminatorMapping != null; final Object discriminator = discriminatorAssembler.extractRawValue( rowProcessingState ); - final DiscriminatorValueDetails discriminatorDetails = - discriminatorMapping.resolveDiscriminatorValue( discriminator ); + final var discriminatorDetails = discriminatorMapping.resolveDiscriminatorValue( discriminator ); if ( discriminatorDetails == null ) { assert discriminator == null : "Discriminator details should only be null for null values"; return null; } else { - final EntityMappingType indicatedEntity = discriminatorDetails.getIndicatedEntity(); + final var indicatedEntity = discriminatorDetails.getIndicatedEntity(); if ( indicatedEntity.isTypeOrSuperType( entityDescriptor ) ) { return indicatedEntity.getEntityPersister(); } @@ -921,169 +919,174 @@ protected boolean useEmbeddedIdentifierInstanceAsEntity(EntityInitializerData da public void resolveInstance(Object instance, EntityInitializerData data) { if ( instance == null ) { setMissing( data ); - return; } - data.setInstance( instance ); - final LazyInitializer lazyInitializer = extractLazyInitializer( instance ); - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final SharedSessionContractImplementor session = rowProcessingState.getSession(); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - if ( lazyInitializer == null ) { - // Entity is most probably initialized - data.entityInstanceForNotify = instance; - data.concreteDescriptor = session.getEntityPersister( null, instance ); - resolveEntityKey( data, data.concreteDescriptor.getIdentifier( instance, session ) ); - data.entityHolder = persistenceContext.getEntityHolder( data.entityKey ); - if ( data.entityHolder == null ) { - // Entity was most probably removed in the same session without setting this association to null. - // Since this load request can happen through `find()` which doesn't auto-flush on association joins, - // the entity must be fully initialized, even if it is removed already - data.entityHolder = persistenceContext.claimEntityHolderIfPossible( - data.entityKey, - data.entityInstanceForNotify, - rowProcessingState.getJdbcValuesSourceProcessingState(), - this - ); + else { + data.setInstance( instance ); + final var lazyInitializer = extractLazyInitializer( instance ); + final var rowProcessingState = data.getRowProcessingState(); + final var session = rowProcessingState.getSession(); + final var persistenceContext = session.getPersistenceContextInternal(); + if ( lazyInitializer == null ) { + // Entity is most probably initialized + data.entityInstanceForNotify = instance; + data.concreteDescriptor = session.getEntityPersister( null, instance ); + resolveEntityKey( data, data.concreteDescriptor.getIdentifier( instance, session ) ); + data.entityHolder = persistenceContext.getEntityHolder( data.entityKey ); + if ( data.entityHolder == null ) { + // Entity was most probably removed in the same session without setting this association to null. + // Since this load request can happen through `find()` which doesn't auto-flush on association joins, + // the entity must be fully initialized, even if it is removed already + data.entityHolder = + persistenceContext.claimEntityHolderIfPossible( + data.entityKey, + data.entityInstanceForNotify, + rowProcessingState.getJdbcValuesSourceProcessingState(), + this + ); + } + if ( data.concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() + && isPersistentAttributeInterceptable( data.entityInstanceForNotify ) + && getAttributeInterceptor( data.entityInstanceForNotify ) + instanceof EnhancementAsProxyLazinessInterceptor enhancementInterceptor + && !enhancementInterceptor.isInitialized() ) { + data.setState( State.RESOLVED ); + } + else { + // If the entity initializer is null, we know the entity is fully initialized; + // otherwise it will be initialized by some other initializer + data.setState( data.entityHolder.getEntityInitializer() == null ? State.INITIALIZED : State.RESOLVED ); + } + + if ( data.getState() == State.RESOLVED ) { + data.entityHolder = persistenceContext.claimEntityHolderIfPossible( + data.entityKey, + data.entityInstanceForNotify, + rowProcessingState.getJdbcValuesSourceProcessingState(), + this + ); + } } - if ( data.concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading() - && isPersistentAttributeInterceptable( data.entityInstanceForNotify ) - && getAttributeInterceptor( data.entityInstanceForNotify ) - instanceof EnhancementAsProxyLazinessInterceptor enhancementInterceptor - && !enhancementInterceptor.isInitialized() ) { + else if ( lazyInitializer.isUninitialized() ) { data.setState( State.RESOLVED ); - } - else { - // If the entity initializer is null, we know the entity is fully initialized, - // otherwise it will be initialized by some other initializer - data.setState( data.entityHolder.getEntityInitializer() == null ? State.INITIALIZED : State.RESOLVED ); - } - - if ( data.getState() == State.RESOLVED ) { + // Read the discriminator from the result set if necessary + data.concreteDescriptor = + discriminatorAssembler == null + ? entityDescriptor + : determineConcreteEntityDescriptor( rowProcessingState, + discriminatorAssembler, entityDescriptor ); + assert data.concreteDescriptor != null; + resolveEntityKey( data, lazyInitializer.getInternalIdentifier() ); data.entityHolder = persistenceContext.claimEntityHolderIfPossible( data.entityKey, - data.entityInstanceForNotify, + null, rowProcessingState.getJdbcValuesSourceProcessingState(), this ); + // Resolve and potentially create the entity instance + data.entityInstanceForNotify = resolveEntityInstance( data ); + lazyInitializer.setImplementation( data.entityInstanceForNotify ); + registerLoadingEntity( data, data.entityInstanceForNotify ); } - } - else if ( lazyInitializer.isUninitialized() ) { - data.setState( State.RESOLVED ); - // Read the discriminator from the result set if necessary - data.concreteDescriptor = discriminatorAssembler == null - ? entityDescriptor - : determineConcreteEntityDescriptor( rowProcessingState, discriminatorAssembler, entityDescriptor ); - assert data.concreteDescriptor != null; - resolveEntityKey( data, lazyInitializer.getInternalIdentifier() ); - data.entityHolder = persistenceContext.claimEntityHolderIfPossible( - data.entityKey, - null, - rowProcessingState.getJdbcValuesSourceProcessingState(), - this - ); - // Resolve and potentially create the entity instance - data.entityInstanceForNotify = resolveEntityInstance( data ); - lazyInitializer.setImplementation( data.entityInstanceForNotify ); - registerLoadingEntity( data, data.entityInstanceForNotify ); - } - else { - data.entityInstanceForNotify = lazyInitializer.getImplementation(); - data.concreteDescriptor = session.getEntityPersister( null, data.entityInstanceForNotify ); - resolveEntityKey( data, lazyInitializer.getInternalIdentifier() ); - data.entityHolder = persistenceContext.getEntityHolder( data.entityKey ); - // Even though the lazyInitializer reports it is initialized, check if the entity holder reports initialized, - // because in a nested initialization scenario, this nested initializer must initialize the entity - data.setState( data.entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED ); - } - if ( identifierAssembler != null ) { - final Initializer initializer = identifierAssembler.getInitializer(); - if ( initializer != null ) { - initializer.resolveInstance( data.entityKey.getIdentifier(), rowProcessingState ); + else { + data.entityInstanceForNotify = lazyInitializer.getImplementation(); + data.concreteDescriptor = session.getEntityPersister( null, data.entityInstanceForNotify ); + resolveEntityKey( data, lazyInitializer.getInternalIdentifier() ); + data.entityHolder = persistenceContext.getEntityHolder( data.entityKey ); + // Even though the lazyInitializer reports it is initialized, check if the entity holder reports initialized, + // because in a nested initialization scenario, this nested initializer must initialize the entity + data.setState( data.entityHolder.isInitialized() ? State.INITIALIZED : State.RESOLVED ); + } + if ( identifierAssembler != null ) { + final Initializer initializer = identifierAssembler.getInitializer(); + if ( initializer != null ) { + initializer.resolveInstance( data.entityKey.getIdentifier(), rowProcessingState ); + } } - } - upgradeLockMode( data ); - if ( data.getState() == State.INITIALIZED ) { - registerReloadedEntity( data ); - resolveInstanceSubInitializers( data ); - if ( rowProcessingState.needsResolveState() ) { - // We need to read result set values to correctly populate the query cache - resolveEntityState( data ); + upgradeLockMode( data ); + if ( data.getState() == State.INITIALIZED ) { + registerReloadedEntity( data ); + resolveInstanceSubInitializers( data ); + if ( rowProcessingState.needsResolveState() ) { + // We need to read result set values to correctly populate the query cache + resolveEntityState( data ); + } + } + else { + resolveKeySubInitializers( data ); } - } - else { - resolveKeySubInitializers( data ); } } @Override public void resolveInstance(EntityInitializerData data) { - if ( data.getState() != State.KEY_RESOLVED ) { - return; - } - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - data.setState( State.RESOLVED ); - if ( data.entityKey == null ) { - assert identifierAssembler != null; - final Object id = identifierAssembler.assemble( rowProcessingState ); - if ( id == null ) { - setMissing( data ); - return; + if ( data.getState() == State.KEY_RESOLVED ) { + final var rowProcessingState = data.getRowProcessingState(); + data.setState( State.RESOLVED ); + if ( data.entityKey == null ) { + assert identifierAssembler != null; + final Object id = identifierAssembler.assemble( rowProcessingState ); + if ( id == null ) { + setMissing( data ); + return; + } + resolveEntityKey( data, id ); } - resolveEntityKey( data, id ); - } - data.entityHolder = - rowProcessingState.getSession().getPersistenceContextInternal() - .claimEntityHolderIfPossible( - data.entityKey, - null, - rowProcessingState.getJdbcValuesSourceProcessingState(), - this - ); + data.entityHolder = + rowProcessingState.getSession().getPersistenceContextInternal() + .claimEntityHolderIfPossible( + data.entityKey, + null, + rowProcessingState.getJdbcValuesSourceProcessingState(), + this + ); - if ( useEmbeddedIdentifierInstanceAsEntity( data ) ) { - data.setInstance( data.entityInstanceForNotify = rowProcessingState.getEntityId() ); - } - else { - resolveEntityInstance1( data ); - if ( data.uniqueKeyAttributePath != null ) { - final SharedSessionContractImplementor session = rowProcessingState.getSession(); - final EntityPersister concreteDescriptor = getConcreteDescriptor( data ); - final EntityUniqueKey euk = new EntityUniqueKey( - concreteDescriptor.getEntityName(), - data.uniqueKeyAttributePath, - rowProcessingState.getEntityUniqueKey(), - data.uniqueKeyPropertyTypes[concreteDescriptor.getSubclassId()], - session.getFactory() - ); - session.getPersistenceContextInternal().addEntity( euk, data.getInstance() ); + if ( useEmbeddedIdentifierInstanceAsEntity( data ) ) { + data.setInstance( data.entityInstanceForNotify = rowProcessingState.getEntityId() ); } - } - - if ( data.getInstance() != null ) { - upgradeLockMode( data ); - if ( data.getState() == State.INITIALIZED ) { - registerReloadedEntity( data ); - if ( rowProcessingState.needsResolveState() ) { - // We need to read result set values to correctly populate the query cache - resolveEntityState( data ); + else { + resolveEntityInstance1( data ); + if ( data.uniqueKeyAttributePath != null ) { + final var session = rowProcessingState.getSession(); + final var concreteDescriptor = getConcreteDescriptor( data ); + final var entityUniqueKey = new EntityUniqueKey( + concreteDescriptor.getEntityName(), + data.uniqueKeyAttributePath, + rowProcessingState.getEntityUniqueKey(), + data.uniqueKeyPropertyTypes[concreteDescriptor.getSubclassId()], + session.getFactory() + ); + session.getPersistenceContextInternal().addEntity( entityUniqueKey, data.getInstance() ); } } - if ( data.shallowCached ) { - initializeSubInstancesFromParent( data ); + + if ( data.getInstance() != null ) { + upgradeLockMode( data ); + if ( data.getState() == State.INITIALIZED ) { + registerReloadedEntity( data ); + if ( rowProcessingState.needsResolveState() ) { + // We need to read result set values to correctly populate the query cache + resolveEntityState( data ); + } + } + if ( data.shallowCached ) { + initializeSubInstancesFromParent( data ); + } } } } protected void resolveEntityInstance1(EntityInitializerData data) { - final Object proxy = data.entityHolder.getProxy(); + final var entityHolder = data.entityHolder; + final Object proxy = entityHolder.getProxy(); final boolean unwrapProxy = proxy != null && referencedModelPart instanceof ToOneAttributeMapping toOneAttributeMapping && toOneAttributeMapping.isUnwrapProxy() && getConcreteDescriptor( data ).getBytecodeEnhancementMetadata().isEnhancedForLazyLoading(); - final Object entityFromExecutionContext; if ( !unwrapProxy && isProxyInstance( proxy ) ) { - if ( ( entityFromExecutionContext = getEntityFromExecutionContext( data ) ) != null ) { - data.setInstance( data.entityInstanceForNotify = entityFromExecutionContext ); + final Object entityFromExecutionContext = getEntityFromExecutionContext( data ); + if ( entityFromExecutionContext != null ) { + data.entityInstanceForNotify = entityFromExecutionContext; + data.setInstance( entityFromExecutionContext ); // If the entity comes from the execution context, it is treated as not initialized // so that we can refresh the data as requested registerReloadedEntity( data ); @@ -1095,7 +1098,7 @@ protected void resolveEntityInstance1(EntityInitializerData data) { data.entityInstanceForNotify = Hibernate.unproxy( proxy ); } else { - final LazyInitializer lazyInitializer = extractLazyInitializer( proxy ); + final var lazyInitializer = extractLazyInitializer( proxy ); assert lazyInitializer != null; data.entityInstanceForNotify = resolveEntityInstance2( data ); lazyInitializer.setImplementation( data.entityInstanceForNotify ); @@ -1103,19 +1106,19 @@ protected void resolveEntityInstance1(EntityInitializerData data) { } } else { - final Object existingEntity = data.entityHolder.getEntity(); + final Object existingEntity = entityHolder.getEntity(); if ( existingEntity != null ) { data.setInstance( data.entityInstanceForNotify = existingEntity ); - if ( data.entityHolder.getEntityInitializer() == null ) { - assert data.entityHolder.isInitialized() == isExistingEntityInitialized( existingEntity ); - if ( data.entityHolder.isInitialized() ) { + if ( entityHolder.getEntityInitializer() == null ) { + assert entityHolder.isInitialized() == isExistingEntityInitialized( existingEntity ); + if ( entityHolder.isInitialized() ) { data.setState( State.INITIALIZED ); } else if ( isResultInitializer() ) { registerLoadingEntity( data, existingEntity ); } } - else if ( data.entityHolder.getEntityInitializer() != this ) { + else if ( entityHolder.getEntityInitializer() != this ) { data.setState( State.INITIALIZED ); } else if ( data.shallowCached ) { @@ -1123,27 +1126,35 @@ else if ( data.shallowCached ) { data.setInstance( data.entityInstanceForNotify = resolveEntityInstance( data ) ); } } - else if ( ( entityFromExecutionContext = getEntityFromExecutionContext( data ) ) != null ) { - // This is the entity to refresh, so don't set the state to initialized - data.setInstance( data.entityInstanceForNotify = entityFromExecutionContext ); - if ( isResultInitializer() ) { - registerLoadingEntity( data, entityFromExecutionContext ); - } - } else { - assert data.entityHolder.getEntityInitializer() == this; - // look to see if another initializer from a parent load context or an earlier - // initializer is already loading the entity - data.setInstance( data.entityInstanceForNotify = resolveEntityInstance2( data ) ); - final Initializer idInitializer; - if ( data.entityHolder.getEntityInitializer() == this && data.getState() != State.INITIALIZED - && identifierAssembler != null - && ( idInitializer = identifierAssembler.getInitializer() ) != null ) { - // If this is the owning initializer and the returned object is not initialized, - // this means that the entity instance was just instantiated. - // In this case, we want to call "assemble" and hence "initializeInstance" on the initializer - // for possibly non-aggregated identifier mappings, so inject the virtual id representation - idInitializer.initializeInstance( data.getRowProcessingState() ); + final Object entityFromExecutionContext = getEntityFromExecutionContext( data ); + if ( entityFromExecutionContext != null ) { + // This is the entity to refresh, so don't set the state to initialized + data.entityInstanceForNotify = entityFromExecutionContext; + data.setInstance( entityFromExecutionContext ); + if ( isResultInitializer() ) { + registerLoadingEntity( data, entityFromExecutionContext ); + } + } + else { + assert entityHolder.getEntityInitializer() == this; + // look to see if another initializer from a parent load context or an earlier + // initializer is already loading the entity + final Object resolvedEntityInstance = resolveEntityInstance2( data ); + data.entityInstanceForNotify = resolvedEntityInstance; + data.setInstance( resolvedEntityInstance ); + if ( entityHolder.getEntityInitializer() == this + && data.getState() != State.INITIALIZED + && identifierAssembler != null ) { + final var idInitializer = identifierAssembler.getInitializer(); + if ( idInitializer != null ) { + // If this is the owning initializer and the returned object is not initialized, + // this means that the entity instance was just instantiated. + // In this case, we want to call "assemble" and hence "initializeInstance" on the initializer + // for possibly non-aggregated identifier mappings, so inject the virtual id representation + idInitializer.initializeInstance( data.getRowProcessingState() ); + } + } } } } @@ -1152,29 +1163,30 @@ else if ( ( entityFromExecutionContext = getEntityFromExecutionContext( data ) ) } protected Object getEntityFromExecutionContext(EntityInitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final ExecutionContext executionContext = rowProcessingState.getJdbcValuesSourceProcessingState() - .getExecutionContext(); - if ( rootEntityDescriptor == executionContext.getRootEntityDescriptor() - && areKeysEqual( data.entityKey.getIdentifier(), executionContext.getEntityId() ) ) { - return executionContext.getEntityInstance(); - } - return null; + final var executionContext = + data.getRowProcessingState() + .getJdbcValuesSourceProcessingState() + .getExecutionContext(); + return rootEntityDescriptor == executionContext.getRootEntityDescriptor() + && areKeysEqual( data.entityKey.getIdentifier(), executionContext.getEntityId() ) + ? executionContext.getEntityInstance() + : null; } protected void upgradeLockMode(EntityInitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); if ( data.lockMode != LockMode.NONE && rowProcessingState.upgradeLocks() ) { - final EntityEntry entry = data.entityHolder.getEntityEntry(); - assert entry == rowProcessingState.getSession().getPersistenceContextInternal() - .getEntry( data.entityInstanceForNotify ); - if ( entry != null && entry.getLockMode().lessThan( data.lockMode ) ) { + final var entityEntry = data.entityHolder.getEntityEntry(); + assert entityEntry == + rowProcessingState.getSession().getPersistenceContextInternal() + .getEntry( data.entityInstanceForNotify ); + if ( entityEntry != null && entityEntry.getLockMode().lessThan( data.lockMode ) ) { //we only check the version when _upgrading_ lock modes - if ( versionAssembler != null && entry.getLockMode() != LockMode.NONE ) { - checkVersion( data, entry, rowProcessingState ); + if ( versionAssembler != null && entityEntry.getLockMode() != LockMode.NONE ) { + checkVersion( data, entityEntry, rowProcessingState ); } //we need to upgrade the lock mode to the mode requested - entry.setLockMode( data.lockMode ); + entityEntry.setLockMode( data.lockMode ); } } } @@ -1202,7 +1214,7 @@ private void checkVersion( // null version means the object is in the process of being loaded somewhere else in the ResultSet final Object currentVersion = versionAssembler.assemble( rowProcessingState ); if ( !data.concreteDescriptor.getVersionType().isEqual( version, currentVersion ) ) { - final StatisticsImplementor statistics = rowProcessingState.getSession().getFactory().getStatistics(); + final var statistics = rowProcessingState.getSession().getFactory().getStatistics(); if ( statistics.isStatisticsEnabled() ) { statistics.optimisticFailure( data.concreteDescriptor.getEntityName() ); } @@ -1228,7 +1240,7 @@ protected Object resolveEntityInstance2(EntityInitializerData data) { } protected Object resolveEntityInstance(EntityInitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); final Object resolved = resolveToOptionalInstance( data ); if ( resolved != null ) { registerLoadingEntity( data, resolved ); @@ -1238,12 +1250,15 @@ protected Object resolveEntityInstance(EntityInitializerData data) { if ( data.shallowCached ) { // We must load the entity this way, because the query cache entry contains only the primary key data.setState( State.INITIALIZED ); - final SharedSessionContractImplementor session = rowProcessingState.getSession(); + final var session = rowProcessingState.getSession(); assert data.entityHolder.getEntityInitializer() == this; // If this initializer owns the entity, we have to remove the entity holder, // because the subsequent loading process will claim the entity - rowProcessingState.getJdbcValuesSourceProcessingState().getLoadingEntityHolders().remove( data.entityHolder ); - session.getPersistenceContextInternal().removeEntityHolder( data.entityKey ); + rowProcessingState.getJdbcValuesSourceProcessingState() + .getLoadingEntityHolders() + .remove( data.entityHolder ); + session.getPersistenceContextInternal() + .removeEntityHolder( data.entityKey ); return session.internalLoad( data.concreteDescriptor.getEntityName(), data.entityKey.getIdentifier(), @@ -1275,8 +1290,10 @@ protected Object instantiateEntity(EntityInitializerData data) { private Object resolveToOptionalInstance(EntityInitializerData data) { if ( isResultInitializer() ) { // this isEntityReturn bit is just for entity loaders, not hql/criteria - final JdbcValuesSourceProcessingOptions processingOptions = - data.getRowProcessingState().getJdbcValuesSourceProcessingState().getProcessingOptions(); + final var processingOptions = + data.getRowProcessingState() + .getJdbcValuesSourceProcessingState() + .getProcessingOptions(); return matchesOptionalInstance( data, processingOptions ) ? processingOptions.getEffectiveOptionalObject() : null; @@ -1307,13 +1324,10 @@ private Object resolveInstanceFromCache(EntityInitializerData data) { } protected void registerLoadingEntity(EntityInitializerData data, Object instance) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - rowProcessingState.getSession().getPersistenceContextInternal().claimEntityHolderIfPossible( - data.entityKey, - instance, - rowProcessingState.getJdbcValuesSourceProcessingState(), - this - ); + final var rowProcessingState = data.getRowProcessingState(); + final var valuesSourceProcessingState = rowProcessingState.getJdbcValuesSourceProcessingState(); + rowProcessingState.getSession().getPersistenceContextInternal() + .claimEntityHolderIfPossible( data.entityKey, instance, valuesSourceProcessingState, this ); } protected void registerReloadedEntity(EntityInitializerData data) { @@ -1335,29 +1349,31 @@ public void initializeInstance(EntityInitializerData data) { } protected boolean consistentInstance(EntityInitializerData data) { - final PersistenceContext persistenceContextInternal = - data.getRowProcessingState().getSession().getPersistenceContextInternal(); // Only call PersistenceContext#getEntity within the assert expression, as it is costly - final Object entity = persistenceContextInternal.getEntity( data.entityKey ); + final Object entity = + data.getRowProcessingState().getSession() + .getPersistenceContextInternal() + .getEntity( data.entityKey ); return entity == null || entity == data.entityInstanceForNotify; } protected void initializeEntityInstance(EntityInitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final SharedSessionContractImplementor session = rowProcessingState.getSession(); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - final EntityKey entityKey = data.entityKey; + final var rowProcessingState = data.getRowProcessingState(); + final var session = rowProcessingState.getSession(); + final var persistenceContext = session.getPersistenceContextInternal(); + final var entityKey = data.entityKey; assert entityKey != null; final Object entityIdentifier = entityKey.getIdentifier(); - final Object[] resolvedEntityState = extractConcreteTypeStateValues( data ); + final var resolvedEntityState = extractConcreteTypeStateValues( data ); preLoad( data, resolvedEntityState ); final Object entityInstanceForNotify = data.entityInstanceForNotify; if ( isPersistentAttributeInterceptable( entityInstanceForNotify ) ) { - final PersistentAttributeInterceptor persistentAttributeInterceptor = - asPersistentAttributeInterceptable( entityInstanceForNotify ).$$_hibernate_getInterceptor(); + final var persistentAttributeInterceptor = + asPersistentAttributeInterceptable( entityInstanceForNotify ) + .$$_hibernate_getInterceptor(); if ( persistentAttributeInterceptor == null || persistentAttributeInterceptor instanceof EnhancementAsProxyLazinessInterceptor ) { // if we do this after the entity has been initialized the @@ -1376,29 +1392,20 @@ protected void initializeEntityInstance(EntityInitializerData data) { final Object version = versionAssembler != null ? versionAssembler.assemble( rowProcessingState ) : null; final Object rowId = rowIdAssembler != null ? rowIdAssembler.assemble( rowProcessingState ) : null; - // from the perspective of Hibernate, an entity is read locked as soon as it is read - // so regardless of the requested lock mode, we upgrade to at least the read level - final LockMode lockModeToAcquire; - if ( data.getRowProcessingState().isTransactionActive() ) { - lockModeToAcquire = data.lockMode == LockMode.NONE ? LockMode.READ : data.lockMode; - } - else { - // data read outside transaction is marked as unlocked - lockModeToAcquire = LockMode.NONE; - } + final var entityEntry = + persistenceContext.addEntry( + entityInstanceForNotify, + Status.LOADING, + resolvedEntityState, + rowId, + entityIdentifier, + version, + lockModeToAcquire( data ), + true, + data.concreteDescriptor, + false + ); - final EntityEntry entityEntry = persistenceContext.addEntry( - entityInstanceForNotify, - Status.LOADING, - resolvedEntityState, - rowId, - entityIdentifier, - version, - lockModeToAcquire, - true, - data.concreteDescriptor, - false - ); entityEntry.setMaybeLazySet( maybeLazySets[data.concreteDescriptor.getSubclassId()] ); data.entityHolder.setEntityEntry( entityEntry ); @@ -1411,7 +1418,7 @@ protected void initializeEntityInstance(EntityInitializerData data) { assert data.concreteDescriptor.getIdentifier( entityInstanceForNotify, session ) != null; - final StatisticsImplementor statistics = session.getFactory().getStatistics(); + final var statistics = session.getFactory().getStatistics(); if ( statistics.isStatisticsEnabled() ) { if ( !rowProcessingState.isQueryCacheHit() ) { statistics.loadEntity( data.concreteDescriptor.getEntityName() ); @@ -1426,6 +1433,18 @@ protected void initializeEntityInstance(EntityInitializerData data) { ); } + private static LockMode lockModeToAcquire(EntityInitializerData data) { + if ( data.getRowProcessingState().isTransactionActive() ) { + // from the perspective of Hibernate, an entity is read locked as soon as it is read + // so regardless of the requested lock mode, we upgrade to at least the read level + return data.lockMode == LockMode.NONE ? LockMode.READ : data.lockMode; + } + else { + // data read outside transaction is marked as unlocked + return LockMode.NONE; + } + } + protected void updateCaches( EntityInitializerData data, SharedSessionContractImplementor session, @@ -1436,7 +1455,7 @@ protected void updateCaches( // No need to put into the entity cache if this is coming from the query cache already && !data.getRowProcessingState().isQueryCacheHit() && session.getCacheMode().isPutEnabled() ) { - final EntityDataAccess cacheAccess = data.concreteDescriptor.getCacheAccessStrategy(); + final var cacheAccess = data.concreteDescriptor.getCacheAccessStrategy(); if ( cacheAccess != null ) { putInCache( data, session, persistenceContext, resolvedEntityState, version, cacheAccess ); } @@ -1480,15 +1499,12 @@ private boolean isReallyReadOnly(EntityInitializerData data, SharedSessionContra return true; } else { - final LazyInitializer lazyInitializer = extractLazyInitializer( data.getInstance() ); - if ( lazyInitializer != null ) { - // there is already a proxy for this impl - // only set the status to read-only if the proxy is read-only - return lazyInitializer.isReadOnly(); - } - else { - return isReadOnly( data.getRowProcessingState(), session ); - } + final var lazyInitializer = extractLazyInitializer( data.getInstance() ); + return lazyInitializer != null + // there is already a proxy for this impl + // only set the status to read-only if the proxy is read-only + ? lazyInitializer.isReadOnly() + : isReadOnly( data.getRowProcessingState(), session ); } } @@ -1499,20 +1515,22 @@ private void putInCache( Object[] resolvedEntityState, Object version, EntityDataAccess cacheAccess) { - final SessionFactoryImplementor factory = session.getFactory(); + final var factory = session.getFactory(); - final CacheEntry cacheEntry = data.concreteDescriptor.buildCacheEntry( - data.entityInstanceForNotify, - resolvedEntityState, - version, - session - ); - final Object cacheKey = cacheAccess.generateCacheKey( - data.entityKey.getIdentifier(), - rootEntityDescriptor, - factory, - session.getTenantIdentifier() - ); + final var cacheEntry = + data.concreteDescriptor.buildCacheEntry( + data.entityInstanceForNotify, + resolvedEntityState, + version, + session + ); + final Object cacheKey = + cacheAccess.generateCacheKey( + data.entityKey.getIdentifier(), + rootEntityDescriptor, + factory, + session.getTenantIdentifier() + ); // explicit handling of caching for rows just inserted and then somehow forced to be read // from the database *within the same transaction*. usually this is done by @@ -1520,10 +1538,10 @@ private void putInCache( // 2) Session#clear + some form of load // // we need to be careful not to clobber the lock here in the cache so that it can be rolled back if need be - final EventMonitor eventMonitor = session.getEventMonitor(); + final var eventMonitor = session.getEventMonitor(); if ( persistenceContext.wasInsertedDuringTransaction( data.concreteDescriptor, data.entityKey.getIdentifier() ) ) { boolean cacheContentChanged = false; - final DiagnosticEvent cachePutEvent = eventMonitor.beginCachePutEvent(); + final var cachePutEvent = eventMonitor.beginCachePutEvent(); try { // Updating the cache entry for entities that were inserted in this transaction // only makes sense for transactional caches. Other implementations no-op for #update @@ -1555,9 +1573,9 @@ private void putInCache( } } else { - final SessionEventListenerManager eventListenerManager = session.getEventListenerManager(); + final var eventListenerManager = session.getEventListenerManager(); boolean put = false; - final DiagnosticEvent cachePutEvent = eventMonitor.beginCachePutEvent(); + final var cachePutEvent = eventMonitor.beginCachePutEvent(); try { eventListenerManager.cachePutStart(); put = cacheAccess.putFromLoad( @@ -1578,7 +1596,7 @@ private void putInCache( put, EventMonitor.CacheActionDescription.ENTITY_LOAD ); - final StatisticsImplementor statistics = factory.getStatistics(); + final var statistics = factory.getStatistics(); if ( put && statistics.isStatisticsEnabled() ) { statistics.entityCachePut( rootEntityDescriptor.getNavigableRole(), cacheAccess.getRegion().getName() ); } @@ -1591,10 +1609,10 @@ protected void registerPossibleUniqueKeyEntries( EntityInitializerData data, Object[] resolvedEntityState, final SharedSessionContractImplementor session) { - for ( UniqueKeyEntry entry : data.concreteDescriptor.uniqueKeyEntries() ) { - final String ukName = entry.getUniqueKeyName(); - final int index = entry.getStateArrayPosition(); - final Type type = entry.getPropertyType(); + for ( var uniqueKeyEntry : data.concreteDescriptor.uniqueKeyEntries() ) { + final String uniqueKeyName = uniqueKeyEntry.getUniqueKeyName(); + final int index = uniqueKeyEntry.getStateArrayPosition(); + final Type type = uniqueKeyEntry.getPropertyType(); // polymorphism not really handled completely correctly, // perhaps...well, actually it's ok, assuming that the @@ -1613,14 +1631,15 @@ protected void registerPossibleUniqueKeyEntries( else { key = resolvedEntityState[index]; } - final EntityUniqueKey entityUniqueKey = new EntityUniqueKey( - data.concreteDescriptor.getRootEntityDescriptor().getEntityName(), - //polymorphism comment above - ukName, - key, - type, - session.getFactory() - ); + final var entityUniqueKey = + new EntityUniqueKey( + data.concreteDescriptor.getRootEntityDescriptor().getEntityName(), + //polymorphism comment above + uniqueKeyName, + key, + type, + session.getFactory() + ); session.getPersistenceContextInternal().addEntity( entityUniqueKey, data.entityInstanceForNotify ); } } @@ -1657,11 +1676,12 @@ public void resolveState(EntityInitializerData data) { if ( data.concreteDescriptor == null ) { data.concreteDescriptor = data.defaultConcreteDescriptor; if ( data.concreteDescriptor == null ) { - data.concreteDescriptor = determineConcreteEntityDescriptor( - data.getRowProcessingState(), - castNonNull( discriminatorAssembler ), - entityDescriptor - ); + data.concreteDescriptor = + determineConcreteEntityDescriptor( + data.getRowProcessingState(), + castNonNull( discriminatorAssembler ), + entityDescriptor + ); if ( data.concreteDescriptor == null ) { // this should imply the entity is missing return; @@ -1673,8 +1693,8 @@ public void resolveState(EntityInitializerData data) { protected void resolveEntityState(EntityInitializerData data) { assert data.concreteDescriptor != null; - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - for ( final DomainResultAssembler assembler : assemblers[data.concreteDescriptor.getSubclassId()] ) { + final var rowProcessingState = data.getRowProcessingState(); + for ( var assembler : assemblers[data.concreteDescriptor.getSubclassId()] ) { if ( assembler != null ) { assembler.resolveState( rowProcessingState ); } @@ -1685,37 +1705,42 @@ protected boolean skipInitialization(EntityInitializerData data) { if ( data.entityHolder.getEntityInitializer() != this ) { return true; } - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final EntityEntry entry = data.entityHolder.getEntityEntry(); - assert entry == rowProcessingState.getSession().getPersistenceContextInternal().getEntry( data.entityInstanceForNotify ); - if ( entry == null ) { - return false; - } - // todo (6.0): do we really need this check ? - else if ( entry.getStatus().isDeletedOrGone() ) { - return true; - } else { - if ( isPersistentAttributeInterceptable( data.entityInstanceForNotify ) ) { - final PersistentAttributeInterceptor interceptor = - asPersistentAttributeInterceptable( data.entityInstanceForNotify ).$$_hibernate_getInterceptor(); - if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) { - // Avoid loading the same entity proxy twice for the same result set: it could lead to errors, - // because some code writes to its input (ID in hydrated state replaced by the loaded entity, in particular). - return entry.getStatus() == Status.LOADING; - } + final var rowProcessingState = data.getRowProcessingState(); + final var entry = data.entityHolder.getEntityEntry(); + assert entry == + rowProcessingState.getSession().getPersistenceContextInternal() + .getEntry( data.entityInstanceForNotify ); + if ( entry == null ) { + return false; } + // todo (6.0): do we really need this check ? + else if ( entry.getStatus().isDeletedOrGone() ) { + return true; + } + else { + if ( isPersistentAttributeInterceptable( data.entityInstanceForNotify ) ) { + final var interceptor = + asPersistentAttributeInterceptable( data.entityInstanceForNotify ) + .$$_hibernate_getInterceptor(); + if ( interceptor instanceof EnhancementAsProxyLazinessInterceptor ) { + // Avoid loading the same entity proxy twice for the same result set: it could lead to errors, + // because some code writes to its input (ID in hydrated state replaced by the loaded entity, in particular). + return entry.getStatus() == Status.LOADING; + } + } - // If the instance to initialize is the main entity, we can't skip this. - // This can happen if we initialize an enhanced proxy. - if ( entry.getStatus() != Status.LOADING ) { // If the instance to initialize is the main entity, we can't skip this. // This can happen if we initialize an enhanced proxy. - return rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions() - .getEffectiveOptionalObject() != data.entityInstanceForNotify; - } - else { - return false; + if ( entry.getStatus() != Status.LOADING ) { + // If the instance to initialize is the main entity, we can't skip this. + // This can happen if we initialize an enhanced proxy. + return rowProcessingState.getJdbcValuesSourceProcessingState().getProcessingOptions() + .getEffectiveOptionalObject() != data.entityInstanceForNotify; + } + else { + return false; + } } } } @@ -1726,10 +1751,12 @@ private boolean isReadOnly(RowProcessingState rowProcessingState, SharedSessionC } protected void preLoad(EntityInitializerData data, Object[] resolvedEntityState) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final SharedSessionContractImplementor session = rowProcessingState.getSession(); + final var rowProcessingState = data.getRowProcessingState(); + final var session = rowProcessingState.getSession(); if ( session.isEventSource() ) { - final PreLoadEvent preLoadEvent = rowProcessingState.getJdbcValuesSourceProcessingState().getPreLoadEvent(); + final var preLoadEvent = + rowProcessingState.getJdbcValuesSourceProcessingState() + .getPreLoadEvent(); assert preLoadEvent != null; preLoadEvent.reset(); @@ -1773,8 +1800,8 @@ public EntityPersister getConcreteDescriptor(EntityInitializerData data) { protected void initializeSubInstancesFromParent(EntityInitializerData data) { if ( data.entityInstanceForNotify != null ) { - for ( Initializer initializer : subInitializers[data.concreteDescriptor.getSubclassId()] ) { - if (initializer != null) { + for ( var initializer : subInitializers[data.concreteDescriptor.getSubclassId()] ) { + if ( initializer != null ) { initializer.initializeInstanceFromParent( data.entityInstanceForNotify, data.getRowProcessingState() ); } } @@ -1783,29 +1810,29 @@ protected void initializeSubInstancesFromParent(EntityInitializerData data) { @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); + final var rowProcessingState = data.getRowProcessingState(); if ( keyAssembler != null ) { - final Initializer initializer = keyAssembler.getInitializer(); + final var initializer = keyAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, rowProcessingState ); } } if ( identifierAssembler != null ) { - final Initializer initializer = identifierAssembler.getInitializer(); + final var initializer = identifierAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, rowProcessingState ); } } - final EntityInitializerData entityInitializerData = (EntityInitializerData) data; + final var entityInitializerData = (EntityInitializerData) data; if ( entityInitializerData.concreteDescriptor == null ) { - for ( Initializer initializer : allInitializers ) { + for ( var initializer : allInitializers ) { if ( initializer != null ) { consumer.accept( initializer, rowProcessingState ); } } } else { - for ( Initializer initializer : subInitializers[entityInitializerData.concreteDescriptor.getSubclassId()] ) { + for ( var initializer : subInitializers[entityInitializerData.concreteDescriptor.getSubclassId()] ) { if ( initializer != null ) { consumer.accept( initializer, rowProcessingState ); } @@ -1819,7 +1846,8 @@ public static PersistentAttributeInterceptor getAttributeInterceptor(Object enti @Override public String toString() { - return "EntityJoinedFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "EntityJoinedFetchInitializer(" + + toLoggableString( getNavigablePath() ) + ")"; } //######################### diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java index 4034a824c4a9..86b70a4f92a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchByUniqueKeyInitializer.java @@ -4,13 +4,7 @@ */ package org.hibernate.sql.results.graph.entity.internal; -import org.hibernate.EntityFilterException; -import org.hibernate.FetchNotFoundException; -import org.hibernate.annotations.NotFoundAction; import org.hibernate.engine.spi.EntityUniqueKey; -import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.log.LoggingHelper; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.spi.NavigablePath; @@ -18,10 +12,13 @@ import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.InitializerParent; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; + /** * @author Andrea Boriero */ -public class EntitySelectFetchByUniqueKeyInitializer extends EntitySelectFetchInitializer { +public class EntitySelectFetchByUniqueKeyInitializer + extends EntitySelectFetchInitializer { private final ToOneAttributeMapping fetchedAttribute; public EntitySelectFetchByUniqueKeyInitializer( @@ -41,42 +38,28 @@ protected void initialize(EntitySelectFetchInitializerData data) { final String entityName = concreteDescriptor.getEntityName(); final String uniqueKeyPropertyName = fetchedAttribute.getReferencedPropertyName(); - final SharedSessionContractImplementor session = data.getRowProcessingState().getSession(); + final var session = data.getRowProcessingState().getSession(); + final var persistenceContext = session.getPersistenceContextInternal(); - final EntityUniqueKey euk = new EntityUniqueKey( - entityName, - uniqueKeyPropertyName, - data.entityIdentifier, - concreteDescriptor.getPropertyType( uniqueKeyPropertyName ), - session.getFactory() - ); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - data.setInstance( persistenceContext.getEntity( euk ) ); + final var entityUniqueKey = + new EntityUniqueKey( + entityName, + uniqueKeyPropertyName, + data.entityIdentifier, + concreteDescriptor.getPropertyType( uniqueKeyPropertyName ), + session.getFactory() + ); + data.setInstance( persistenceContext.getEntity( entityUniqueKey ) ); if ( data.getInstance() == null ) { - final Object instance = concreteDescriptor.loadByUniqueKey( - uniqueKeyPropertyName, - data.entityIdentifier, - session - ); + final Object instance = + concreteDescriptor.loadByUniqueKey( uniqueKeyPropertyName, data.entityIdentifier, session ); data.setInstance( instance ); - if ( instance == null ) { - if ( toOneMapping.getNotFoundAction() != NotFoundAction.IGNORE ) { - if ( affectedByFilter ) { - throw new EntityFilterException( - entityName, - data.entityIdentifier, - toOneMapping.getNavigableRole().getFullPath() - ); - } - if ( toOneMapping.getNotFoundAction() == NotFoundAction.EXCEPTION ) { - throw new FetchNotFoundException( entityName, data.entityIdentifier ); - } - } + checkNotFound( data ); } - // If the entity was not in the Persistence Context, but was found now, - // add it to the Persistence Context - persistenceContext.addEntity( euk, instance ); + // If the entity was not in the persistence context but was found now, + // then add it to the persistence context + persistenceContext.addEntity( entityUniqueKey, instance ); } if ( data.getInstance() != null ) { data.setInstance( persistenceContext.proxyFor( data.getInstance() ) ); @@ -85,6 +68,7 @@ protected void initialize(EntitySelectFetchInitializerData data) { @Override public String toString() { - return "EntitySelectFetchByUniqueKeyInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "EntitySelectFetchByUniqueKeyInitializer(" + + toLoggableString( getNavigablePath() ) + ")"; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java index b7fc9523439a..f714c945312f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntitySelectFetchInitializer.java @@ -10,17 +10,10 @@ import org.hibernate.FetchNotFoundException; import org.hibernate.Hibernate; import org.hibernate.annotations.NotFoundAction; -import org.hibernate.engine.spi.EntityHolder; import org.hibernate.engine.spi.EntityKey; -import org.hibernate.engine.spi.PersistenceContext; -import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.internal.log.LoggingHelper; -import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.proxy.HibernateProxy; -import org.hibernate.proxy.LazyInitializer; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; @@ -34,6 +27,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; +import static org.hibernate.internal.log.LoggingHelper.toLoggableString; import static org.hibernate.proxy.HibernateProxy.extractLazyInitializer; /** @@ -82,20 +76,15 @@ public EntitySelectFetchInitializer( this.parent = parent; this.toOneMapping = toOneMapping; this.navigablePath = fetchedNavigable; - this.isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parent ); this.concreteDescriptor = concreteDescriptor; - this.keyAssembler = keyResult.createResultAssembler( this, creationState ); - this.isEnhancedForLazyLoading = concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading(); this.affectedByFilter = affectedByFilter; - final Initializer initializer = keyAssembler.getInitializer(); - if ( initializer == null ) { - this.keyIsEager = false; - this.hasLazySubInitializer = false; - } - else { - this.keyIsEager = initializer.isEager(); - this.hasLazySubInitializer = !initializer.isEager() || initializer.hasLazySubInitializers(); - } + + isPartOfKey = Initializer.isPartOfKey( fetchedNavigable, parent ); + keyAssembler = keyResult.createResultAssembler( this, creationState ); + isEnhancedForLazyLoading = concreteDescriptor.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading(); + + keyIsEager = keyAssembler.isEager(); + hasLazySubInitializer = keyAssembler.hasLazySubInitializers(); } @Override @@ -124,7 +113,7 @@ public void resolveFromPreviousRow(Data data) { data.setState( State.MISSING ); } else { - final Initializer initializer = keyAssembler.getInitializer(); + final var initializer = keyAssembler.getInitializer(); if ( initializer != null ) { initializer.resolveFromPreviousRow( data.getRowProcessingState() ); } @@ -135,20 +124,19 @@ public void resolveFromPreviousRow(Data data) { @Override public void resolveInstance(Data data) { - if ( data.getState() != State.KEY_RESOLVED ) { - return; - } - - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - data.entityIdentifier = keyAssembler.assemble( rowProcessingState ); - - if ( data.entityIdentifier == null ) { - data.setState( State.MISSING ); - data.setInstance( null ); - return; + if ( data.getState() == State.KEY_RESOLVED ) { + final var rowProcessingState = data.getRowProcessingState(); + final Object identifier = keyAssembler.assemble( rowProcessingState ); + data.entityIdentifier = identifier; + if ( identifier == null ) { + data.setState( State.MISSING ); + data.setInstance( null ); + } + else { + data.setState( State.INITIALIZED ); + initialize( data ); + } } - data.setState( State.INITIALIZED ); - initialize( data ); } @Override @@ -159,8 +147,8 @@ public void resolveInstance(Object instance, Data data) { data.setInstance( null ); } else { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final LazyInitializer lazyInitializer = extractLazyInitializer( data.getInstance() ); + final var rowProcessingState = data.getRowProcessingState(); + final var lazyInitializer = extractLazyInitializer( data.getInstance() ); if ( lazyInitializer == null ) { data.setState( State.INITIALIZED ); if ( keyIsEager ) { @@ -181,7 +169,7 @@ else if ( lazyInitializer.isUninitialized() ) { } data.setInstance( instance ); if ( keyIsEager ) { - final Initializer initializer = keyAssembler.getInitializer(); + final var initializer = keyAssembler.getInitializer(); assert initializer != null; initializer.resolveInstance( data.entityIdentifier, rowProcessingState ); } @@ -194,20 +182,19 @@ else if ( rowProcessingState.needsResolveState() ) { @Override public void initializeInstance(Data data) { - if ( data.getState() != State.RESOLVED ) { - return; + if ( data.getState() == State.RESOLVED ) { + data.setState( State.INITIALIZED ); + Hibernate.initialize( data.getInstance() ); } - data.setState( State.INITIALIZED ); - Hibernate.initialize( data.getInstance() ); } protected void initialize(EntitySelectFetchInitializerData data) { - final RowProcessingState rowProcessingState = data.getRowProcessingState(); - final SharedSessionContractImplementor session = rowProcessingState.getSession(); - final EntityKey entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor ); + final var rowProcessingState = data.getRowProcessingState(); + final var session = rowProcessingState.getSession(); + final var entityKey = new EntityKey( data.entityIdentifier, concreteDescriptor ); - final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); - final EntityHolder holder = persistenceContext.getEntityHolder( entityKey ); + final var persistenceContext = session.getPersistenceContextInternal(); + final var holder = persistenceContext.getEntityHolder( entityKey ); if ( holder != null ) { data.setInstance( persistenceContext.proxyFor( holder, concreteDescriptor ) ); if ( holder.getEntityInitializer() == null ) { @@ -240,18 +227,7 @@ else if ( data.getInstance() == null ) { data.setInstance( instance ); if ( instance == null ) { - if ( toOneMapping.getNotFoundAction() != NotFoundAction.IGNORE ) { - if ( affectedByFilter ) { - throw new EntityFilterException( - entityName, - data.entityIdentifier, - toOneMapping.getNavigableRole().getFullPath() - ); - } - if ( toOneMapping.getNotFoundAction() == NotFoundAction.EXCEPTION ) { - throw new FetchNotFoundException( entityName, data.entityIdentifier ); - } - } + checkNotFound( data ); persistenceContext.claimEntityHolderIfPossible( new EntityKey( data.entityIdentifier, concreteDescriptor ), instance, @@ -261,18 +237,41 @@ else if ( data.getInstance() == null ) { } final boolean unwrapProxy = toOneMapping.isUnwrapProxy() && isEnhancedForLazyLoading; - final LazyInitializer lazyInitializer = HibernateProxy.extractLazyInitializer( data.getInstance() ); + final var lazyInitializer = extractLazyInitializer( data.getInstance() ); if ( lazyInitializer != null ) { lazyInitializer.setUnwrap( unwrapProxy ); } } + void checkNotFound(EntitySelectFetchInitializerData data) { + checkNotFound( toOneMapping, affectedByFilter, + concreteDescriptor.getEntityName(), + data.entityIdentifier ); + } + + static void checkNotFound( + ToOneAttributeMapping toOneMapping, + boolean affectedByFilter, + String entityName, Object identifier) { + final var notFoundAction = toOneMapping.getNotFoundAction(); + if ( notFoundAction != NotFoundAction.IGNORE ) { + if ( affectedByFilter ) { + throw new EntityFilterException( entityName, identifier, + toOneMapping.getNavigableRole().getFullPath() ); + } + if ( notFoundAction == NotFoundAction.EXCEPTION ) { + throw new FetchNotFoundException( entityName, identifier ); + } + } + } + @Override public void initializeInstanceFromParent(Object parentInstance, Data data) { - final AttributeMapping attributeMapping = getInitializedPart().asAttributeMapping(); - final Object instance = attributeMapping != null - ? attributeMapping.getValue( parentInstance ) - : parentInstance; + final var attributeMapping = getInitializedPart().asAttributeMapping(); + final Object instance = + attributeMapping != null + ? attributeMapping.getValue( parentInstance ) + : parentInstance; if ( instance == null ) { data.setState( State.MISSING ); data.entityIdentifier = null; @@ -289,7 +288,7 @@ public void initializeInstanceFromParent(Object parentInstance, Data data) { @Override protected void forEachSubInitializer(BiConsumer, RowProcessingState> consumer, InitializerData data) { - final Initializer initializer = keyAssembler.getInitializer(); + final var initializer = keyAssembler.getInitializer(); if ( initializer != null ) { consumer.accept( initializer, data.getRowProcessingState() ); } @@ -337,7 +336,8 @@ public boolean isResultInitializer() { @Override public String toString() { - return "EntitySelectFetchInitializer(" + LoggingHelper.toLoggableString( getNavigablePath() ) + ")"; + return "EntitySelectFetchInitializer(" + + toLoggableString( getNavigablePath() ) + ")"; } public DomainResultAssembler getKeyAssembler() { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/internal/AbstractInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/internal/AbstractInitializer.java index 8692e87010b0..131f7e1c9182 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/internal/AbstractInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/internal/AbstractInitializer.java @@ -21,7 +21,7 @@ protected AbstractInitializer(AssemblerCreationState creationState) { @Override public void startLoading(RowProcessingState rowProcessingState) { - final InitializerData data = createInitializerData( rowProcessingState ); + final var data = createInitializerData( rowProcessingState ); rowProcessingState.setInitializerData( initializerId, data ); forEachSubInitializer( Initializer::startLoading, data ); } @@ -52,5 +52,4 @@ public void finishUpRow(Data data) { protected abstract void forEachSubInitializer( BiConsumer, RowProcessingState> consumer, InitializerData data); - } From ace7664c45902d76d626929ac5809fc97cbd5beb Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Mon, 29 Sep 2025 15:01:14 +0200 Subject: [PATCH 08/12] Apply session options to enable proper locking for CockroachDB This fixes issues with ScopeTests when running against CockroachDB. Also remove test skips that were due to locking not working correctly. See https://github.com/cockroachdb/cockroach/issues/88995 for details --- .../dialect/lock/internal/CockroachLockingSupport.java | 2 ++ .../hibernate/orm/test/jpa/lock/LockExceptionTests.java | 1 - .../java/org/hibernate/orm/test/jpa/lock/LockTest.java | 6 ------ .../jpa/lock/StatementIsClosedAfterALockExceptionTest.java | 2 -- .../java/org/hibernate/orm/test/locking/LockModeTest.java | 4 ---- .../hibernate/testing/orm/transaction/TransactionUtil.java | 7 +++++-- local-build-plugins/src/main/groovy/local.databases.gradle | 3 ++- 7 files changed, 9 insertions(+), 16 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/CockroachLockingSupport.java b/hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/CockroachLockingSupport.java index 4db473322ad2..1803bbd23bdc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/CockroachLockingSupport.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/lock/internal/CockroachLockingSupport.java @@ -55,6 +55,8 @@ public LockTimeoutType getLockTimeoutType(Timeout timeout) { return switch (timeout.milliseconds()) { case WAIT_FOREVER_MILLI -> QUERY; case NO_WAIT_MILLI -> supportsNoWait ? QUERY : LockTimeoutType.NONE; + // Due to https://github.com/cockroachdb/cockroach/issues/88995, locking doesn't work properly + // without certain configuration options and hence skipping locked rows also doesn't work correctly. case SKIP_LOCKED_MILLI -> LockTimeoutType.NONE; // it does not, however, support WAIT as part of for-update, but does support a connection-level lock_timeout setting default -> CONNECTION; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java index ba6ed85fbcb0..85ccd2f50e29 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockExceptionTests.java @@ -53,7 +53,6 @@ protected void tearDown() { @Test @JiraKey( value = "HHH-8786" ) - @SkipForDialect(dialectClass = CockroachDialect.class, reason = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(dialectClass = InformixDialect.class, reason = "no failure") public void testLockTimeoutFind() { final Item item = new Item( "find" ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java index ec004f531f9c..c8e6e9f191d3 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/LockTest.java @@ -107,7 +107,6 @@ public void testFindWithTimeoutHint() { @RequiresDialectFeature( value = DialectChecks.SupportsLockTimeouts.class, comment = "Test verifies proper exception throwing when a lock timeout is specified.", jiraKey = "HHH-7252" ) - @SkipForDialect(value = CockroachDialect.class, comment = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(value = AltibaseDialect.class, comment = "Altibase close socket after lock timeout occurred") public void testFindWithPessimisticWriteLockTimeoutException() { Lock lock = new Lock(); @@ -157,7 +156,6 @@ public void testFindWithPessimisticWriteLockTimeoutException() { @RequiresDialectFeature( value = DialectChecks.SupportsLockTimeouts.class, comment = "Test verifies proper exception throwing when a lock timeout is specified for Query#getSingleResult.", jiraKey = "HHH-13364" ) - @SkipForDialect(value = CockroachDialect.class, comment = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(value = AltibaseDialect.class, comment = "Altibase close socket after lock timeout occurred") public void testQuerySingleResultPessimisticWriteLockTimeoutException() { Lock lock = new Lock(); @@ -204,7 +202,6 @@ public void testQuerySingleResultPessimisticWriteLockTimeoutException() { @RequiresDialectFeature( value = DialectChecks.SupportsLockTimeouts.class, comment = "Test verifies proper exception throwing when a lock timeout is specified for Query#getResultList.", jiraKey = "HHH-13364" ) - @SkipForDialect(value = CockroachDialect.class, comment = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(value = AltibaseDialect.class, comment = "Altibase close socket after lock timeout occurred") public void testQueryResultListPessimisticWriteLockTimeoutException() { Lock lock = new Lock(); @@ -254,7 +251,6 @@ public void testQueryResultListPessimisticWriteLockTimeoutException() { @RequiresDialectFeature( value = DialectChecks.SupportsLockTimeouts.class, comment = "Test verifies proper exception throwing when a lock timeout is specified for NamedQuery#getResultList.", jiraKey = "HHH-13364" ) - @SkipForDialect(value = CockroachDialect.class, comment = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(value = AltibaseDialect.class, comment = "Altibase close socket after lock timeout occurred") public void testNamedQueryResultListPessimisticWriteLockTimeoutException() { Lock lock = new Lock(); @@ -298,7 +294,6 @@ public void testNamedQueryResultListPessimisticWriteLockTimeoutException() { @Test @RequiresDialectFeature( value = DialectChecks.SupportsSkipLocked.class ) - @SkipForDialect(value = CockroachDialect.class, comment = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") public void testUpdateWithPessimisticReadLockSkipLocked() { Lock lock = new Lock(); lock.setName( "name" ); @@ -343,7 +338,6 @@ public void testUpdateWithPessimisticReadLockSkipLocked() { @Test @RequiresDialectFeature(value = DialectChecks.SupportsLockTimeouts.class) - @SkipForDialect(value = CockroachDialect.class, comment = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") public void testUpdateWithPessimisticReadLockWithoutNoWait() { Lock lock = new Lock(); lock.setName( "name" ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/StatementIsClosedAfterALockExceptionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/StatementIsClosedAfterALockExceptionTest.java index 1fafd6166559..8baa34f13999 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/StatementIsClosedAfterALockExceptionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/lock/StatementIsClosedAfterALockExceptionTest.java @@ -14,7 +14,6 @@ import org.hibernate.cfg.AvailableSettings; import org.hibernate.community.dialect.AltibaseDialect; import org.hibernate.community.dialect.InformixDialect; -import org.hibernate.dialect.CockroachDialect; import org.hibernate.orm.test.jpa.BaseEntityManagerFunctionalTestCase; import org.hibernate.testing.orm.jdbc.PreparedStatementSpyConnectionProvider; import org.hibernate.testing.DialectChecks; @@ -35,7 +34,6 @@ * @author Andrea Boriero */ @RequiresDialectFeature({DialectChecks.SupportsLockTimeouts.class}) -@SkipForDialect(value = CockroachDialect.class, comment = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(value = AltibaseDialect.class, comment = "Altibase does not close Statement after lock timeout") public class StatementIsClosedAfterALockExceptionTest extends BaseEntityManagerFunctionalTestCase { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java index a7ab40e814cc..43d3ff9919fd 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/locking/LockModeTest.java @@ -87,7 +87,6 @@ public void prepareTest() throws Exception { @Test @RequiresDialectFeature( feature = DialectFeatureChecks.SupportsLockTimeouts.class ) - @SkipForDialect(dialectClass = CockroachDialect.class, reason = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Can't commit transaction because Altibase closes socket after lock timeout") public void testLoading() { // open a session, begin a transaction and lock row @@ -104,7 +103,6 @@ public void testLoading() { @Test @RequiresDialectFeature( feature = DialectFeatureChecks.SupportsLockTimeouts.class ) - @SkipForDialect(dialectClass = CockroachDialect.class, reason = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Can't commit transaction because Altibase closes socket after lock timeout") public void testCriteria() { // open a session, begin a transaction and lock row @@ -128,7 +126,6 @@ public void testCriteria() { @Test @RequiresDialectFeature( feature = DialectFeatureChecks.SupportsLockTimeouts.class ) - @SkipForDialect(dialectClass = CockroachDialect.class, reason = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Can't commit transaction because Altibase closes socket after lock timeout") public void testCriteriaAliasSpecific() { // open a session, begin a transaction and lock row @@ -154,7 +151,6 @@ public void testCriteriaAliasSpecific() { @Test @RequiresDialectFeature( feature = DialectFeatureChecks.SupportsLockTimeouts.class ) - @SkipForDialect(dialectClass = CockroachDialect.class, reason = "for update clause does not imply locking. See https://github.com/cockroachdb/cockroach/issues/88995") @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Can't commit transaction because Altibase closes socket after lock timeout") public void testQuery() { // open a session, begin a transaction and lock row diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/orm/transaction/TransactionUtil.java b/hibernate-testing/src/main/java/org/hibernate/testing/orm/transaction/TransactionUtil.java index 7634fac22af4..c243b3c21656 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/orm/transaction/TransactionUtil.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/orm/transaction/TransactionUtil.java @@ -10,6 +10,7 @@ import java.util.function.Function; import jakarta.persistence.EntityManager; +import jakarta.persistence.QueryTimeoutException; import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.SharedSessionContract; @@ -172,7 +173,8 @@ public static void deleteRow(SessionFactoryScope factoryScope, String tableName, } catch (RuntimeException re) { if ( re.getCause() instanceof jakarta.persistence.LockTimeoutException - || re.getCause() instanceof org.hibernate.exception.LockTimeoutException ) { + || re.getCause() instanceof org.hibernate.exception.LockTimeoutException + || re.getCause() instanceof QueryTimeoutException ) { if ( !expectingToBlock ) { fail( "Expecting update to " + tableName + " to succeed, but failed due to async timeout (presumably due to locks)", re.getCause() ); } @@ -227,7 +229,8 @@ else if ( !expectingToBlock && resultSize == 0 ) { } catch (RuntimeException re) { if ( re.getCause() instanceof jakarta.persistence.LockTimeoutException - || re.getCause() instanceof org.hibernate.exception.LockTimeoutException ) { + || re.getCause() instanceof org.hibernate.exception.LockTimeoutException + || re.getCause() instanceof QueryTimeoutException ) { if ( !expectingToBlock ) { fail( "Expecting update to " + tableName + " to succeed, but failed due to async timeout (presumably due to locks)", re.getCause() ); } diff --git a/local-build-plugins/src/main/groovy/local.databases.gradle b/local-build-plugins/src/main/groovy/local.databases.gradle index 49fbf19502db..4e6702baa3a2 100644 --- a/local-build-plugins/src/main/groovy/local.databases.gradle +++ b/local-build-plugins/src/main/groovy/local.databases.gradle @@ -371,7 +371,8 @@ ext { 'jdbc.url' : 'jdbc:postgresql://' + dbHost + ':26257/defaultdb?sslmode=disable&preparedStatementCacheQueries=0&escapeSyntaxCallMode=callIfNoReturn', 'jdbc.datasource' : 'org.postgresql.Driver', // 'jdbc.datasource' : 'org.postgresql.ds.PGSimpleDataSource', - 'connection.init_sql' : '' + // Configure the for-update clause to use proper locks: https://github.com/cockroachdb/cockroach/issues/88995 + 'connection.init_sql' : 'set enable_durable_locking_for_serializable = on; set optimizer_use_lock_op_for_serializable = on;' ], firebird : [ 'db.dialect' : 'org.hibernate.community.dialect.FirebirdDialect', From 5d6a874be50592b27e5a412f76967d253b2ed5c7 Mon Sep 17 00:00:00 2001 From: loic_lefevre Date: Mon, 29 Sep 2025 15:18:25 +0200 Subject: [PATCH 09/12] HHH-99999 - Upgrade OTP GitHub Action to v1.0.20 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfe3eea5ab9a..06350b10ca61 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -210,7 +210,7 @@ jobs: ${{ steps.cache-key.outputs.buildtool-monthly-cache-key }}- - id: create_database - uses: loiclefevre/test@03ce1d1ee2313b45249e7bf6b84dc0f4333cdd77 # v1.0.18 + uses: loiclefevre/test@ce2f5049188a384c17ffcfcb8c8d04cf118e2cd7 # v1.0.20 with: oci-service: ${{ matrix.rdbms }} action: create @@ -231,7 +231,7 @@ jobs: run: ./ci/build-github.sh shell: bash - - uses: loiclefevre/test@03ce1d1ee2313b45249e7bf6b84dc0f4333cdd77 # v1.0.18 + - uses: loiclefevre/test@ce2f5049188a384c17ffcfcb8c8d04cf118e2cd7 # v1.0.20 if: always() with: oci-service: ${{ matrix.rdbms }} From e54f0b0d1ef71c484385e4f72624dee493e9c891 Mon Sep 17 00:00:00 2001 From: loic_lefevre Date: Mon, 29 Sep 2025 15:24:28 +0200 Subject: [PATCH 10/12] HHH-99999 - Provide OTP stats in README.adoc --- README.adoc | 1 + 1 file changed, 1 insertion(+) diff --git a/README.adoc b/README.adoc index f37bcf0249bc..cc38a7d3069a 100644 --- a/README.adoc +++ b/README.adoc @@ -4,6 +4,7 @@ image:https://img.shields.io/maven-central/v/org.hibernate.orm/hibernate-core.sv image:https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fci.hibernate.org%2Fjob%2Fhibernate-orm-pipeline%2Fjob%2Fmain%2F&style=for-the-badge[Build Status,link=https://ci.hibernate.org/job/hibernate-orm-pipeline/job/main/] image:https://img.shields.io/badge/Revved%20up%20by-Develocity-06A0CE?style=for-the-badge&logo=gradle[Develocity,link=https://develocity.commonhaus.dev/scans?search.rootProjectNames=Hibernate%20ORM] image:https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/jvm-repo-rebuild/reproducible-central/master/content/org/hibernate/orm/hibernate-core/badge.json&style=for-the-badge[Reproducible Builds,link=https://github.com/jvm-repo-rebuild/reproducible-central/blob/master/content/org/hibernate/orm/hibernate-core/README.md] +image:https://testpilot.oracle.com/ords/testpilot/badges/github/hibernate/hibernate-orm[Oracle Test Pilot,link=https://testpilot.oracle.com/] Hibernate ORM is a powerful object/relational mapping solution for Java, and makes it easy to develop persistence logic for applications, libraries, and frameworks. From 60b79969b86382ee2ea44a437ab49a39ef07f37b Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 4 Sep 2025 14:45:37 +0200 Subject: [PATCH 11/12] HHH-19752 Add test for issue --- .../unit/lockhint/MySQLStorageEngineTest.java | 78 ++++++++++++++++--- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java index f1a3da96e901..12907d59f07f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/dialect/unit/lockhint/MySQLStorageEngineTest.java @@ -4,26 +4,37 @@ */ package org.hibernate.orm.test.dialect.unit.lockhint; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.Environment; +import org.hibernate.dialect.DatabaseVersion; +import org.hibernate.dialect.Dialect; import org.hibernate.dialect.MySQLDialect; - -import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.hibernate.orm.test.dialect.resolver.TestingDialectResolutionInfo; +import org.hibernate.testing.orm.junit.BaseUnitTest; +import org.hibernate.testing.orm.junit.DomainModel; import org.hibernate.testing.orm.junit.RequiresDialect; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; import java.util.Properties; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + @RequiresDialect(MySQLDialect.class) -public class MySQLStorageEngineTest extends BaseUnitTestCase { +@BaseUnitTest +public class MySQLStorageEngineTest { @Test public void testDefaultStorage() { - assertEquals( " engine=InnoDB", new MySQLDialect().getTableTypeString() ); + assertThat( new MySQLDialect().getTableTypeString() ).isEqualTo( " engine=InnoDB" ); } @Test @@ -31,10 +42,49 @@ public void testOverrideStorage() throws NoSuchFieldException, IllegalAccessExce final Field globalPropertiesField = Environment.class.getDeclaredField( "GLOBAL_PROPERTIES" ); globalPropertiesField.setAccessible( true ); final Properties systemProperties = (Properties) globalPropertiesField.get( null ); - assertNotNull( systemProperties ); + assertThat( systemProperties ).isNotNull(); + final Object previousValue = systemProperties.setProperty( AvailableSettings.STORAGE_ENGINE, "myisam" ); + try { + assertThat( new MySQLDialect().getTableTypeString() ).isEqualTo( " engine=MyISAM" ); + } + finally { + if ( previousValue != null ) { + systemProperties.setProperty( AvailableSettings.STORAGE_ENGINE, previousValue.toString() ); + } + else { + systemProperties.remove( AvailableSettings.STORAGE_ENGINE ); + } + } + } + + @Test + @SessionFactory + @ServiceRegistry(settings = {@Setting(name = AvailableSettings.STORAGE_ENGINE, value = "myisam")}) + @DomainModel(annotatedClasses = {TestEntity.class}) + public void testOverrideStorageWithConfigurationProperties(SessionFactoryScope scope) { + Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect(); + assertThat( dialect.getTableTypeString() ).isEqualTo( " engine=MyISAM" ); + } + + @Test + public void testOverrideStorageEngineConfigurationPropertyHasPrecedenceOverSystemProperty() + throws Exception { + final Field globalPropertiesField = Environment.class.getDeclaredField( "GLOBAL_PROPERTIES" ); + globalPropertiesField.setAccessible( true ); + final Properties systemProperties = (Properties) globalPropertiesField.get( null ); + assertThat( systemProperties ).isNotNull(); final Object previousValue = systemProperties.setProperty( AvailableSettings.STORAGE_ENGINE, "myisam" ); try { - assertEquals( " engine=MyISAM", new MySQLDialect().getTableTypeString() ); + final Map configurationValues = new HashMap<>(); + configurationValues.put( AvailableSettings.STORAGE_ENGINE, "innodb" ); + Dialect dialect = new MySQLDialect( + TestingDialectResolutionInfo.forDatabaseInfo( + "MySQL", + null, + DatabaseVersion.NO_VERSION, + DatabaseVersion.NO_VERSION, + configurationValues ) ); + assertThat( dialect.getTableTypeString() ).isEqualTo( " engine=InnoDB" ); } finally { if ( previousValue != null ) { @@ -46,4 +96,12 @@ public void testOverrideStorage() throws NoSuchFieldException, IllegalAccessExce } } + @Entity(name = "TestEntity") + public static class TestEntity{ + @Id + private Long id; + + private String name; + } + } From c8645746c645079ee2c65d932c11666628408db6 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Thu, 4 Sep 2025 12:06:02 +0200 Subject: [PATCH 12/12] HHH-19752 Allow setting MariaDB/MySQL storage engine not only using System properties but also configuration properties --- .../hibernate/cfg/SchemaToolingSettings.java | 7 ++++--- .../org/hibernate/dialect/MySQLDialect.java | 19 ++++++++++-------- .../dialect/MySQLServerConfiguration.java | 20 ++++++++++++++++++- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java index 83269ff6ef4d..e4ac6b6bdc57 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/SchemaToolingSettings.java @@ -296,9 +296,10 @@ public interface SchemaToolingSettings { /** * Specifies the default storage engine for a relational database that supports - * multiple storage engines. This property must be set either as an {@link Environment} - * variable or JVM System Property, since the {@link org.hibernate.dialect.Dialect} is - * instantiated before Hibernate property resolution. + * multiple storage engines. + * + * This property can be set as an {@link Environment} variable, a JVM System Property + * or a configuration property. *

* For MySQL, the legal values are {@code innodb} (the default) and {@code myisam}. * diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index f0a5f40873a0..32069be58970 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -93,6 +93,7 @@ import java.time.temporal.TemporalAccessor; import java.util.Calendar; import java.util.Date; +import java.util.Locale; import java.util.TimeZone; import static java.lang.Integer.parseInt; @@ -146,7 +147,7 @@ public class MySQLDialect extends Dialect { private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 8 ); - private final MySQLStorageEngine storageEngine = createStorageEngine(); + private final MySQLStorageEngine storageEngine; private final SizeStrategy sizeStrategy = new SizeStrategyImpl() { @Override @@ -207,7 +208,11 @@ public MySQLDialect(DatabaseVersion version, int bytesPerCharacter) { } public MySQLDialect(DatabaseVersion version, MySQLServerConfiguration serverConfiguration) { - this( version, serverConfiguration.getBytesPerCharacter(), serverConfiguration.isNoBackslashEscapesEnabled() ); + super( version ); + maxVarcharLength = maxVarcharLength( getMySQLVersion(), serverConfiguration.getBytesPerCharacter() ); //conservative assumption + maxVarbinaryLength = maxVarbinaryLength( getMySQLVersion() ); + noBackslashEscapesEnabled = serverConfiguration.isNoBackslashEscapesEnabled(); + storageEngine = createStorageEngine( serverConfiguration.getConfiguredStorageEngine() ); } public MySQLDialect(DatabaseVersion version, int bytesPerCharacter, boolean noBackslashEscapes) { @@ -215,6 +220,7 @@ public MySQLDialect(DatabaseVersion version, int bytesPerCharacter, boolean noBa maxVarcharLength = maxVarcharLength( getMySQLVersion(), bytesPerCharacter ); //conservative assumption maxVarbinaryLength = maxVarbinaryLength( getMySQLVersion() ); noBackslashEscapesEnabled = noBackslashEscapes; + storageEngine = createStorageEngine( Environment.getProperties().getProperty( AvailableSettings.STORAGE_ENGINE ) ); } public MySQLDialect(DialectResolutionInfo info) { @@ -257,13 +263,10 @@ protected void initDefaultProperties() { getDefaultProperties().setProperty( FetchSettings.MAX_FETCH_DEPTH, "2" ); } - private MySQLStorageEngine createStorageEngine() { - final String storageEngine = - Environment.getProperties() - .getProperty( AvailableSettings.STORAGE_ENGINE ); - return storageEngine == null + private MySQLStorageEngine createStorageEngine(String configuredStorageEngine) { + return configuredStorageEngine == null ? getDefaultMySQLStorageEngine() - : switch ( storageEngine ) { + : switch ( configuredStorageEngine.toLowerCase(Locale.ROOT) ) { case "innodb" -> InnoDBStorageEngine.INSTANCE; case "myisam" -> MyISAMStorageEngine.INSTANCE; default -> throw new UnsupportedOperationException( diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLServerConfiguration.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLServerConfiguration.java index 1c1cc77062ea..a66b5089d94f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLServerConfiguration.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLServerConfiguration.java @@ -9,7 +9,9 @@ import java.sql.SQLException; import org.hibernate.Internal; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; +import org.hibernate.internal.util.config.ConfigurationHelper; import static org.hibernate.cfg.DialectSpecificSettings.MYSQL_BYTES_PER_CHARACTER; import static org.hibernate.cfg.DialectSpecificSettings.MYSQL_NO_BACKSLASH_ESCAPES; @@ -26,10 +28,18 @@ public class MySQLServerConfiguration { private final int bytesPerCharacter; private final boolean noBackslashEscapesEnabled; + private final String storageEngine; public MySQLServerConfiguration(int bytesPerCharacter, boolean noBackslashEscapesEnabled) { this.bytesPerCharacter = bytesPerCharacter; this.noBackslashEscapesEnabled = noBackslashEscapesEnabled; + this.storageEngine = null; + } + + public MySQLServerConfiguration(int bytesPerCharacter, boolean noBackslashEscapesEnabled, String storageEngine) { + this.bytesPerCharacter = bytesPerCharacter; + this.noBackslashEscapesEnabled = noBackslashEscapesEnabled; + this.storageEngine = storageEngine; } public int getBytesPerCharacter() { @@ -40,6 +50,10 @@ public boolean isNoBackslashEscapesEnabled() { return noBackslashEscapesEnabled; } + public String getConfiguredStorageEngine() { + return storageEngine; + } + static Integer getBytesPerCharacter(String characterSet) { final int collationIndex = characterSet.indexOf( '_' ); // According to https://dev.mysql.com/doc/refman/8.0/en/charset-charsets.html @@ -78,7 +92,11 @@ public static MySQLServerConfiguration fromDialectResolutionInfo(DialectResoluti if ( noBackslashEscapes == null ) { noBackslashEscapes = getBoolean( MYSQL_NO_BACKSLASH_ESCAPES, info.getConfigurationValues() ); } - return new MySQLServerConfiguration( bytesPerCharacter, noBackslashEscapes ); + + return new MySQLServerConfiguration( + bytesPerCharacter, + noBackslashEscapes, + ConfigurationHelper.getString( AvailableSettings.STORAGE_ENGINE, info.getConfigurationValues() ) ); } /**