diff --git a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 index 37c177378d..6598168db0 100644 --- a/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 +++ b/hibernate-core/src/main/antlr/org/hibernate/grammars/hql/HqlParser.g4 @@ -1052,6 +1052,7 @@ identifier | UPDATE | UPPER | VALUE + | VALUES | VERSION | VERSIONED | WEEK diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/internal/NativeQueryInterpreterStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/query/internal/NativeQueryInterpreterStandardImpl.java index 3de90cb49b..eb2691fd44 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/internal/NativeQueryInterpreterStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/internal/NativeQueryInterpreterStandardImpl.java @@ -35,7 +35,7 @@ public class NativeQueryInterpreterStandardImpl implements NativeQueryInterprete return new NativeSelectQueryPlanImpl<>( queryDefinition.getSqlString(), queryDefinition.getAffectedTableNames(), - queryDefinition.getQueryParameterList(), + queryDefinition.getQueryParameterOccurrences(), queryDefinition.getResultSetMapping(), sessionFactory ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeQueryInterpreter.java b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeQueryInterpreter.java index d10ace2871..831f9f1edf 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeQueryInterpreter.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/query/spi/NativeQueryInterpreter.java @@ -44,7 +44,7 @@ public interface NativeQueryInterpreter extends Service { return new NativeSelectQueryPlanImpl<>( queryDefinition.getSqlString(), queryDefinition.getAffectedTableNames(), - queryDefinition.getQueryParameterList(), + queryDefinition.getQueryParameterOccurrences(), queryDefinition.getResultSetMapping(), sessionFactory ); diff --git a/hibernate-core/src/main/java/org/hibernate/internal/TableGroupFilterAliasGenerator.java b/hibernate-core/src/main/java/org/hibernate/internal/TableGroupFilterAliasGenerator.java index 5f18ea5004..681aa45376 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/TableGroupFilterAliasGenerator.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/TableGroupFilterAliasGenerator.java @@ -26,7 +26,7 @@ public class TableGroupFilterAliasGenerator implements FilterAliasGenerator { if ( table == null ) { table = defaultTable; } - final TableReference tableReference = tableGroup.getTableReference( table ); + final TableReference tableReference = tableGroup.getTableReference( null, table, true, true ); return tableReference == null ? null : tableReference.getIdentificationVariable(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java index e86a61a894..06e1d6a449 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/CollectionLoaderSubSelectFetch.java @@ -33,6 +33,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext; import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.entity.LoadingEntityEntry; +import org.hibernate.sql.results.internal.ResultsHelper; import org.hibernate.sql.results.internal.RowTransformerPassThruImpl; import org.hibernate.sql.results.spi.ListResultsConsumer; @@ -163,13 +164,22 @@ public class CollectionLoaderSubSelectFetch implements CollectionLoader { ); if ( subSelectFetchedCollections != null && ! subSelectFetchedCollections.isEmpty() ) { - subSelectFetchedCollections.forEach( (c) -> { - if ( c.wasInitialized() ) { - return; - } + subSelectFetchedCollections.forEach( + c -> { + if ( c.wasInitialized() ) { + return; + } - c.initializeEmptyCollection( getLoadable().getCollectionDescriptor() ); - } ); + c.initializeEmptyCollection( getLoadable().getCollectionDescriptor() ); + ResultsHelper.finalizeCollectionLoading( + persistenceContext, + getLoadable().getCollectionDescriptor(), + c, + c.getKey(), + true + ); + } + ); subSelectFetchedCollections.clear(); } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java index 458e981885..582d4b7817 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/internal/LoaderSelectBuilder.java @@ -60,9 +60,9 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.expression.SqlTuple; -import org.hibernate.sql.ast.tree.from.RootTableGroupProducer; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.InListPredicate; @@ -401,21 +401,9 @@ public class LoaderSelectBuilder { for ( ModelPart part : partsToSelect ) { final NavigablePath navigablePath = rootNavigablePath.append( part.getPartName() ); final TableGroup tableGroup; - if ( part instanceof RootTableGroupProducer ) { - tableGroup = ( (RootTableGroupProducer) part ).createRootTableGroup( - true, - navigablePath, - null, - () -> rootQuerySpec::applyPredicate, - sqlAstCreationState, - creationContext - ); - rootQuerySpec.getFromClause().addRoot( tableGroup ); - sqlAstCreationState.getFromClauseAccess().registerTableGroup( navigablePath, tableGroup ); - } - else if ( part instanceof ToOneAttributeMapping ) { - final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) part; - final TableGroupJoin tableGroupJoin = toOneAttributeMapping.createTableGroupJoin( + if ( part instanceof TableGroupJoinProducer ) { + final TableGroupJoinProducer tableGroupJoinProducer = (TableGroupJoinProducer) part; + final TableGroupJoin tableGroupJoin = tableGroupJoinProducer.createTableGroupJoin( navigablePath, rootTableGroup, null, @@ -613,8 +601,18 @@ public class LoaderSelectBuilder { tableGroup ); if ( manyToManyFilterPredicate != null ) { - assert tableGroup.getTableReferenceJoins().size() == 1; - tableGroup.getTableReferenceJoins().get( 0 ).applyPredicate( manyToManyFilterPredicate ); + TableGroupJoin elementTableGroupJoin = null; + for ( TableGroupJoin nestedTableGroupJoin : tableGroup.getNestedTableGroupJoins() ) { + final NavigablePath navigablePath = nestedTableGroupJoin.getNavigablePath(); + if ( navigablePath.getParent() == tableGroup.getNavigablePath() + && CollectionPart.Nature.ELEMENT.getName().equals( navigablePath.getUnaliasedLocalName() ) ) { + elementTableGroupJoin = nestedTableGroupJoin; + break; + } + } + + assert elementTableGroupJoin != null; + elementTableGroupJoin.applyPredicate( manyToManyFilterPredicate ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/Loadable.java b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/Loadable.java index 853361fd5c..22fca6d9f5 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/Loadable.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/ast/spi/Loadable.java @@ -16,6 +16,7 @@ import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationContext; +import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.tree.from.RootTableGroupProducer; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.predicate.Predicate; @@ -42,7 +43,8 @@ public interface Loadable extends ModelPart, RootTableGroupProducer { NavigablePath navigablePath, String explicitSourceAlias, Supplier> additionalPredicateCollectorAccess, - SqlAstCreationState creationState, SqlAstCreationContext creationContext) { + SqlAstCreationState creationState, + SqlAstCreationContext creationContext) { throw new NotYetImplementedFor6Exception( getClass() ); } @@ -53,7 +55,8 @@ public interface Loadable extends ModelPart, RootTableGroupProducer { String explicitSourceAlias, Supplier> additionalPredicateCollectorAccess, SqlAliasBase sqlAliasBase, - SqlAstCreationState creationState, SqlAstCreationContext creationContext) { + SqlExpressionResolver expressionResolver, + SqlAstCreationContext creationContext) { throw new NotYetImplementedFor6Exception( getClass() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java index 2dceba39a0..96cde3219e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/AbstractCompositeIdentifierMapping.java @@ -159,7 +159,6 @@ public abstract class AbstractCompositeIdentifierMapping fetchParent, fetchTiming, selected, - attributeMetadataAccess.resolveAttributeMetadata( null ).isNullable(), creationState ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityRepresentationStrategyPojoStandard.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityRepresentationStrategyPojoStandard.java index c80366a5ad..86aee8493a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityRepresentationStrategyPojoStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/EntityRepresentationStrategyPojoStandard.java @@ -87,7 +87,7 @@ public class EntityRepresentationStrategyPojoStandard implements EntityRepresent .getJavaTypeDescriptorRegistry(); final Class mappedJavaType = bootDescriptor.getMappedClass(); - this.mappedJtd = jtdRegistry.resolveManagedTypeDescriptor( mappedJavaType ); + this.mappedJtd = jtdRegistry.resolveEntityTypeDescriptor( mappedJavaType ); final Class proxyJavaType = bootDescriptor.getProxyInterface(); if ( proxyJavaType != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java index cc814e7831..658dfe8233 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EmbeddableMappingType.java @@ -38,6 +38,7 @@ import org.hibernate.mapping.Selectable; import org.hibernate.mapping.Table; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping; +import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.SelectableMappingsImpl; @@ -52,6 +53,7 @@ import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.Fetchable; @@ -176,7 +178,8 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp } private EmbeddableMappingType( - EmbeddableValuedModelPart valueMapping, + EmbeddedAttributeMapping valueMapping, + TableGroupProducer declaringTableGroupProducer, SelectableMappings selectableMappings, EmbeddableMappingType inverseMappingType, MappingModelCreationProcess creationProcess) { @@ -186,6 +189,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp this.valueMapping = valueMapping; this.createEmptyCompositesEnabled = inverseMappingType.isCreateEmptyCompositesEnabled(); this.selectableMappings = selectableMappings; + final ManagedMappingType declaringType = valueMapping.getDeclaringType(); creationProcess.registerInitializationCallback( "EmbeddableMappingType(" + inverseMappingType.getNavigableRole().getFullPath() + ".{inverse})#finishInitialization", () -> { @@ -201,6 +205,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp final BasicAttributeMapping original = (BasicAttributeMapping) attributeMapping; final SelectableMapping selectableMapping = selectableMappings.getSelectable( currentIndex ); attributeMapping = BasicAttributeMapping.withSelectableMapping( + declaringType, original, original.getPropertyAccess(), original.getValueGeneration(), @@ -210,13 +215,18 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp } else if ( attributeMapping instanceof ToOneAttributeMapping ) { final ToOneAttributeMapping original = (ToOneAttributeMapping) attributeMapping; - final ToOneAttributeMapping toOne = original.copy(); + final ToOneAttributeMapping toOne = original.copy( + declaringType, + declaringTableGroupProducer + ); final int offset = currentIndex; toOne.setIdentifyingColumnsTableExpression( selectableMappings.getSelectable( offset ).getContainingTableExpression() ); toOne.setForeignKeyDescriptor( original.getForeignKeyDescriptor().withKeySelectionMapping( + declaringType, + declaringTableGroupProducer, index -> selectableMappings.getSelectable( offset + index ), creationProcess ) @@ -237,10 +247,17 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp } public EmbeddableMappingType createInverseMappingType( - EmbeddableValuedModelPart valueMapping, + EmbeddedAttributeMapping valueMapping, + TableGroupProducer declaringTableGroupProducer, SelectableMappings selectableMappings, MappingModelCreationProcess creationProcess) { - return new EmbeddableMappingType( valueMapping, selectableMappings, this, creationProcess ); + return new EmbeddableMappingType( + valueMapping, + declaringTableGroupProducer, + selectableMappings, + this, + creationProcess + ); } private boolean finishInitialization( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java index 13d32b5b9f..fc12f06ffe 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/EntityMappingType.java @@ -9,6 +9,7 @@ package org.hibernate.metamodel.mapping; import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Supplier; @@ -195,8 +196,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel return superMappingType.getRootEntityDescriptor(); } - default TableReference locateTableReference(TableGroup tableGroup) { - return tableGroup.getPrimaryTableReference(); + default void pruneForSubclasses(TableGroup tableGroup, Set treatedEntityNames) { } default boolean isAbstract() { @@ -296,7 +296,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel explicitSourceAlias, additionalPredicateCollectorAccess, creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ), - creationState, + creationState.getSqlExpressionResolver(), creationContext ); } @@ -308,7 +308,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel String explicitSourceAlias, Supplier> additionalPredicateCollectorAccess, SqlAliasBase sqlAliasBase, - SqlAstCreationState creationState, + SqlExpressionResolver expressionResolver, SqlAstCreationContext creationContext) { return getEntityPersister().createRootTableGroup( canUseInnerJoins, @@ -316,7 +316,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel explicitSourceAlias, additionalPredicateCollectorAccess, sqlAliasBase, - creationState, + expressionResolver, creationContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java index 38653bf787..114c9b3a2e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ForeignKeyDescriptor.java @@ -15,6 +15,7 @@ import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.results.graph.DomainResult; @@ -102,15 +103,15 @@ public interface ForeignKeyDescriptor extends VirtualModelPart { DomainResultCreationState creationState); Predicate generateJoinPredicate( - TableGroup lhs, - TableGroup tableGroup, + TableGroup targetSideTableGroup, + TableGroup keySideTableGroup, SqlAstJoinType sqlAstJoinType, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext); Predicate generateJoinPredicate( - TableReference lhs, - TableReference rhs, + TableReference targetSideReference, + TableReference keySideReference, SqlAstJoinType sqlAstJoinType, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext); @@ -149,6 +150,8 @@ public interface ForeignKeyDescriptor extends VirtualModelPart { * Return a copy of this foreign key descriptor with the selectable mappings as provided by the given accessor. */ ForeignKeyDescriptor withKeySelectionMapping( + ManagedMappingType declaringType, + TableGroupProducer declaringTableGroupProducer, IntFunction selectableMappingAccess, MappingModelCreationProcess creationProcess); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingModelHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingModelHelper.java index 06ba6d3f91..c7bf1633f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingModelHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/MappingModelHelper.java @@ -46,7 +46,7 @@ public class MappingModelHelper { qualifier = selection.getContainingTableExpression(); } else { - qualifier = tableGroup.getTableReference( selection.getContainingTableExpression() ).getIdentificationVariable(); + qualifier = tableGroup.resolveTableReference( selection.getContainingTableExpression() ).getIdentificationVariable(); } if ( sqlExpressionResolver == null ) { colRef = new ColumnReference( @@ -78,7 +78,7 @@ public class MappingModelHelper { qualifier = basicPart.getContainingTableExpression(); } else { - qualifier = tableGroup.getTableReference( basicPart.getContainingTableExpression() ).getIdentificationVariable(); + qualifier = tableGroup.resolveTableReference( basicPart.getContainingTableExpression() ).getIdentificationVariable(); } if ( sqlExpressionResolver == null ) { return new ColumnReference( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDiscriminatorMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDiscriminatorMapping.java index 7215a96296..cb9a4c30c7 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDiscriminatorMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDiscriminatorMapping.java @@ -177,7 +177,6 @@ public abstract class AbstractDiscriminatorMapping implements EntityDiscriminato fetchParent, fetchablePath, this, - false, null, fetchTiming, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyDiscriminatorPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyDiscriminatorPart.java index 01a6b89a88..c4052de525 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyDiscriminatorPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyDiscriminatorPart.java @@ -188,7 +188,7 @@ public class AnyDiscriminatorPart implements BasicValuedModelPart, FetchOptions, final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); final TableGroup tableGroup = fromClauseAccess.getTableGroup( fetchablePath.getParent().getParent() ); - final TableReference tableReference = tableGroup.getTableReference( fetchablePath, table ); + final TableReference tableReference = tableGroup.resolveTableReference( fetchablePath, table ); final Expression columnReference = sqlExpressionResolver.resolveSqlExpression( createColumnReferenceKey( tableReference, column ), processingState -> new ColumnReference( @@ -212,7 +212,6 @@ public class AnyDiscriminatorPart implements BasicValuedModelPart, FetchOptions, fetchParent, fetchablePath, this, - nullable, null, fetchTiming, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyKeyPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyKeyPart.java index d507d86d8a..bb20fadf3a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyKeyPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AnyKeyPart.java @@ -151,7 +151,7 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions { .getSessionFactory(); final TableGroup tableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath().getParent() ); - final TableReference tableReference = tableGroup.getTableReference( fetchablePath, table ); + final TableReference tableReference = tableGroup.resolveTableReference( fetchablePath, table ); final Expression columnReference = sqlExpressionResolver.resolveSqlExpression( SqlExpressionResolver.createColumnReferenceKey( tableReference, column ), @@ -177,7 +177,6 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions { fetchParent, fetchablePath, this, - nullable, null, fetchTiming, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java index cec3a5e38b..b3924ed532 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicAttributeMapping.java @@ -106,6 +106,7 @@ public class BasicAttributeMapping } public static BasicAttributeMapping withSelectableMapping( + ManagedMappingType declaringType, BasicValuedModelPart original, PropertyAccess propertyAccess, ValueGeneration valueGeneration, @@ -114,19 +115,16 @@ public class BasicAttributeMapping int stateArrayPosition = 0; StateArrayContributorMetadataAccess attributeMetadataAccess = null; BasicValueConverter valueConverter = null; - ManagedMappingType declaringType = null; if ( original instanceof SingleAttributeIdentifierMapping ) { final SingleAttributeIdentifierMapping mapping = (SingleAttributeIdentifierMapping) original; attributeName = mapping.getAttributeName(); attributeMetadataAccess = null; - declaringType = mapping.findContainingEntityMapping(); } else if ( original instanceof SingularAttributeMapping ) { final SingularAttributeMapping mapping = (SingularAttributeMapping) original; attributeName = mapping.getAttributeName(); stateArrayPosition = mapping.getStateArrayPosition(); attributeMetadataAccess = mapping.getAttributeMetadataAccess(); - declaringType = mapping.getDeclaringType(); } if ( original instanceof ConvertibleModelPart ) { valueConverter = ( (ConvertibleModelPart) original ).getValueConverter(); @@ -212,7 +210,7 @@ public class BasicAttributeMapping TableGroup tableGroup, String resultVariable, DomainResultCreationState creationState) { - final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, true, creationState ); + final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, true, creationState ); //noinspection unchecked return new BasicResult( @@ -225,12 +223,13 @@ public class BasicAttributeMapping } private SqlSelection resolveSqlSelection( + NavigablePath navigablePath, TableGroup tableGroup, boolean allowFkOptimization, DomainResultCreationState creationState) { final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver(); final TableReference tableReference = tableGroup.resolveTableReference( - tableGroup.getNavigablePath().append( getNavigableRole().getNavigableName() ), + navigablePath, getContainingTableExpression(), allowFkOptimization ); @@ -257,7 +256,7 @@ public class BasicAttributeMapping NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { - resolveSqlSelection( tableGroup, true, creationState ); + resolveSqlSelection( navigablePath, tableGroup, true, creationState ); } @Override @@ -266,7 +265,7 @@ public class BasicAttributeMapping TableGroup tableGroup, DomainResultCreationState creationState, BiConsumer selectionConsumer) { - selectionConsumer.accept( resolveSqlSelection( tableGroup, true, creationState ), getJdbcMapping() ); + selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, true, creationState ), getJdbcMapping() ); } @Override @@ -293,7 +292,7 @@ public class BasicAttributeMapping assert tableGroup != null; - final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, false, creationState ); + final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, true, creationState ); valuesArrayPosition = sqlSelection.getValuesArrayPosition(); } @@ -302,7 +301,6 @@ public class BasicAttributeMapping fetchParent, fetchablePath, this, - getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(), valueConverter, fetchTiming, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java index dd847f5ea5..8e85fd3f2f 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicEntityIdentifierMappingImpl.java @@ -31,6 +31,7 @@ import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.Clause; +import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.expression.ColumnReference; @@ -326,12 +327,19 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa boolean selected, String resultVariable, DomainResultCreationState creationState) { + final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); + final TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup( + fetchParent.getNavigablePath() + ); + + assert tableGroup != null; + + final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, false, creationState ); return new BasicFetch<>( - 0, + sqlSelection.getValuesArrayPosition(), fetchParent, fetchablePath, this, - false, null, FetchTiming.IMMEDIATE, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java index 99fd1d238d..32a33028d3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/BasicValuedCollectionPart.java @@ -230,7 +230,6 @@ public class BasicValuedCollectionPart fetchParent, fetchablePath, this, - false, valueConverter, FetchTiming.IMMEDIATE, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CaseStatementDiscriminatorMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CaseStatementDiscriminatorMappingImpl.java index 09589034c2..7f226d7db9 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CaseStatementDiscriminatorMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CaseStatementDiscriminatorMappingImpl.java @@ -9,20 +9,29 @@ package org.hibernate.metamodel.mapping.internal; import java.util.LinkedHashMap; import java.util.Map; +import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.JdbcMappingContainer; import org.hibernate.persister.entity.DiscriminatorType; import org.hibernate.persister.entity.JoinedSubclassEntityPersister; import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.SqlAstTranslator; +import org.hibernate.sql.ast.spi.SqlAppender; import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.QueryLiteral; +import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; import org.hibernate.sql.ast.tree.predicate.Predicate; +import org.hibernate.sql.results.graph.DomainResultCreationState; +import org.hibernate.sql.results.graph.FetchParent; +import org.hibernate.sql.results.graph.basic.BasicFetch; import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey; @@ -69,6 +78,31 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator } } + @Override + public BasicFetch generateFetch( + FetchParent fetchParent, + NavigablePath fetchablePath, + FetchTiming fetchTiming, + boolean selected, + String resultVariable, + DomainResultCreationState creationState) { + final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); + final TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup( + fetchParent.getNavigablePath() + ); + // Since the expression is lazy, based on the available table reference joins, + // we need to force the initialization in case this is a fetch + tableDiscriminatorDetailsMap.forEach( + (tableName, tableDiscriminatorDetails) -> tableGroup.getTableReference( + fetchablePath, + tableName, + false, + true + ) + ); + return super.generateFetch( fetchParent, fetchablePath, fetchTiming, selected, resultVariable, creationState ); + } + @Override public Expression resolveSqlExpression( NavigablePath navigablePath, @@ -82,37 +116,63 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator ); } - private CaseSearchedExpression createCaseSearchedExpression(TableGroup entityTableGroup) { - final CaseSearchedExpression caseSearchedExpression = new CaseSearchedExpression( this ); + private Expression createCaseSearchedExpression(TableGroup entityTableGroup) { + return new SelfRenderingExpression() { + CaseSearchedExpression caseSearchedExpression; - tableDiscriminatorDetailsMap.forEach( (tableName, tableDiscriminatorDetails) -> { - final TableReference tableReference = entityTableGroup.getTableReference( entityTableGroup.getNavigablePath(), tableName ); + @Override + public void renderToSql( + SqlAppender sqlAppender, + SqlAstTranslator walker, + SessionFactoryImplementor sessionFactory) { + if ( caseSearchedExpression == null ) { + // todo (6.0): possible optimization is to omit cases for table reference joins, that touch a super class, where a subclass is inner joined due to pruning + caseSearchedExpression = new CaseSearchedExpression( CaseStatementDiscriminatorMappingImpl.this ); + tableDiscriminatorDetailsMap.forEach( + (tableName, tableDiscriminatorDetails) -> { + final TableReference tableReference = entityTableGroup.getTableReference( + entityTableGroup.getNavigablePath(), + tableName, + false, + false + ); - if ( tableReference == null ) { - // assume this is because it is a table that is not part of the processing entity's sub-hierarchy - return; + if ( tableReference == null ) { + // assume this is because it is a table that is not part of the processing entity's sub-hierarchy + return; + } + + final Predicate predicate = new NullnessPredicate( + new ColumnReference( + tableReference, + tableDiscriminatorDetails.getCheckColumnName(), + false, + null, + null, + getJdbcMapping(), + getSessionFactory() + ), + true + ); + + caseSearchedExpression.when( + predicate, + new QueryLiteral<>( + tableDiscriminatorDetails.getDiscriminatorValue(), + getUnderlyingJdbcMappingType() + ) + ); + } + ); + } + caseSearchedExpression.accept( walker ); } - final Predicate predicate = new NullnessPredicate( - new ColumnReference( - tableReference, - tableDiscriminatorDetails.getCheckColumnName(), - false, - null, - null, - getJdbcMapping(), - getSessionFactory() - ), - true - ); - - caseSearchedExpression.when( predicate, new QueryLiteral<>( - tableDiscriminatorDetails.getDiscriminatorValue(), - getUnderlyingJdbcMappingType() - ) ); - } ); - - return caseSearchedExpression; + @Override + public JdbcMappingContainer getExpressionType() { + return CaseStatementDiscriminatorMappingImpl.this; + } + }; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java index cfde7d5971..9da9b81a70 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/CollectionIdentifierDescriptorImpl.java @@ -180,7 +180,6 @@ public class CollectionIdentifierDescriptorImpl implements CollectionIdentifierD fetchParent, fetchablePath, this, - ! selected, null, FetchTiming.IMMEDIATE, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java index 9147477553..92c0c267b2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedAttributeMapping.java @@ -43,6 +43,7 @@ import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.from.CompositeTableGroup; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.results.graph.DomainResult; @@ -110,6 +111,8 @@ public class EmbeddedAttributeMapping // Constructor is only used for creating the inverse attribute mapping private EmbeddedAttributeMapping( + ManagedMappingType keyDeclaringType, + TableGroupProducer declaringTableGroupProducer, SelectableMappings selectableMappings, EmbeddableValuedModelPart inverseModelPart, MappingModelCreationProcess creationProcess) { @@ -118,11 +121,7 @@ public class EmbeddedAttributeMapping -1, null, inverseModelPart.getMappedFetchOptions(), - inverseModelPart instanceof AttributeMapping - ? ( (AttributeMapping) inverseModelPart ).getDeclaringType() - : inverseModelPart instanceof EntityIdentifierMapping - ? inverseModelPart.findContainingEntityMapping() - : null, + keyDeclaringType, null, null ); @@ -132,6 +131,7 @@ public class EmbeddedAttributeMapping this.tableExpression = selectableMappings.getSelectable( 0 ).getContainingTableExpression(); this.embeddableMappingType = inverseModelPart.getEmbeddableTypeDescriptor().createInverseMappingType( this, + declaringTableGroupProducer, selectableMappings, creationProcess ); @@ -140,9 +140,17 @@ public class EmbeddedAttributeMapping public static EmbeddableValuedModelPart createInverseModelPart( EmbeddableValuedModelPart modelPart, + ManagedMappingType keyDeclaringType, + TableGroupProducer declaringTableGroupProducer, SelectableMappings selectableMappings, MappingModelCreationProcess creationProcess) { - return new EmbeddedAttributeMapping( selectableMappings, modelPart, creationProcess ); + return new EmbeddedAttributeMapping( + keyDeclaringType, + declaringTableGroupProducer, + selectableMappings, + modelPart, + creationProcess + ); } @Override @@ -225,7 +233,6 @@ public class EmbeddedAttributeMapping fetchParent, fetchTiming, selected, - getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(), creationState ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java index 25e497d5b9..a96837c2dc 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedCollectionPart.java @@ -159,7 +159,6 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF fetchParent, FetchTiming.IMMEDIATE, selected, - true, creationState ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java index 02d7916772..9c887039a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddedForeignKeyDescriptor.java @@ -19,6 +19,7 @@ import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPartContainer; @@ -27,6 +28,7 @@ import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMappings; import org.hibernate.metamodel.model.domain.NavigableRole; +import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.ComparisonOperator; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.Clause; @@ -36,6 +38,7 @@ import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; @@ -101,6 +104,8 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { private EmbeddedForeignKeyDescriptor( EmbeddedForeignKeyDescriptor original, String keyTable, + ManagedMappingType keyDeclaringType, + TableGroupProducer keyDeclaringTableGroupProducer, SelectableMappings keySelectableMappings, MappingModelCreationProcess creationProcess) { this.keyTable = keyTable; @@ -112,6 +117,8 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { Nature.KEY, EmbeddedAttributeMapping.createInverseModelPart( original.targetSide.getModelPart(), + keyDeclaringType, + keyDeclaringTableGroupProducer, keySelectableMappings, creationProcess ) @@ -158,6 +165,8 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { @Override public ForeignKeyDescriptor withKeySelectionMapping( + ManagedMappingType declaringType, + TableGroupProducer declaringTableGroupProducer, IntFunction selectableMappingAccess, MappingModelCreationProcess creationProcess) { SelectableMapping[] selectionMappings = new SelectableMapping[keySelectableMappings.getJdbcTypeCount()]; @@ -167,6 +176,8 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { return new EmbeddedForeignKeyDescriptor( this, selectionMappings[0].getContainingTableExpression(), + declaringType, + declaringTableGroupProducer, new SelectableMappingsImpl( selectionMappings ), creationProcess ); @@ -340,11 +351,11 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { SqlAstJoinType sqlAstJoinType, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { - final TableReference lhsTableReference = targetSideTableGroup.getTableReference( + final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference( targetSideTableGroup.getNavigablePath(), targetTable ); - final TableReference rhsTableKeyReference = keySideTableGroup.getTableReference( keyTable ); + final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference( keyTable ); return generateJoinPredicate( lhsTableReference, diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java index e0cf748cd3..bbbeb850ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityCollectionPart.java @@ -6,42 +6,66 @@ */ package org.hibernate.metamodel.mapping.internal; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; +import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.dialect.Dialect; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.mapping.Collection; +import org.hibernate.mapping.IndexedCollection; +import org.hibernate.mapping.OneToMany; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.mapping.ToOne; import org.hibernate.mapping.Value; +import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.CollectionPart; +import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EntityAssociationMapping; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.SelectableConsumer; +import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.Joinable; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.spi.FromClauseAccess; +import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlSelection; +import org.hibernate.sql.ast.tree.from.LazyTableGroup; +import org.hibernate.sql.ast.tree.from.OneToManyTableGroup; +import org.hibernate.sql.ast.tree.from.StandardTableGroup; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; +import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.FetchOptions; import org.hibernate.sql.results.graph.FetchParent; -import org.hibernate.sql.results.graph.collection.internal.EntityCollectionPartTableGroup; import org.hibernate.sql.results.graph.entity.EntityFetch; import org.hibernate.sql.results.graph.entity.EntityValuedFetchable; import org.hibernate.sql.results.graph.entity.internal.EntityFetchJoinedImpl; +import org.hibernate.tuple.IdentifierProperty; +import org.hibernate.tuple.entity.EntityMetamodel; +import org.hibernate.type.CompositeType; +import org.hibernate.type.EntityType; +import org.hibernate.type.Type; import org.hibernate.type.descriptor.java.JavaType; /** @@ -53,8 +77,10 @@ public class EntityCollectionPart private final CollectionPersister collectionDescriptor; private final Nature nature; private final EntityMappingType entityMappingType; + private final Set targetKeyPropertyNames; private ModelPart fkTargetModelPart; + private ForeignKeyDescriptor fkDescriptor; @SuppressWarnings("WeakerAccess") public EntityCollectionPart( @@ -67,6 +93,81 @@ public class EntityCollectionPart this.collectionDescriptor = collectionDescriptor; this.nature = nature; this.entityMappingType = entityMappingType; + final String referencedPropertyName; + if ( bootModelValue instanceof OneToMany ) { + referencedPropertyName = null; + } + else { + referencedPropertyName = ( (ToOne) bootModelValue ).getReferencedPropertyName(); + } + if ( referencedPropertyName == null ) { + final Set targetKeyPropertyNames = new HashSet<>( 2 ); + targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME ); + final IdentifierProperty identifierProperty = getEntityMappingType() + .getEntityPersister() + .getEntityMetamodel() + .getIdentifierProperty(); + final Type propertyType = identifierProperty.getType(); + if ( identifierProperty.getName() == null ) { + final CompositeType compositeType; + if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded() + && compositeType.getPropertyNames().length == 1 ) { + ToOneAttributeMapping.addPrefixedPropertyNames( + targetKeyPropertyNames, + compositeType.getPropertyNames()[0], + compositeType.getSubtypes()[0], + creationProcess.getCreationContext().getSessionFactory() + ); + } + else { + ToOneAttributeMapping.addPrefixedPropertyNames( + targetKeyPropertyNames, + null, + propertyType, + creationProcess.getCreationContext().getSessionFactory() + ); + } + } + else { + ToOneAttributeMapping.addPrefixedPropertyNames( + targetKeyPropertyNames, + identifierProperty.getName(), + propertyType, + creationProcess.getCreationContext().getSessionFactory() + ); + } + this.targetKeyPropertyNames = targetKeyPropertyNames; + } + else if ( bootModelValue instanceof OneToMany ) { + this.targetKeyPropertyNames = Collections.singleton( referencedPropertyName ); + } + else { + final EntityMetamodel entityMetamodel = entityMappingType.getEntityPersister().getEntityMetamodel(); + final int propertyIndex = entityMetamodel.getPropertyIndex( referencedPropertyName ); + final Type propertyType = entityMetamodel.getPropertyTypes()[propertyIndex]; + final CompositeType compositeType; + if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded() + && compositeType.getPropertyNames().length == 1 ) { + this.targetKeyPropertyNames = Collections.singleton( compositeType.getPropertyNames()[0] ); + } + else { + final String mapsIdAttributeName; + if ( ( mapsIdAttributeName = ToOneAttributeMapping.mapsId( entityMappingType, referencedPropertyName ) ) != null ) { + final Set targetKeyPropertyNames = new HashSet<>( 2 ); + targetKeyPropertyNames.add( referencedPropertyName ); + ToOneAttributeMapping.addPrefixedPropertyNames( + targetKeyPropertyNames, + mapsIdAttributeName, + entityMappingType.getEntityPersister().getIdentifierType(), + creationProcess.getCreationContext().getSessionFactory() + ); + this.targetKeyPropertyNames = targetKeyPropertyNames; + } + else { + this.targetKeyPropertyNames = Collections.singleton( referencedPropertyName ); + } + } + } } @SuppressWarnings("WeakerAccess") @@ -81,8 +182,80 @@ public class EntityCollectionPart else { fkTargetModelPart = entityMappingType.findSubPart( fkTargetModelPartName, null ); } + if ( nature == Nature.ELEMENT ) { + fkDescriptor = createForeignKeyDescriptor( + bootValueMapping.getElement(), + (EntityType) collectionDescriptor.getElementType(), + creationProcess, + collectionDescriptor.getFactory().getJdbcServices().getDialect() + ); + } + else { + fkDescriptor = createForeignKeyDescriptor( + ( (IndexedCollection) bootValueMapping).getIndex(), + (EntityType) collectionDescriptor.getIndexType(), + creationProcess, + collectionDescriptor.getFactory().getJdbcServices().getDialect() + ); + } } + private ForeignKeyDescriptor createForeignKeyDescriptor( + Value fkBootDescriptorSource, + EntityType entityType, + MappingModelCreationProcess creationProcess, + Dialect dialect) { + final EntityPersister associatedEntityDescriptor = creationProcess.getEntityPersister( entityType.getAssociatedEntityName() ); + final ModelPart fkTargetPart = entityType.isReferenceToPrimaryKey() + ? associatedEntityDescriptor.getIdentifierMapping() + : associatedEntityDescriptor.findSubPart( entityType.getRHSUniqueKeyPropertyName() ); + + if ( fkTargetPart instanceof BasicValuedModelPart ) { + final BasicValuedModelPart basicFkTargetPart = (BasicValuedModelPart) fkTargetPart; + final Joinable collectionDescriptorAsJoinable = (Joinable) collectionDescriptor; + final SelectableMapping keySelectableMapping = SelectableMappingImpl.from( + collectionDescriptorAsJoinable.getTableName(), + fkBootDescriptorSource.getColumnIterator().next(), + basicFkTargetPart.getJdbcMapping(), + dialect, + creationProcess.getSqmFunctionRegistry() + ); + final boolean hasConstraint; + if ( fkBootDescriptorSource instanceof SimpleValue ) { + hasConstraint = ( (SimpleValue) fkBootDescriptorSource ).isConstrained(); + } + else { + // We assume there is a constraint if the key is not nullable + hasConstraint = !fkBootDescriptorSource.isNullable(); + } + return new SimpleForeignKeyDescriptor( + associatedEntityDescriptor, + basicFkTargetPart, + null, + keySelectableMapping, + basicFkTargetPart, + entityType.isReferenceToPrimaryKey(), + hasConstraint + ); + } + else if ( fkTargetPart instanceof EmbeddableValuedModelPart ) { + return MappingModelCreationHelper.buildEmbeddableForeignKeyDescriptor( + (EmbeddableValuedModelPart) fkTargetPart, + fkBootDescriptorSource, + findContainingEntityMapping(), + collectionDescriptor.getAttributeMapping(), + false, + dialect, + creationProcess + ); + } + else { + throw new NotYetImplementedFor6Exception( + "Support for composite foreign keys not yet implemented : " + collectionDescriptor + .getRole() + ); + } + } @Override public SqlAstJoinType getDefaultSqlAstJoinType(TableGroup parentTableGroup) { @@ -146,22 +319,24 @@ public class EntityCollectionPart final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess(); creationState.registerVisitedAssociationKey( getForeignKeyDescriptor().getAssociationKey() ); - TableGroup tableGroup = fromClauseAccess.resolveTableGroup( + final TableGroup partTableGroup = fromClauseAccess.resolveTableGroup( fetchablePath, np -> { - // We need to create one. The Result will be able to find it later by path + final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( np.getParent() ); + if ( collectionDescriptor.isOneToMany() && nature == Nature.ELEMENT ) { + return ( (OneToManyTableGroup) parentTableGroup ).getElementTableGroup(); + } + for ( TableGroupJoin nestedTableGroupJoin : parentTableGroup.getNestedTableGroupJoins() ) { + if ( nestedTableGroupJoin.getNavigablePath().equals( np ) ) { + return nestedTableGroupJoin.getJoinedGroup(); + } + } - // first, find the collection's TableGroup - final TableGroup collectionTableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath() ); - - assert collectionTableGroup != null; - - // create a "wrapper" around the collection TableGroup adding in the entity's table references - return new EntityCollectionPartTableGroup( fetchablePath, collectionTableGroup, this ); + throw new IllegalStateException( "Could not find table group for: " + np ); } ); - return new EntityFetchJoinedImpl( fetchParent, this, tableGroup, selected, fetchablePath, creationState ); + return new EntityFetchJoinedImpl( fetchParent, this, partTableGroup, selected, fetchablePath, creationState ); } @Override @@ -184,15 +359,20 @@ public class EntityCollectionPart final TableGroup partTableGroup = fromClauseAccess.resolveTableGroup( navigablePath, np -> { - // We need to create one. The Result will be able to find it later by path - - // first, find the collection's TableGroup - final TableGroup collectionTableGroup = fromClauseAccess.getTableGroup( np.getParent() ); - - assert collectionTableGroup != null; - - // create a "wrapper" around the collection TableGroup adding in the entity's table references - return new EntityCollectionPartTableGroup( np, collectionTableGroup, this ); + final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( np.getParent() ); + if ( collectionDescriptor.isOneToMany() && nature == Nature.ELEMENT ) { + return ( (OneToManyTableGroup) parentTableGroup ).getElementTableGroup(); + } + final TableGroupJoin tableGroupJoin = createTableGroupJoin( + navigablePath, + parentTableGroup, + resultVariable, + SqlAstJoinType.INNER, + true, + creationState.getSqlAstCreationState() + ); + parentTableGroup.addTableGroupJoin( tableGroupJoin ); + return tableGroupJoin.getJoinedGroup(); } ); @@ -247,8 +427,7 @@ public class EntityCollectionPart @Override public ForeignKeyDescriptor getForeignKeyDescriptor() { - // todo (6.0) : this will not strictly work - we'd want a new ForeignKeyDescriptor that points the other direction - return collectionDescriptor.getAttributeMapping().getKeyDescriptor(); + return fkDescriptor; } @Override @@ -269,27 +448,60 @@ public class EntityCollectionPart @Override public TableGroupJoin createTableGroupJoin( NavigablePath navigablePath, - TableGroup lhs, + TableGroup collectionTableGroup, String explicitSourceAlias, SqlAstJoinType sqlAstJoinType, boolean fetched, SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { - return collectionDescriptor.getAttributeMapping().createTableGroupJoin( + if ( collectionDescriptor.isOneToMany() && nature == Nature.ELEMENT ) { + // If this is a one-to-many, the element part is already available, so we return a TableGroupJoin "hull" + return new TableGroupJoin( + navigablePath, + sqlAstJoinType, + ( (OneToManyTableGroup) collectionTableGroup ).getElementTableGroup(), + null + ); + } + + final LazyTableGroup lazyTableGroup = createRootTableGroupJoin( navigablePath, - lhs, + collectionTableGroup, explicitSourceAlias, sqlAstJoinType, fetched, + null, aliasBaseGenerator, sqlExpressionResolver, creationContext ); + final TableGroupJoin join = new TableGroupJoin( + navigablePath, + sqlAstJoinType, + lazyTableGroup, + null + ); + + final TableReference keySideTableReference = collectionTableGroup.getPrimaryTableReference(); + + lazyTableGroup.setTableGroupInitializerCallback( + tableGroup -> join.applyPredicate( + fkDescriptor.generateJoinPredicate( + tableGroup.getPrimaryTableReference(), + keySideTableReference, + sqlAstJoinType, + sqlExpressionResolver, + creationContext + ) + ) + ); + + return join; } @Override - public TableGroup createRootTableGroupJoin( + public LazyTableGroup createRootTableGroupJoin( NavigablePath navigablePath, TableGroup lhs, String explicitSourceAlias, @@ -299,17 +511,101 @@ public class EntityCollectionPart SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { - return collectionDescriptor.getAttributeMapping().createRootTableGroupJoin( + final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() ); + final boolean canUseInnerJoin = sqlAstJoinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins(); + final LazyTableGroup lazyTableGroup = new LazyTableGroup( + canUseInnerJoin, navigablePath, - lhs, - explicitSourceAlias, - sqlAstJoinType, fetched, - predicateConsumer, - aliasBaseGenerator, + () -> createTableGroupInternal( + canUseInnerJoin, + navigablePath, + fetched, + null, + sqlAliasBase, + sqlExpressionResolver, + creationContext + ), + (np, tableExpression) -> { + NavigablePath path = np.getParent(); + // Fast path + if ( path != null && navigablePath.equals( path ) ) { + return targetKeyPropertyNames.contains( np.getUnaliasedLocalName() ) + && fkDescriptor.getKeyTable().equals( tableExpression ); + } + final StringBuilder sb = new StringBuilder( np.getFullPath().length() ); + sb.append( np.getUnaliasedLocalName() ); + while ( path != null && !navigablePath.equals( path ) ) { + sb.insert( 0, '.' ); + sb.insert( 0, path.getUnaliasedLocalName() ); + path = path.getParent(); + } + return path != null && navigablePath.equals( path ) + && targetKeyPropertyNames.contains( sb.toString() ) + && fkDescriptor.getKeyTable().equals( tableExpression ); + }, + this, + explicitSourceAlias, + sqlAliasBase, + creationContext.getSessionFactory(), + lhs + ); + + if ( predicateConsumer != null ) { + final TableReference keySideTableReference = lhs.resolveTableReference( + navigablePath, + fkDescriptor.getKeyTable() + ); + + lazyTableGroup.setTableGroupInitializerCallback( + tableGroup -> predicateConsumer.accept( + fkDescriptor.generateJoinPredicate( + tableGroup.getPrimaryTableReference(), + keySideTableReference, + sqlAstJoinType, + sqlExpressionResolver, + creationContext + ) + ) + ); + } + + return lazyTableGroup; + } + + public TableGroup createTableGroupInternal( + boolean canUseInnerJoins, + NavigablePath navigablePath, + boolean fetched, + String sourceAlias, + final SqlAliasBase sqlAliasBase, + SqlExpressionResolver sqlExpressionResolver, + SqlAstCreationContext creationContext) { + final TableReference primaryTableReference = getEntityMappingType().createPrimaryTableReference( + sqlAliasBase, sqlExpressionResolver, creationContext ); + + return new StandardTableGroup( + canUseInnerJoins, + navigablePath, + this, + fetched, + sourceAlias, + primaryTableReference, + false, + sqlAliasBase, + (tableExpression) -> getEntityMappingType().containsTableReference( tableExpression ), + (tableExpression, tg) -> getEntityMappingType().createTableReferenceJoin( + tableExpression, + sqlAliasBase, + primaryTableReference, + sqlExpressionResolver, + creationContext + ), + creationContext.getSessionFactory() + ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityVersionMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityVersionMappingImpl.java index 6cac570605..89eea69abc 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityVersionMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EntityVersionMappingImpl.java @@ -200,7 +200,6 @@ public class EntityVersionMappingImpl implements EntityVersionMapping, FetchOpti fetchParent, fetchablePath, this, - false, null, fetchTiming, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java index 85e42f6123..f9a6d878d0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/MappingModelCreationHelper.java @@ -76,6 +76,7 @@ import org.hibernate.metamodel.model.convert.spi.BasicValueConverter; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.collection.QueryableCollection; import org.hibernate.persister.collection.SQLLoadableCollection; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; @@ -84,6 +85,7 @@ import org.hibernate.property.access.internal.ChainedPropertyAccessImpl; import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.sql.ast.spi.SqlAliasStemHelper; +import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.tuple.ValueGeneration; import org.hibernate.type.AnyType; import org.hibernate.type.AssociationType; @@ -908,6 +910,17 @@ public class MappingModelCreationHelper { final ModelPart fkTarget; final String lhsPropertyName = collectionDescriptor.getCollectionType().getLHSPropertyName(); final boolean isReferenceToPrimaryKey = lhsPropertyName == null; + final ManagedMappingType keyDeclaringType; + if ( collectionDescriptor.getElementType().isEntityType() ) { + keyDeclaringType = ( (QueryableCollection) collectionDescriptor ).getElementPersister(); + } + else { + // This is not "really correct" but it is as good as it gets. + // The key declaring type serves as declaring type for the inverse model part of a FK. + // Most of the time, there is a proper managed type, but not for basic collections. + // Since the declaring type is needed for certain operations, we use the one from the target side of the FK + keyDeclaringType = declaringType; + } if ( isReferenceToPrimaryKey ) { fkTarget = collectionDescriptor.getOwnerEntityPersister().getIdentifierMapping(); } @@ -929,6 +942,7 @@ public class MappingModelCreationHelper { ); attributeMapping.setForeignKeyDescriptor( new SimpleForeignKeyDescriptor( + keyDeclaringType, simpleFkTarget, null, keySelectableMapping, @@ -943,6 +957,9 @@ public class MappingModelCreationHelper { buildEmbeddableForeignKeyDescriptor( (EmbeddableValuedModelPart) fkTarget, bootValueMapping, + keyDeclaringType, + collectionDescriptor.getAttributeMapping(), + false, dialect, creationProcess ); @@ -1025,6 +1042,8 @@ public class MappingModelCreationHelper { final EmbeddedForeignKeyDescriptor embeddedForeignKeyDescriptor = buildEmbeddableForeignKeyDescriptor( (EmbeddableValuedModelPart) modelPart, bootValueMapping, + attributeMapping.getDeclaringType(), + attributeMapping.findContainingEntityMapping(), true, dialect, creationProcess @@ -1058,12 +1077,15 @@ public class MappingModelCreationHelper { if ( inversePropertyAccess == null ) { // So far, OneToOne mappings are only supported based on the owner's PK if ( bootValueMapping instanceof OneToOne ) { - declaringKeyPart = (BasicValuedModelPart) attributeMapping.findContainingEntityMapping().getIdentifierMapping(); + declaringKeyPart = simpleFkTarget; + final EntityIdentifierMapping identifierMapping = attributeMapping.findContainingEntityMapping() + .getIdentifierMapping(); + declaringKeyPropertyAccess = ( (PropertyBasedMapping) identifierMapping ).getPropertyAccess(); } else { declaringKeyPart = simpleFkTarget; + declaringKeyPropertyAccess = ( (PropertyBasedMapping) declaringKeyPart ).getPropertyAccess(); } - declaringKeyPropertyAccess = ( (PropertyBasedMapping) declaringKeyPart ).getPropertyAccess(); } else { declaringKeyPart = simpleFkTarget; @@ -1094,6 +1116,7 @@ public class MappingModelCreationHelper { } final ForeignKeyDescriptor foreignKeyDescriptor = new SimpleForeignKeyDescriptor( + attributeMapping.getDeclaringType(), declaringKeyPart, declaringKeyPropertyAccess, keySelectableMapping, @@ -1108,6 +1131,8 @@ public class MappingModelCreationHelper { final EmbeddedForeignKeyDescriptor embeddedForeignKeyDescriptor = buildEmbeddableForeignKeyDescriptor( (EmbeddableValuedModelPart) fkTarget, bootValueMapping, + attributeMapping.getDeclaringType(), + attributeMapping.findContainingEntityMapping(), swapDirection, dialect, creationProcess @@ -1179,20 +1204,8 @@ public class MappingModelCreationHelper { public static EmbeddedForeignKeyDescriptor buildEmbeddableForeignKeyDescriptor( EmbeddableValuedModelPart embeddableValuedModelPart, Value bootValueMapping, - Dialect dialect, - MappingModelCreationProcess creationProcess) { - return buildEmbeddableForeignKeyDescriptor( - embeddableValuedModelPart, - bootValueMapping, - false, - dialect, - creationProcess - ); - } - - private static EmbeddedForeignKeyDescriptor buildEmbeddableForeignKeyDescriptor( - EmbeddableValuedModelPart embeddableValuedModelPart, - Value bootValueMapping, + ManagedMappingType keyDeclaringType, + TableGroupProducer keyDeclaringTableGroupProducer, boolean inverse, Dialect dialect, MappingModelCreationProcess creationProcess) { @@ -1241,6 +1254,8 @@ public class MappingModelCreationHelper { embeddableValuedModelPart, EmbeddedAttributeMapping.createInverseModelPart( embeddableValuedModelPart, + keyDeclaringType, + keyDeclaringTableGroupProducer, keySelectableMappings, creationProcess ), @@ -1256,6 +1271,8 @@ public class MappingModelCreationHelper { return new EmbeddedForeignKeyDescriptor( EmbeddedAttributeMapping.createInverseModelPart( embeddableValuedModelPart, + keyDeclaringType, + keyDeclaringTableGroupProducer, keySelectableMappings, creationProcess ), diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java index d9cbe784eb..8e18e16af7 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/NonAggregatedIdentifierMappingImpl.java @@ -19,6 +19,7 @@ import org.hibernate.mapping.ManyToOne; import org.hibernate.mapping.Property; import org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.SingularAttributeMapping; @@ -175,7 +176,7 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif @Override public String getFetchableName() { - return "id"; + return EntityIdentifierMapping.ROLE_LOCAL_NAME; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java index 5c63d2100d..47539b39cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java @@ -6,14 +6,11 @@ */ package org.hibernate.metamodel.mapping.internal; +import java.util.ArrayList; import java.util.function.BiConsumer; -import java.util.function.BiFunction; import java.util.function.Consumer; -import java.util.function.Function; import java.util.function.Supplier; -import org.hibernate.NotYetImplementedFor6Exception; -import org.hibernate.dialect.Dialect; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.CascadeStyle; @@ -21,31 +18,24 @@ import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.mapping.Collection; -import org.hibernate.mapping.IndexedCollection; import org.hibernate.mapping.IndexedConsumer; import org.hibernate.mapping.List; import org.hibernate.mapping.Property; -import org.hibernate.mapping.SimpleValue; -import org.hibernate.mapping.Value; -import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor; import org.hibernate.metamodel.mapping.CollectionMappingType; import org.hibernate.metamodel.mapping.CollectionPart; -import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; -import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.mapping.ordering.OrderByFragment; import org.hibernate.metamodel.mapping.ordering.OrderByFragmentTranslator; import org.hibernate.metamodel.mapping.ordering.TranslationContext; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.persister.collection.CollectionPersister; -import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.Joinable; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.NavigablePath; @@ -58,11 +48,11 @@ import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlSelection; -import org.hibernate.sql.ast.tree.from.StandardTableGroup; +import org.hibernate.sql.ast.tree.from.CollectionTableGroup; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.from.TableReferenceJoin; +import org.hibernate.sql.ast.tree.from.OneToManyTableGroup; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; @@ -74,7 +64,6 @@ import org.hibernate.sql.results.graph.collection.internal.DelayedCollectionFetc import org.hibernate.sql.results.graph.collection.internal.EagerCollectionFetch; import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollectionFetch; import org.hibernate.tuple.ValueGeneration; -import org.hibernate.type.EntityType; import org.jboss.logging.Logger; @@ -113,45 +102,10 @@ public class PluralAttributeMappingImpl private final IndexMetadata indexMetadata; private ForeignKeyDescriptor fkDescriptor; - private ForeignKeyDescriptor elementFkDescriptor; - private ForeignKeyDescriptor indexFkDescriptor; private OrderByFragment orderByFragment; private OrderByFragment manyToManyOrderByFragment; - @SuppressWarnings({"WeakerAccess", "rawtypes"}) - public PluralAttributeMappingImpl( - String attributeName, - Collection bootDescriptor, - PropertyAccess propertyAccess, - StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess, - CollectionMappingType collectionMappingType, - int stateArrayPosition, - CollectionPart elementDescriptor, - CollectionPart indexDescriptor, - CollectionIdentifierDescriptor identifierDescriptor, - FetchOptions fetchOptions, - CascadeStyle cascadeStyle, - ManagedMappingType declaringType, - CollectionPersister collectionDescriptor) { - this( - attributeName, - bootDescriptor, - propertyAccess, - stateArrayContributorMetadataAccess, - collectionMappingType, - stateArrayPosition, - elementDescriptor, - indexDescriptor, - identifierDescriptor, - fetchOptions.getTiming(), - fetchOptions.getStyle(), - cascadeStyle, - declaringType, - collectionDescriptor - ); - } - @SuppressWarnings({"WeakerAccess", "rawtypes"}) public PluralAttributeMappingImpl( String attributeName, @@ -243,40 +197,6 @@ public class PluralAttributeMappingImpl Property bootProperty, Collection bootDescriptor, MappingModelCreationProcess creationProcess) { - final Dialect dialect = creationProcess.getCreationContext() - .getSessionFactory() - .getJdbcServices() - .getDialect(); - if ( collectionDescriptor.getElementType() instanceof EntityType ) { - creationProcess.registerForeignKeyPostInitCallbacks( - "To-many key - " + getNavigableRole(), - () -> { - elementFkDescriptor = createForeignKeyDescriptor( - bootDescriptor.getElement(), - (EntityType) collectionDescriptor.getElementType(), - creationProcess, - dialect - ); - return true; - } - ); - } - if ( collectionDescriptor.getIndexType() instanceof EntityType ) { - creationProcess.registerForeignKeyPostInitCallbacks( - "To-many index - " + getNavigableRole(), - () -> { - indexFkDescriptor = createForeignKeyDescriptor( - ( (IndexedCollection) bootDescriptor ).getIndex(), - (EntityType) collectionDescriptor.getIndexType(), - creationProcess, - dialect - ); - - return true; - } - ); - } - final boolean hasOrder = bootDescriptor.getOrderBy() != null; final boolean hasManyToManyOrder = bootDescriptor.getManyToManyOrdering() != null; @@ -315,59 +235,6 @@ public class PluralAttributeMappingImpl } } - private ForeignKeyDescriptor createForeignKeyDescriptor( - Value fkBootDescriptorSource, - EntityType entityType, - MappingModelCreationProcess creationProcess, - Dialect dialect) { - final EntityPersister associatedEntityDescriptor = creationProcess.getEntityPersister( entityType.getAssociatedEntityName() ); - final ModelPart fkTargetPart = entityType.isReferenceToPrimaryKey() - ? associatedEntityDescriptor.getIdentifierMapping() - : associatedEntityDescriptor.findSubPart( entityType.getRHSUniqueKeyPropertyName() ); - - if ( fkTargetPart instanceof BasicValuedModelPart ) { - final BasicValuedModelPart basicFkTargetPart = (BasicValuedModelPart) fkTargetPart; - final Joinable collectionDescriptorAsJoinable = (Joinable) collectionDescriptor; - final SelectableMapping keySelectableMapping = SelectableMappingImpl.from( - collectionDescriptorAsJoinable.getTableName(), - fkBootDescriptorSource.getColumnIterator().next(), - basicFkTargetPart.getJdbcMapping(), - dialect, - creationProcess.getSqmFunctionRegistry() - ); - final boolean hasConstraint; - if ( fkBootDescriptorSource instanceof SimpleValue ) { - hasConstraint = ( (SimpleValue) fkBootDescriptorSource ).isConstrained(); - } - else { - // We assume there is a constraint if the key is not nullable - hasConstraint = !fkBootDescriptorSource.isNullable(); - } - return new SimpleForeignKeyDescriptor( - basicFkTargetPart, - null, - keySelectableMapping, - basicFkTargetPart, - entityType.isReferenceToPrimaryKey(), - hasConstraint - ); - } - else if ( fkTargetPart instanceof EmbeddableValuedModelPart ) { - return MappingModelCreationHelper.buildEmbeddableForeignKeyDescriptor( - (EmbeddableValuedModelPart) fkTargetPart, - fkBootDescriptorSource, - dialect, - creationProcess - ); - } - else { - throw new NotYetImplementedFor6Exception( - "Support for composite foreign keys not yet implemented : " + collectionDescriptor - .getRole() - ); - } - } - @Override public NavigableRole getNavigableRole() { return getCollectionDescriptor().getNavigableRole(); @@ -482,6 +349,10 @@ public class PluralAttributeMappingImpl assert collectionTableGroup != null; + // This is only used for collection initialization where we know the owner is available, so we mark it as visited + // which will cause bidirectional to-one associations to be treated as such and avoid a join + creationState.registerVisitedAssociationKey( fkDescriptor.getAssociationKey() ); + //noinspection unchecked return new CollectionDomainResult( navigablePath, this, resultVariable, tableGroup, creationState ); } @@ -498,7 +369,7 @@ public class PluralAttributeMappingImpl creationState.registerVisitedAssociationKey( fkDescriptor.getAssociationKey() ); - if ( fetchTiming == FetchTiming.IMMEDIATE) { + if ( fetchTiming == FetchTiming.IMMEDIATE ) { if ( selected ) { final TableGroup collectionTableGroup = resolveCollectionTableGroup( fetchParent, @@ -628,29 +499,26 @@ public class PluralAttributeMappingImpl SqlAliasBaseGenerator aliasBaseGenerator, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { + final java.util.List predicates = new ArrayList<>( 2 ); final TableGroup tableGroup = createRootTableGroupJoin( navigablePath, lhs, explicitSourceAlias, sqlAstJoinType, fetched, - null, + predicates::add, aliasBaseGenerator, sqlExpressionResolver, creationContext ); - return new TableGroupJoin( + final TableGroupJoin tableGroupJoin = new TableGroupJoin( navigablePath, sqlAstJoinType, tableGroup, - getKeyDescriptor().generateJoinPredicate( - lhs, - tableGroup, - sqlAstJoinType, - sqlExpressionResolver, - creationContext - ) + null ); + predicates.forEach( tableGroupJoin::applyPredicate ); + return tableGroupJoin; } @Override @@ -715,125 +583,36 @@ public class PluralAttributeMappingImpl SqlAliasBase sqlAliasBase, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { - final EntityMappingType elementDescriptorEntityMappingType; - if ( elementDescriptor instanceof EntityCollectionPart ) { - elementDescriptorEntityMappingType = ( (EntityCollectionPart) elementDescriptor ).getEntityMappingType(); - } - else { - assert indexDescriptor instanceof EntityCollectionPart; - elementDescriptorEntityMappingType = null; - } - - final EntityMappingType indexDescriptorEntityMappingType; - if ( indexDescriptor instanceof EntityCollectionPart ) { - indexDescriptorEntityMappingType = ( (EntityCollectionPart) indexDescriptor ).getEntityMappingType(); - } - else { - indexDescriptorEntityMappingType = null; - } - - if ( indexDescriptorEntityMappingType == null || elementDescriptorEntityMappingType == null ) { - final EntityMappingType entityMappingType; - if ( indexDescriptorEntityMappingType == null ) { - entityMappingType = elementDescriptorEntityMappingType.getEntityMappingType(); - } - else { - entityMappingType = indexDescriptorEntityMappingType.getEntityMappingType(); - } - final TableReference primaryTableReference = entityMappingType - .createPrimaryTableReference( - sqlAliasBase, - sqlExpressionResolver, - creationContext - ); - - return new StandardTableGroup( - canUseInnerJoins, - navigablePath, - this, - fetched, - sourceAlias, - primaryTableReference, - true, - sqlAliasBase, - entityMappingType::containsTableReference, - (tableExpression, tg) -> entityMappingType.createTableReferenceJoin( - tableExpression, - sqlAliasBase, - primaryTableReference, - sqlExpressionResolver, - creationContext - ), - creationContext.getSessionFactory() - ); - } - final TableReference primaryTableReference = elementDescriptorEntityMappingType - .createPrimaryTableReference( - sqlAliasBase, - sqlExpressionResolver, - creationContext - ); - - final BiFunction tableReferenceJoinCreator; - - final java.util.function.Predicate tableReferenceJoinNameChecker = createTableReferenceJoinNameChecker( - elementDescriptorEntityMappingType, - indexDescriptorEntityMappingType - ); - - final TableReference indexAssociatedPrimaryTable = indexDescriptorEntityMappingType.createPrimaryTableReference( + final TableGroup elementTableGroup = ( (EntityCollectionPart) elementDescriptor ).createTableGroupInternal( + canUseInnerJoins, + navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), + fetched, + sourceAlias, sqlAliasBase, sqlExpressionResolver, creationContext ); - - final Function indexTableGroupFinalizer = createTableGroupFinalizer( - sqlExpressionResolver, - creationContext, - primaryTableReference, - indexAssociatedPrimaryTable, - SqlAstJoinType.INNER, - indexFkDescriptor - ); - - tableReferenceJoinCreator = (tableExpression, tableGroup) -> { - if ( elementDescriptorEntityMappingType.containsTableReference( tableExpression ) ) { - return elementDescriptorEntityMappingType.createTableReferenceJoin( - tableExpression, - sqlAliasBase, - primaryTableReference, - sqlExpressionResolver, - creationContext - ); - } - else if ( indexDescriptorEntityMappingType.containsTableReference( tableExpression ) ) { - return createTableReferenceJoin( - sqlExpressionResolver, - creationContext, - sqlAliasBase, - indexDescriptorEntityMappingType, - indexAssociatedPrimaryTable, - indexTableGroupFinalizer, - tableExpression, - tableGroup - ); - } - throw new IllegalStateException( "could not create join for table `" + tableExpression + "`" ); - }; - - return new StandardTableGroup( - canUseInnerJoins, - navigablePath, + final OneToManyTableGroup tableGroup = new OneToManyTableGroup( this, - fetched, - sourceAlias, - primaryTableReference, - true, - sqlAliasBase, - tableReferenceJoinNameChecker, - tableReferenceJoinCreator, + elementTableGroup, creationContext.getSessionFactory() ); + + if ( indexDescriptor instanceof EntityCollectionPart ) { + final TableGroupJoin tableGroupJoin = ( (EntityCollectionPart) indexDescriptor ).createTableGroupJoin( + navigablePath.append( CollectionPart.Nature.INDEX.getName() ), + elementTableGroup, + null, + SqlAstJoinType.INNER, + fetched, + stem -> sqlAliasBase, + sqlExpressionResolver, + creationContext + ); + tableGroup.registerIndexTableGroup( tableGroupJoin ); + } + + return tableGroup; } private TableGroup createCollectionTableGroup( @@ -854,116 +633,7 @@ public class PluralAttributeMappingImpl creationContext.getSessionFactory() ); - final EntityMappingType elementDescriptorEntityMappingType; - if ( elementDescriptor instanceof EntityCollectionPart ) { - elementDescriptorEntityMappingType = ( (EntityCollectionPart) elementDescriptor ).getEntityMappingType(); - } - else { - elementDescriptorEntityMappingType = null; - } - - final EntityMappingType indexDescriptorEntityMappingType; - if ( indexDescriptor instanceof EntityCollectionPart ) { - indexDescriptorEntityMappingType = ( (EntityCollectionPart) indexDescriptor ).getEntityMappingType(); - } - else { - indexDescriptorEntityMappingType = null; - } - - final BiFunction tableReferenceJoinCreator; - - final java.util.function.Predicate tableReferenceJoinNameChecker = createTableReferenceJoinNameChecker( - elementDescriptorEntityMappingType, - indexDescriptorEntityMappingType - ); - - final TableReference elementAssociatedPrimaryTable; - final Function elementTableGroupFinalizer; - // todo (6.0) : not sure it is - if ( elementDescriptorEntityMappingType != null ) { - elementAssociatedPrimaryTable = elementDescriptorEntityMappingType.createPrimaryTableReference( - sqlAliasBase, - sqlExpressionResolver, - creationContext - ); - - elementTableGroupFinalizer = createTableGroupFinalizer( - sqlExpressionResolver, - creationContext, - collectionTableReference, - elementAssociatedPrimaryTable, - SqlAstJoinType.INNER, - elementFkDescriptor - ); - } - else { - elementAssociatedPrimaryTable = null; - elementTableGroupFinalizer = null; - } - - TableReference indexAssociatedPrimaryTable; - final Function indexTableGroupFinalizer; - if ( indexDescriptorEntityMappingType != null ) { - indexAssociatedPrimaryTable = indexDescriptorEntityMappingType.createPrimaryTableReference( - sqlAliasBase, - sqlExpressionResolver, - creationContext - ); - - indexTableGroupFinalizer = createTableGroupFinalizer( - sqlExpressionResolver, - creationContext, - collectionTableReference, - indexAssociatedPrimaryTable, - SqlAstJoinType.INNER, - indexFkDescriptor - ); - } - else { - indexAssociatedPrimaryTable = null; - indexTableGroupFinalizer = null; - } - - if ( elementDescriptorEntityMappingType != null || indexDescriptorEntityMappingType != null ) { - tableReferenceJoinCreator = (tableExpression, tableGroup) -> { - if ( elementDescriptorEntityMappingType != null - && elementDescriptorEntityMappingType.containsTableReference( tableExpression ) ) { - return createTableReferenceJoin( - sqlExpressionResolver, - creationContext, - sqlAliasBase, - elementDescriptorEntityMappingType, - elementAssociatedPrimaryTable, - elementTableGroupFinalizer, - tableExpression, - tableGroup - ); - } - else if ( indexDescriptorEntityMappingType != null - && indexDescriptorEntityMappingType.containsTableReference( tableExpression ) ) { - return createTableReferenceJoin( - sqlExpressionResolver, - creationContext, - sqlAliasBase, - indexDescriptorEntityMappingType, - indexAssociatedPrimaryTable, - indexTableGroupFinalizer, - tableExpression, - tableGroup - ); - } - throw new IllegalStateException( "could not create join for table `" + tableExpression + "`" ); - }; - } - else { - tableReferenceJoinCreator = (tableExpression, tableGroup) -> { - throw new UnsupportedOperationException( - "element-collection cannot contain joins : " + collectionTableReference.getTableExpression() + " -> " + tableExpression - ); - }; - } - - final StandardTableGroup tableGroup = new StandardTableGroup( + final CollectionTableGroup tableGroup = new CollectionTableGroup( canUseInnerJoins, navigablePath, this, @@ -972,86 +642,39 @@ public class PluralAttributeMappingImpl collectionTableReference, true, sqlAliasBase, - tableReferenceJoinNameChecker, - tableReferenceJoinCreator, + s -> false, + null, creationContext.getSessionFactory() ); - return tableGroup; - } - - private TableReferenceJoin createTableReferenceJoin( - SqlExpressionResolver sqlExpressionResolver, - SqlAstCreationContext creationContext, - SqlAliasBase sqlAliasBase, - EntityMappingType elementDescriptorEntityMappingType, - TableReference elementAssociatedPrimaryTable, - Function elementTableGroupFinalizer, - String tableExpression, TableGroup tableGroup) { - TableReferenceJoin elementTableReferenceJoin; - if ( elementAssociatedPrimaryTable.getTableExpression().equals( tableExpression ) ) { - return elementTableGroupFinalizer.apply( tableGroup ); - } - else { - StandardTableGroup standardTableGroup = (StandardTableGroup) tableGroup; - if ( standardTableGroup.getTableReferenceJoins().isEmpty() ) { - elementTableReferenceJoin = elementTableGroupFinalizer.apply( tableGroup ); - } - else { - elementTableReferenceJoin = null; - } - } - final TableReferenceJoin tableReferenceJoin = elementDescriptorEntityMappingType.createTableReferenceJoin( - tableExpression, - sqlAliasBase, - elementAssociatedPrimaryTable, - sqlExpressionResolver, - creationContext - ); - if ( tableReferenceJoin != null && elementTableReferenceJoin != null ) { - ( (StandardTableGroup) tableGroup ).addTableReferenceJoin( elementTableReferenceJoin ); - return tableReferenceJoin; - } - return elementTableReferenceJoin == null ? tableReferenceJoin : elementTableReferenceJoin; - } - - private Function createTableGroupFinalizer( - SqlExpressionResolver sqlExpressionResolver, - SqlAstCreationContext creationContext, - TableReference collectionTableReference, - TableReference elementAssociatedPrimaryTable, - SqlAstJoinType joinType, - ForeignKeyDescriptor elementFkDescriptor) { - return tableGroup -> { - final TableReferenceJoin associationJoin = new TableReferenceJoin( - joinType, - elementAssociatedPrimaryTable, - elementFkDescriptor.generateJoinPredicate( - elementAssociatedPrimaryTable, - collectionTableReference, - joinType, - sqlExpressionResolver, - creationContext - ) + if ( indexDescriptor instanceof EntityCollectionPart ) { + final TableGroupJoin tableGroupJoin = ( (EntityCollectionPart) indexDescriptor ).createTableGroupJoin( + navigablePath.append( CollectionPart.Nature.INDEX.getName() ), + tableGroup, + null, + SqlAstJoinType.INNER, + fetched, + stem -> sqlAliasBase, + sqlExpressionResolver, + creationContext ); - return associationJoin; - }; - } + tableGroup.registerIndexTableGroup( tableGroupJoin ); + } + if ( elementDescriptor instanceof EntityCollectionPart ) { + final TableGroupJoin tableGroupJoin = ( (EntityCollectionPart) elementDescriptor ).createTableGroupJoin( + navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ), + tableGroup, + null, + SqlAstJoinType.INNER, + fetched, + stem -> sqlAliasBase, + sqlExpressionResolver, + creationContext + ); + tableGroup.registerElementTableGroup( tableGroupJoin ); + } - private java.util.function.Predicate createTableReferenceJoinNameChecker( - EntityMappingType elementDescriptorEntityMappingType, - EntityMappingType indexDescriptorEntityMappingType) { - return tableExpression -> { - if ( elementDescriptorEntityMappingType != null - && elementDescriptorEntityMappingType.containsTableReference( tableExpression ) ) { - return true; - } - if ( indexDescriptorEntityMappingType != null - && indexDescriptorEntityMappingType.containsTableReference( tableExpression ) ) { - return true; - } - return false; - }; + return tableGroup; } @Override @@ -1068,7 +691,7 @@ public class PluralAttributeMappingImpl explicitSourceAlias, additionalPredicateCollectorAccess, creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ), - creationState, + creationState.getSqlExpressionResolver(), creationContext ); } @@ -1080,7 +703,7 @@ public class PluralAttributeMappingImpl String explicitSourceAlias, Supplier> additionalPredicateCollectorAccess, SqlAliasBase sqlAliasBase, - SqlAstCreationState creationState, + SqlExpressionResolver expressionResolver, SqlAstCreationContext creationContext) { if ( getCollectionDescriptor().isOneToMany() ) { return createOneToManyTableGroup( @@ -1089,7 +712,7 @@ public class PluralAttributeMappingImpl false, explicitSourceAlias, sqlAliasBase, - creationState.getSqlExpressionResolver(), + expressionResolver, creationContext ); } @@ -1100,7 +723,7 @@ public class PluralAttributeMappingImpl false, explicitSourceAlias, sqlAliasBase, - creationState.getSqlExpressionResolver(), + expressionResolver, creationContext ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java index 7376c8e521..1d69de6c41 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SimpleForeignKeyDescriptor.java @@ -19,6 +19,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PropertyBasedMapping; @@ -37,8 +38,8 @@ import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.results.graph.DomainResult; @@ -61,16 +62,18 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa private AssociationKey associationKey; public SimpleForeignKeyDescriptor( + ManagedMappingType keyDeclaringType, BasicValuedModelPart keyModelPart, PropertyAccess keyPropertyAccess, SelectableMapping keySelectableMapping, BasicValuedModelPart targetModelPart, boolean refersToPrimaryKey, boolean hasConstraint) { - this( keyModelPart, keyPropertyAccess, keySelectableMapping, targetModelPart, refersToPrimaryKey, hasConstraint, false ); + this( keyDeclaringType, keyModelPart, keyPropertyAccess, keySelectableMapping, targetModelPart, refersToPrimaryKey, hasConstraint, false ); } public SimpleForeignKeyDescriptor( + ManagedMappingType keyDeclaringType, BasicValuedModelPart keyModelPart, PropertyAccess keyPropertyAccess, SelectableMapping keySelectableMapping, @@ -82,6 +85,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa assert targetModelPart != null; keyModelPart = BasicAttributeMapping.withSelectableMapping( + keyDeclaringType, keyModelPart, keyPropertyAccess, NoValueGeneration.INSTANCE, @@ -131,9 +135,12 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa @Override public ForeignKeyDescriptor withKeySelectionMapping( + ManagedMappingType declaringType, + TableGroupProducer declaringTableGroupProducer, IntFunction selectableMappingAccess, MappingModelCreationProcess creationProcess) { return new SimpleForeignKeyDescriptor( + declaringType, keySide.getModelPart(), ( (PropertyBasedMapping) keySide.getModelPart() ).getPropertyAccess(), selectableMappingAccess.apply( 0 ), @@ -148,8 +155,6 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { - assert tableGroup.getTableReference( navigablePath, keySide.getModelPart().getContainingTableExpression() ) != null; - return createDomainResult( navigablePath, tableGroup, @@ -160,8 +165,6 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa @Override public DomainResult createTargetDomainResult(NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { - assert tableGroup.getTableReference( navigablePath, targetSide.getModelPart().getContainingTableExpression() ) != null; - return createDomainResult( navigablePath, tableGroup, @@ -210,7 +213,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); final TableReference tableReference = tableGroup.resolveTableReference( - navigablePath.append( getNavigableRole().getNavigableName() ), + navigablePath.append( getTargetPart().getFetchableName() ), selectableMapping.getContainingTableExpression() ); final String identificationVariable = tableReference.getIdentificationVariable(); @@ -269,11 +272,11 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa SqlAstJoinType sqlAstJoinType, SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { - final TableReference lhsTableReference = targetSideTableGroup.getTableReference( + final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference( targetSideTableGroup.getNavigablePath(), targetSide.getModelPart().getContainingTableExpression() ); - final TableReference rhsTableKeyReference = keySideTableGroup.getTableReference( + final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference( keySide.getModelPart().getContainingTableExpression() ); @@ -287,7 +290,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa } protected TableReference getTableReference(TableGroup lhs, TableGroup tableGroup, String table) { - final NavigablePath navigablePath = lhs.getNavigablePath().append( getNavigableRole().getNavigableName() ); + final NavigablePath navigablePath = lhs.getNavigablePath().append( getTargetPart().getFetchableName() ); if ( lhs.getPrimaryTableReference().getTableReference( navigablePath, table ) != null ) { return lhs.getPrimaryTableReference(); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java index 733a224314..91d53ffbe3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java @@ -6,6 +6,7 @@ */ package org.hibernate.metamodel.mapping.internal; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; @@ -17,6 +18,7 @@ import org.hibernate.LockMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.ArrayHelper; @@ -48,6 +50,7 @@ import org.hibernate.persister.entity.EntityPersister; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.EntityIdentifierNavigablePath; import org.hibernate.query.NavigablePath; +import org.hibernate.query.TreatedNavigablePath; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.spi.FromClauseAccess; @@ -86,6 +89,7 @@ import org.hibernate.tuple.IdentifierProperty; import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.type.ComponentType; import org.hibernate.type.CompositeType; +import org.hibernate.type.EntityType; import org.hibernate.type.Type; /** @@ -360,22 +364,50 @@ public class ToOneAttributeMapping if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded() && compositeType.getPropertyNames().length == 1 ) { this.targetKeyPropertyName = compositeType.getPropertyNames()[0]; - addPrefixedPropertyNames( targetKeyPropertyNames, targetKeyPropertyName, compositeType.getSubtypes()[0] ); + addPrefixedPropertyNames( + targetKeyPropertyNames, + targetKeyPropertyName, + compositeType.getSubtypes()[0], + declaringEntityPersister.getFactory() + ); } else { this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME; - addPrefixedPropertyNames( targetKeyPropertyNames, null, propertyType ); + addPrefixedPropertyNames( + targetKeyPropertyNames, + null, + propertyType, + declaringEntityPersister.getFactory() + ); + addPrefixedPropertyNames( + targetKeyPropertyNames, + targetKeyPropertyName, + propertyType, + declaringEntityPersister.getFactory() + ); } } else { this.targetKeyPropertyName = identifierProperty.getName(); - addPrefixedPropertyNames( targetKeyPropertyNames, targetKeyPropertyName, propertyType ); + addPrefixedPropertyNames( + targetKeyPropertyNames, + targetKeyPropertyName, + propertyType, + declaringEntityPersister.getFactory() + ); } this.targetKeyPropertyNames = targetKeyPropertyNames; } else if ( bootValue.isReferenceToPrimaryKey() ) { this.targetKeyPropertyName = referencedPropertyName; - this.targetKeyPropertyNames = Collections.singleton( targetKeyPropertyName ); + final Set targetKeyPropertyNames = new HashSet<>( 2 ); + addPrefixedPropertyNames( + targetKeyPropertyNames, + targetKeyPropertyName, + bootValue.getType(), + declaringEntityPersister.getFactory() + ); + this.targetKeyPropertyNames = targetKeyPropertyNames; } else { final EntityMetamodel entityMetamodel = entityMappingType.getEntityPersister().getEntityMetamodel(); @@ -389,18 +421,35 @@ public class ToOneAttributeMapping } else { this.targetKeyPropertyName = referencedPropertyName; - this.targetKeyPropertyNames = Collections.singleton( targetKeyPropertyName ); + final String mapsIdAttributeName; + if ( ( mapsIdAttributeName = mapsId( entityMappingType, referencedPropertyName ) ) != null ) { + final Set targetKeyPropertyNames = new HashSet<>( 2 ); + targetKeyPropertyNames.add( targetKeyPropertyName ); + addPrefixedPropertyNames( + targetKeyPropertyNames, + mapsIdAttributeName, + entityMappingType.getEntityPersister().getIdentifierType(), + declaringEntityPersister.getFactory() + ); + this.targetKeyPropertyNames = targetKeyPropertyNames; + } + else { + this.targetKeyPropertyNames = Collections.singleton( targetKeyPropertyName ); + } } } } - private ToOneAttributeMapping(ToOneAttributeMapping original) { + private ToOneAttributeMapping( + ToOneAttributeMapping original, + ManagedMappingType declaringType, + TableGroupProducer declaringTableGroupProducer) { super( original.getAttributeName(), original.getStateArrayPosition(), original.getAttributeMetadataAccess(), original, - original.getDeclaringType(), + declaringType, original.getPropertyAccess(), original.getValueGeneration() ); @@ -417,7 +466,7 @@ public class ToOneAttributeMapping this.targetKeyPropertyNames = original.targetKeyPropertyNames; this.cardinality = original.cardinality; this.bidirectionalAttributeName = original.bidirectionalAttributeName; - this.declaringTableGroupProducer = original.declaringTableGroupProducer; + this.declaringTableGroupProducer = declaringTableGroupProducer; this.isConstrained = original.isConstrained; } @@ -438,10 +487,19 @@ public class ToOneAttributeMapping return true; } - private static void addPrefixedPropertyNames( + static String mapsId(EntityMappingType entityMappingType, String referencedPropertyName) { + final AbstractEntityPersister persister = (AbstractEntityPersister) entityMappingType.getEntityPersister(); + if ( Arrays.equals( persister.getKeyColumnNames(), persister.getPropertyColumnNames( referencedPropertyName ) ) ) { + return persister.getIdentifierPropertyName(); + } + return null; + } + + static void addPrefixedPropertyNames( Set targetKeyPropertyNames, String prefix, - Type type) { + Type type, + SessionFactoryImplementor factory) { if ( prefix != null ) { targetKeyPropertyNames.add( prefix ); } @@ -457,13 +515,32 @@ public class ToOneAttributeMapping else { newPrefix = prefix + "." + propertyNames[i]; } - addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, componentTypeSubtypes[i] ); + addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, componentTypeSubtypes[i], factory ); } } + else if ( type.isEntityType() ) { + final EntityType entityType = (EntityType) type; + final Type identifierOrUniqueKeyType = entityType.getIdentifierOrUniqueKeyType( factory ); + final String propertyName; + if ( entityType.isReferenceToPrimaryKey() ) { + propertyName = entityType.getAssociatedEntityPersister( factory ).getIdentifierPropertyName(); + } + else { + propertyName = entityType.getRHSUniqueKeyPropertyName(); + } + final String newPrefix; + if ( prefix == null ) { + newPrefix = propertyName; + } + else { + newPrefix = prefix + "." + propertyName; + } + addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, identifierOrUniqueKeyType, factory ); + } } - public ToOneAttributeMapping copy() { - return new ToOneAttributeMapping( this ); + public ToOneAttributeMapping copy(ManagedMappingType declaringType, TableGroupProducer declaringTableGroupProducer) { + return new ToOneAttributeMapping( this, declaringType, declaringTableGroupProducer ); } @Override @@ -538,7 +615,7 @@ public class ToOneAttributeMapping public ModelPart findSubPart(String name, EntityMappingType targetType) { // Prefer resolving the key part of the foreign key rather than the target part if possible // This way, we don't have to register table groups the target entity type - if ( canUseParentTableGroup && name.equals( targetKeyPropertyName ) ) { + if ( canUseParentTableGroup && targetKeyPropertyNames.contains( name ) ) { return foreignKeyDescriptor.getKeyPart(); } return EntityValuedFetchable.super.findSubPart( name, targetType ); @@ -606,7 +683,7 @@ public class ToOneAttributeMapping fetchablePath, fetchParent, parentNavigablePath, - LockMode.READ + creationState ); } @@ -695,14 +772,17 @@ public class ToOneAttributeMapping } } else if ( parentModelPart instanceof PluralAttributeMapping ) { - return ( (PluralAttributeMapping) parentModelPart ).isBidirectionalAttributeName( fetchablePath, this ); + // The parent must be non-null. If it is null, the root is a CollectionResult + return parentNavigablePath.getParent() != null + && ( (PluralAttributeMapping) parentModelPart ).isBidirectionalAttributeName( fetchablePath, this ); } else if ( parentModelPart instanceof EntityCollectionPart ) { NavigablePath parentOfParent = parentNavigablePath.getParent(); if ( parentOfParent instanceof EntityIdentifierNavigablePath ) { parentOfParent = parentOfParent.getParent(); } - return ( (PluralAttributeMapping) creationState.resolveModelPart( parentOfParent ) ) + // The parent must be non-null. If it is null, the root is a CollectionResult + return parentOfParent.getParent() != null && ( (PluralAttributeMapping) creationState.resolveModelPart( parentOfParent ) ) .isBidirectionalAttributeName( fetchablePath, this ); } return false; @@ -723,11 +803,18 @@ public class ToOneAttributeMapping this.mappedBy = "biologicalChild" parent.getFullPath() = "Mother.biologicalChild" */ - final String fullPath = parentNavigablePath.getFullPath(); - if ( fullPath.endsWith( bidirectionalAttributeName + "." + CollectionPart.Nature.ELEMENT.getName() ) ) { - final NavigablePath parentPath = parentNavigablePath.getParent().getParent(); + final NavigablePath grandparentNavigablePath = parentNavigablePath.getParent(); + if ( parentNavigablePath.getUnaliasedLocalName().equals( CollectionPart.Nature.ELEMENT.getName() ) + && grandparentNavigablePath != null + && grandparentNavigablePath.getUnaliasedLocalName().equals( bidirectionalAttributeName ) ) { + final NavigablePath parentPath = grandparentNavigablePath.getParent(); // This can be null for a collection loader - if ( parentPath != null ) { + if ( parentPath == null ) { + return grandparentNavigablePath.getFullPath().equals( + entityMappingType.findSubPart( bidirectionalAttributeName ).getNavigableRole().getFullPath() + ); + } + else { // If the parent is null, this is a simple collection fetch of a root, in which case the types must match if ( parentPath.getParent() == null ) { final String entityName = entityMappingType.getPartName(); @@ -756,22 +843,83 @@ public class ToOneAttributeMapping NavigablePath fetchablePath, FetchParent fetchParent, NavigablePath parentNavigablePath, - LockMode lockMode) { - NavigablePath referencedNavigablePath; + DomainResultCreationState creationState) { + final NavigablePath referencedNavigablePath; + final boolean hasBidirectionalFetchParent; + FetchParent realFetchParent = fetchParent; + // Traverse up the embeddable fetches + while ( realFetchParent.getNavigablePath() != parentNavigablePath ) { + realFetchParent = ( (Fetch) fetchParent ).getFetchParent(); + } if ( parentNavigablePath.getParent() == null ) { referencedNavigablePath = parentNavigablePath; + hasBidirectionalFetchParent = true; + } + else if ( CollectionPart.Nature.fromNameExact( parentNavigablePath.getUnaliasedLocalName() ) != null ) { + referencedNavigablePath = parentNavigablePath.getParent().getParent(); + hasBidirectionalFetchParent = fetchParent instanceof Fetch + && ( (Fetch) fetchParent ).getFetchParent() instanceof Fetch; } else { referencedNavigablePath = parentNavigablePath.getParent(); + hasBidirectionalFetchParent = fetchParent instanceof Fetch; + } + // The referencedNavigablePath can be null if this is a collection initialization + if ( referencedNavigablePath != null ) { + if ( hasBidirectionalFetchParent ) { + return new CircularBiDirectionalFetchImpl( + FetchTiming.IMMEDIATE, + fetchablePath, + fetchParent, + this, + LockMode.READ, + referencedNavigablePath + ); + } + else { + // A query like `select ch from Phone p join p.callHistory ch` returns collection element domain results + // but detects that Call#phone is bidirectional in the query. + // The problem with a bidirectional fetch though is that we can't find an initializer + // because there is none, as we don't fetch the data of the parent node. + // To avoid creating another join, we create a special join fetch that uses the existing joined data + final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess(); + final TableGroup tableGroup = fromClauseAccess.getTableGroup( referencedNavigablePath ); + fromClauseAccess.registerTableGroup( fetchablePath, tableGroup ); + return new EntityFetchJoinedImpl( + fetchParent, + this, + tableGroup, + false, + fetchablePath, + creationState + ); + } + } + else { + // We get here is this is a lazy collection initialization for which we know the owner is in the PC + // So we create a delayed fetch, as we are sure to find the entity in the PC + final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess(); + final NavigablePath realParent; + if ( CollectionPart.Nature.fromNameExact( parentNavigablePath.getUnaliasedLocalName() ) != null ) { + realParent = parentNavigablePath.getParent(); + } + else { + realParent = parentNavigablePath; + } + final TableGroup tableGroup = fromClauseAccess.getTableGroup( realParent ); + return new EntityDelayedFetchImpl( + fetchParent, + this, + fetchablePath, + foreignKeyDescriptor.createDomainResult( + fetchablePath, + tableGroup, + sideNature, + creationState + ), + isSelectByUniqueKey( sideNature ) + ); } - return new CircularBiDirectionalFetchImpl( - FetchTiming.IMMEDIATE, - fetchablePath, - fetchParent, - this, - lockMode, - referencedNavigablePath - ); } @Override @@ -791,7 +939,9 @@ public class ToOneAttributeMapping ); final NavigablePath parentNavigablePath = fetchablePath.getParent(); - assert parentNavigablePath.equals( fetchParent.getNavigablePath() ); + assert parentNavigablePath.equals( fetchParent.getNavigablePath() ) + || fetchParent.getNavigablePath() instanceof TreatedNavigablePath + && parentNavigablePath.equals( fetchParent.getNavigablePath().getRealParent() ); if ( fetchTiming == FetchTiming.IMMEDIATE && selected ) { final TableGroup tableGroup; @@ -905,7 +1055,8 @@ public class ToOneAttributeMapping fetchParent, this, fetchablePath, - keyResult + keyResult, + selectByUniqueKey ); } @@ -917,7 +1068,10 @@ public class ToOneAttributeMapping } else { // case 1.1 - return bidirectionalAttributeName != null; + // Make sure the entity identifier is not a target key property i.e. this really is a unique key mapping + return bidirectionalAttributeName != null && !targetKeyPropertyNames.contains( + entityMappingType.getEntityPersister().getEntityMetamodel().getIdentifierProperty().getName() + ); } } @@ -930,19 +1084,20 @@ public class ToOneAttributeMapping // We only need a join if the key is on the referring side i.e. this is an inverse to-one // and if the FK refers to a non-PK, in which case we must load the whole entity if ( sideNature == ForeignKeyDescriptor.Nature.TARGET || referencedPropertyName != null ) { - final TableGroupJoin tableGroupJoin = createTableGroupJoin( + creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup( navigablePath, - tableGroup, - null, - getDefaultSqlAstJoinType( tableGroup ), - true, - creationState.getSqlAstCreationState() - ); - tableGroup.addTableGroupJoin( tableGroupJoin ); - - creationState.getSqlAstCreationState().getFromClauseAccess().registerTableGroup( - navigablePath, - tableGroupJoin.getJoinedGroup() + np -> { + final TableGroupJoin tableGroupJoin = createTableGroupJoin( + navigablePath, + tableGroup, + null, + getDefaultSqlAstJoinType( tableGroup ), + true, + creationState.getSqlAstCreationState() + ); + tableGroup.addTableGroupJoin( tableGroupJoin ); + return tableGroupJoin.getJoinedGroup(); + } ); } if ( referencedPropertyName == null ) { @@ -955,12 +1110,15 @@ public class ToOneAttributeMapping } else { // We don't support proxies based on a non-PK yet, so we must fetch the whole entity - return new EntityResultImpl( + final EntityResultImpl entityResult = new EntityResultImpl( navigablePath, this, tableGroup, null, creationState ); + entityResult.afterInitialize( entityResult, creationState ); + //noinspection unchecked + return entityResult; } } @@ -980,25 +1138,6 @@ public class ToOneAttributeMapping } } - private TableGroup createTableGroupJoin( - NavigablePath fetchablePath, - boolean fetched, - SqlAstJoinType sqlAstJoinType, - String sourceAlias, - DomainResultCreationState creationState, - TableGroup parentTableGroup) { - final TableGroupJoin tableGroupJoin = createTableGroupJoin( - fetchablePath, - parentTableGroup, - sourceAlias, - sqlAstJoinType, - fetched, - creationState.getSqlAstCreationState() - ); - - return tableGroupJoin.getJoinedGroup(); - } - @Override public int getNumberOfFetchables() { return getEntityMappingType().getNumberOfFetchables(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java index 89d0a6f8a2..ad217684af 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/AbstractIdentifiableType.java @@ -401,7 +401,7 @@ public abstract class AbstractIdentifiableType else if ( nonAggregatedIdAttributes != null && ! nonAggregatedIdAttributes.isEmpty() ) { // non-aggregate composite id if ( idClassType == null ) { - return new NonAggregatedCompositeSqmPathSource( + return new NonAggregatedCompositeSqmPathSource<>( EntityIdentifierMapping.ROLE_LOCAL_NAME, Bindable.BindableType.SINGULAR_ATTRIBUTE, this diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java index 603339f901..8d1723622b 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/JpaMetamodelImpl.java @@ -592,7 +592,7 @@ public class JpaMetamodelImpl implements JpaMetamodel, Serializable { else { javaTypeDescriptor = context.getTypeConfiguration() .getJavaTypeDescriptorRegistry() - .resolveManagedTypeDescriptor( javaType ); + .resolveEntityTypeDescriptor( javaType ); } final EntityTypeImpl entityType = new EntityTypeImpl( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/NonAggregatedCompositeSqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/NonAggregatedCompositeSqmPathSource.java index 21698a2c52..b74487bde6 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/NonAggregatedCompositeSqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/NonAggregatedCompositeSqmPathSource.java @@ -16,11 +16,11 @@ import org.hibernate.query.sqm.tree.domain.SqmPath; * * @author Steve Ebersole */ -public class NonAggregatedCompositeSqmPathSource extends AbstractSqmPathSource implements CompositeSqmPathSource { +public class NonAggregatedCompositeSqmPathSource extends AbstractSqmPathSource implements CompositeSqmPathSource { public NonAggregatedCompositeSqmPathSource( String localName, BindableType bindableType, - ManagedDomainType container) { + ManagedDomainType container) { super( localName, container, bindableType ); } @@ -35,8 +35,8 @@ public class NonAggregatedCompositeSqmPathSource extends AbstractSqmPathSource i } @Override - public SqmPath createSqmPath(SqmPath lhs) { - return new NonAggregatedCompositeSimplePath( + public SqmPath createSqmPath(SqmPath lhs) { + return new NonAggregatedCompositeSimplePath<>( lhs.getNavigablePath().append( getPathName() ), this, lhs, diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 35e6fc0c7d..8e0df634c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -1171,7 +1171,7 @@ public abstract class AbstractCollectionPersister null, () -> p -> {}, new SqlAliasBaseConstant( alias ), - sqlAstCreationState, + sqlAstCreationState.getSqlExpressionResolver(), getFactory() ); @@ -1828,7 +1828,7 @@ public abstract class AbstractCollectionPersister buffer.append( " and " ); } assert elementPersister instanceof Joinable; - final TableReference tableReference = tableGroup.getTableReference( ( (Joinable) elementPersister ).getTableName() ); + final TableReference tableReference = tableGroup.resolveTableReference( ( (Joinable) elementPersister ).getTableName() ); buffer.append( StringHelper.replace( manyToManyWhereTemplate, Template.TEMPLATE, tableReference.getIdentificationVariable() ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index bb311e42df..6dd7023516 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -216,7 +216,6 @@ import org.hibernate.sql.ast.spi.SqlAliasBaseConstant; import org.hibernate.sql.ast.spi.SqlAliasBaseManager; import org.hibernate.sql.ast.spi.SqlAliasStemHelper; import org.hibernate.sql.ast.spi.SqlAstCreationContext; -import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.expression.AliasedExpression; @@ -1266,8 +1265,16 @@ public abstract class AbstractEntityPersister TableGroup tableGroup, String resultVariable, DomainResultCreationState creationState) { + final EntityResultImpl entityResult = new EntityResultImpl( + navigablePath, + this, + tableGroup, + resultVariable, + creationState + ); + entityResult.afterInitialize( entityResult, creationState ); //noinspection unchecked - return new EntityResultImpl( navigablePath, this, tableGroup, resultVariable, creationState ); + return entityResult; } @Override @@ -1313,10 +1320,8 @@ public abstract class AbstractEntityPersister String explicitSourceAlias, Supplier> additionalPredicateCollectorAccess, SqlAliasBase sqlAliasBase, - SqlAstCreationState creationState, + SqlExpressionResolver sqlExpressionResolver, SqlAstCreationContext creationContext) { - final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver(); - final TableReference primaryTableReference = createPrimaryTableReference( sqlAliasBase, sqlExpressionResolver, @@ -1905,7 +1910,7 @@ public abstract class AbstractEntityPersister null, () -> p -> {}, new SqlAliasBaseConstant( alias ), - sqlAstCreationState, + sqlAstCreationState.getSqlExpressionResolver(), getFactory() ); @@ -6399,6 +6404,10 @@ public abstract class AbstractEntityPersister } } + protected EntityMappingType getSubclassMappingType(String subclassName) { + return subclassMappingTypes != null ? subclassMappingTypes.get( subclassName ) : null; + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // EntityDefinition impl (walking model - deprecated) diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java index 14017794cb..300554affb 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/JoinedSubclassEntityPersister.java @@ -8,6 +8,8 @@ package org.hibernate.persister.entity; import java.io.Serializable; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -54,12 +56,14 @@ import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.query.NavigablePath; import org.hibernate.sql.InFragment; import org.hibernate.sql.Insert; +import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableReference; +import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.results.graph.DomainResult; @@ -1293,14 +1297,79 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { String resultVariable, DomainResultCreationState creationState) { if ( hasSubclasses() ) { + final EntityResultJoinedSubclassImpl entityResultJoinedSubclass = new EntityResultJoinedSubclassImpl( + navigablePath, + this, + tableGroup, + resultVariable, + creationState + ); + entityResultJoinedSubclass.afterInitialize( entityResultJoinedSubclass, creationState ); //noinspection unchecked - return new EntityResultJoinedSubclassImpl( navigablePath, this, tableGroup, resultVariable, creationState ); + return entityResultJoinedSubclass; } else { return super.createDomainResult( navigablePath, tableGroup, resultVariable, creationState ); } } + @Override + public void pruneForSubclasses(TableGroup tableGroup, Set treatedEntityNames) { + if ( treatedEntityNames.contains( getEntityName() ) ) { + return; + } + final Set retainedTableReferences = new HashSet<>( treatedEntityNames.size() ); + final Set sharedSuperclassTables = new HashSet<>(); + + for ( String treatedEntityName : treatedEntityNames ) { + final JoinedSubclassEntityPersister subPersister = (JoinedSubclassEntityPersister) getSubclassMappingType( treatedEntityName ); + final String[] subclassTableNames = subPersister.getSubclassTableNames(); + if ( tableGroup.canUseInnerJoins() ) { + if ( sharedSuperclassTables.isEmpty() ) { + for ( int i = 0; i < subclassTableNames.length; i++ ) { + if ( subPersister.isClassOrSuperclassTable[i] ) { + sharedSuperclassTables.add( subclassTableNames[i] ); + } + } + } + else { + sharedSuperclassTables.retainAll( Arrays.asList( subclassTableNames ) ); + } + } + // todo (6.0): no need to resolve all table references, only the ones needed for cardinality + for ( int i = 0; i < subclassTableNames.length; i++ ) { + retainedTableReferences.add( tableGroup.resolveTableReference( null, subclassTableNames[i], false ) ); + } + } + final List tableReferenceJoins = tableGroup.getTableReferenceJoins(); + if ( sharedSuperclassTables.isEmpty() ) { + tableReferenceJoins + .removeIf( join -> !retainedTableReferences.contains( join.getJoinedTableReference() ) ); + } + else { + final TableReferenceJoin[] oldJoins = tableReferenceJoins.toArray( new TableReferenceJoin[0] ); + tableReferenceJoins.clear(); + for ( TableReferenceJoin oldJoin : oldJoins ) { + final TableReference joinedTableReference = oldJoin.getJoinedTableReference(); + if ( retainedTableReferences.contains( joinedTableReference ) ) { + if ( oldJoin.getJoinType() != SqlAstJoinType.INNER + && sharedSuperclassTables.contains( joinedTableReference.getTableExpression() ) ) { + tableReferenceJoins.add( + new TableReferenceJoin( + SqlAstJoinType.INNER, + joinedTableReference, + oldJoin.getPredicate() + ) + ); + } + else { + tableReferenceJoins.add( oldJoin ); + } + } + } + } + } + @Override public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) { for ( int i = 0; i < constraintOrderedTableNames.length; i++ ) { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java index 4afc60d1fd..e3431d6f49 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/SingleTableEntityPersister.java @@ -47,12 +47,12 @@ import org.hibernate.sql.InFragment; import org.hibernate.sql.Insert; import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAstCreationContext; -import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.from.TableGroup; +import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; @@ -654,7 +654,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { if ( !queryable.isAbstract() ) { values.add( queryable.getDiscriminatorSQLValue() ); } - else if ( queryable.hasSubclasses() ) { + if ( queryable.hasSubclasses() ) { // if the treat is an abstract class, add the concrete implementations to values if any Set actualSubClasses = queryable.getEntityMetamodel().getSubclassEntityNames(); @@ -843,7 +843,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { String explicitSourceAlias, Supplier> additionalPredicateCollectorAccess, SqlAliasBase sqlAliasBase, - SqlAstCreationState creationState, + SqlExpressionResolver expressionResolver, SqlAstCreationContext creationContext) { final TableGroup tableGroup = super.createRootTableGroup( canUseInnerJoins, @@ -851,14 +851,14 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { explicitSourceAlias, additionalPredicateCollectorAccess, sqlAliasBase, - creationState, + expressionResolver, creationContext ); if ( additionalPredicateCollectorAccess != null && needsDiscriminator() ) { final Predicate discriminatorPredicate = createDiscriminatorPredicate( tableGroup, - creationState.getSqlExpressionResolver(), + expressionResolver, creationContext ); additionalPredicateCollectorAccess.get().accept( discriminatorPredicate ); @@ -952,6 +952,17 @@ public class SingleTableEntityPersister extends AbstractEntityPersister { ); } + @Override + public void pruneForSubclasses(TableGroup tableGroup, Set treatedEntityNames) { + if ( treatedEntityNames.contains( getEntityName() ) ) { + return; + } + final TableReference tableReference = tableGroup.getPrimaryTableReference(); + tableReference.setPrunedTableExpression( + "(select * from " + getTableName() + " t where " + discriminatorFilterFragment( "t", treatedEntityNames ) + ")" + ); + } + @Override public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) { for ( int i = 0; i < constraintOrderedTableNames.length; i++ ) { diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java index 69bf5cfebb..acbdd73ca9 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/UnionSubclassEntityPersister.java @@ -12,6 +12,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -42,12 +43,14 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Subclass; import org.hibernate.mapping.Table; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; +import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAstCreationContext; -import org.hibernate.sql.ast.spi.SqlAstCreationState; +import org.hibernate.sql.ast.spi.SqlExpressionResolver; +import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.UnionTableGroup; @@ -251,7 +254,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { String explicitSourceAlias, Supplier> additionalPredicateCollectorAccess, SqlAliasBase sqlAliasBase, - SqlAstCreationState creationState, + SqlExpressionResolver expressionResolver, SqlAstCreationContext creationContext) { final TableReference tableReference = resolvePrimaryTableReference( sqlAliasBase ); @@ -390,6 +393,14 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { return isAbstract() || hasSubclasses(); } + @Override + public void pruneForSubclasses(TableGroup tableGroup, Set treatedEntityNames) { + if ( treatedEntityNames.contains( getEntityName() ) ) { + return; + } + final TableReference tableReference = tableGroup.resolveTableReference( getRootTableName() ); + tableReference.setPrunedTableExpression( generateSubquery( treatedEntityNames ) ); + } @Override public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) { @@ -509,6 +520,68 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister { return buf.append( " )" ).toString(); } + protected String generateSubquery(Set treated) { + if ( !hasSubclasses() ) { + return getTableName(); + } + + final Dialect dialect = getFactory().getJdbcServices().getDialect(); + + final LinkedHashMap> selectables = new LinkedHashMap<>(); + visitSubTypeAttributeMappings( + attributeMapping -> attributeMapping.forEachSelectable( + (i, selectable) -> selectables.computeIfAbsent( selectable.getSelectionExpression(), k -> new HashMap<>() ) + .put( selectable.getContainingTableExpression(), selectable ) + ) + ); + final Set treatedTableNames = new HashSet<>( treated.size() ); + for ( String subclassName : treated ) { + final UnionSubclassEntityPersister subPersister = (UnionSubclassEntityPersister) getSubclassMappingType( subclassName ); + for ( String subclassTableName : subPersister.getSubclassTableNames() ) { + if ( ArrayHelper.indexOf( subclassSpaces, subclassTableName ) != -1 ) { + treatedTableNames.add( subclassTableName ); + } + } + } + + final StringBuilder buf = new StringBuilder( subquery.length() ) + .append( "( " ); + + for ( int i = 0; i < subclassTableNames.length; i++ ) { + final String subclassTableName = subclassTableNames[i]; + if ( treatedTableNames.contains( subclassTableName ) ) { + buf.append( "select " ); + for ( Map selectableMappings : selectables.values() ) { + SelectableMapping selectableMapping = selectableMappings.get( subclassTableName ); + if ( selectableMapping == null ) { + selectableMapping = selectableMappings.values().iterator().next(); + final int sqlType = selectableMapping.getJdbcMapping().getJdbcTypeDescriptor() + .getDefaultSqlTypeCode(); + buf.append( dialect.getSelectClauseNullString( sqlType ) ) + .append( " as " ); + } + buf.append( + new ColumnReference( (String) null, selectableMapping, getFactory() ).getExpressionText() + ); + buf.append( ", " ); + } + buf.append( i ).append( " as clazz_" ); + buf.append( " from " ).append( subclassTableName ); + buf.append( " union " ); + if ( dialect.supportsUnionAll() ) { + buf.append( "all " ); + } + } + } + + if ( buf.length() > 2 ) { + //chop the last union (all) + buf.setLength( buf.length() - ( dialect.supportsUnionAll() ? 11 : 7 ) ); + } + + return buf.append( " )" ).toString(); + } + @Override protected String[] getSubclassTableKeyColumns(int j) { if ( j != 0 ) { diff --git a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java index b553c64f5a..b416078cef 100644 --- a/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureParameterImpl.java @@ -88,6 +88,7 @@ public class ProcedureParameterImpl extends AbstractQueryParameter impleme return javaType; } + @Override public NamedCallableQueryMemento.ParameterMemento toMemento() { return session -> { if ( getName() != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/NavigablePath.java b/hibernate-core/src/main/java/org/hibernate/query/NavigablePath.java index 7ef823819f..a7d320a36f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/NavigablePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/NavigablePath.java @@ -101,6 +101,25 @@ public class NavigablePath implements DotIdentifierSequence, Serializable { this( "" ); } + NavigablePath( + NavigablePath parent, + String fullPath, + String unaliasedLocalName, + String identifierForTableGroup) { + this.parent = parent; + this.fullPath = fullPath; + this.unaliasedLocalName = unaliasedLocalName; + this.identifierForTableGroup = identifierForTableGroup; + } + + public NavigablePath treatAs(String entityName) { + return new TreatedNavigablePath( this, entityName ); + } + + public NavigablePath treatAs(String entityName, String alias) { + return new TreatedNavigablePath( this, entityName, alias ); + } + public NavigablePath append(String property) { return new NavigablePath( this, property ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/TreatedNavigablePath.java b/hibernate-core/src/main/java/org/hibernate/query/TreatedNavigablePath.java index 9ca05e1fda..30dbb057bf 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/TreatedNavigablePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/TreatedNavigablePath.java @@ -11,20 +11,39 @@ package org.hibernate.query; */ public class TreatedNavigablePath extends NavigablePath { - public static final String ROLE_LOCAL_NAME = "{treated}"; - public TreatedNavigablePath(NavigablePath parent, String entityTypeName) { - super( parent, ROLE_LOCAL_NAME, entityTypeName ); + this( parent, entityTypeName, null ); + } + + public TreatedNavigablePath(NavigablePath parent, String entityTypeName, String alias) { + super( + parent, + alias == null ? "treat(" + parent.getFullPath() + " as " + entityTypeName + ")" + : "treat(" + parent.getFullPath() + " as " + entityTypeName + ")(" + alias + ")", + entityTypeName, + "treat(" + parent.getFullPath() + " as " + entityTypeName + ")" + ); + assert !( parent instanceof TreatedNavigablePath ); + } + + @Override + public NavigablePath treatAs(String entityName) { + return new TreatedNavigablePath( getRealParent(), entityName ); + } + + @Override + public NavigablePath treatAs(String entityName, String alias) { + return new TreatedNavigablePath( getRealParent(), entityName, alias ); } @Override public String getLocalName() { - return ROLE_LOCAL_NAME; + return getUnaliasedLocalName(); } @Override public int hashCode() { - return getParent().getFullPath().hashCode(); + return getFullPath().hashCode(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaCollectionJoin.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaCollectionJoin.java index 50daef4393..2ade59a0a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaCollectionJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaCollectionJoin.java @@ -6,6 +6,10 @@ */ package org.hibernate.query.criteria; +import java.util.Collection; + +import org.hibernate.metamodel.model.domain.EntityDomainType; + import jakarta.persistence.criteria.CollectionJoin; import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Predicate; @@ -15,7 +19,7 @@ import jakarta.persistence.criteria.Predicate; * * @author Steve Ebersole */ -public interface JpaCollectionJoin extends JpaJoin, CollectionJoin { +public interface JpaCollectionJoin extends JpaPluralJoin, T>, CollectionJoin { @Override JpaCollectionJoin on(JpaExpression restriction); @@ -31,4 +35,7 @@ public interface JpaCollectionJoin extends JpaJoin, CollectionJoin JpaCollectionJoin treatAs(Class treatAsType); + + @Override + JpaCollectionJoin treatAs(EntityDomainType treatAsType); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaJoin.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaJoin.java index 3e58809eb4..78c3237f54 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaJoin.java @@ -11,6 +11,7 @@ import jakarta.persistence.criteria.Fetch; import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.Predicate; +import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.PersistentAttribute; /** @@ -32,4 +33,10 @@ public interface JpaJoin extends JpaJoinedFrom, Join { @Override JpaJoin on(Predicate... restrictions); + + @Override + JpaJoin treatAs(Class treatAsType); + + @Override + JpaJoin treatAs(EntityDomainType treatAsType); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaListJoin.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaListJoin.java index 14d539a0cf..8ab23eea6e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaListJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaListJoin.java @@ -6,6 +6,10 @@ */ package org.hibernate.query.criteria; +import java.util.List; + +import org.hibernate.metamodel.model.domain.EntityDomainType; + import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.ListJoin; import jakarta.persistence.criteria.Predicate; @@ -15,7 +19,7 @@ import jakarta.persistence.criteria.Predicate; * * @author Steve Ebersole */ -public interface JpaListJoin extends JpaJoin, ListJoin { +public interface JpaListJoin extends JpaPluralJoin, T>, ListJoin { @Override JpaListJoin on(JpaExpression restriction); @@ -30,4 +34,7 @@ public interface JpaListJoin extends JpaJoin, ListJoin { @Override JpaListJoin treatAs(Class treatAsType); + + @Override + JpaListJoin treatAs(EntityDomainType treatAsType); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaMapJoin.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaMapJoin.java index c0afcf2eca..709acf8f1f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaMapJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaMapJoin.java @@ -6,6 +6,11 @@ */ package org.hibernate.query.criteria; +import java.util.Map; + +import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.PathException; + import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.MapJoin; import jakarta.persistence.criteria.Predicate; @@ -15,7 +20,7 @@ import jakarta.persistence.criteria.Predicate; * * @author Steve Ebersole */ -public interface JpaMapJoin extends JpaJoin, MapJoin { +public interface JpaMapJoin extends JpaPluralJoin, V>, MapJoin { @Override JpaMapJoin on(JpaExpression restriction); @@ -29,4 +34,7 @@ public interface JpaMapJoin extends JpaJoin, MapJoin { JpaMapJoin on(Predicate... restrictions); JpaMapJoin treatAs(Class treatAsType); + + @Override + JpaMapJoin treatAs(EntityDomainType treatJavaType); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaPath.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaPath.java index 3d8d5c5abd..b045f3f137 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaPath.java @@ -37,12 +37,12 @@ public interface JpaPath extends JpaExpression, Path { /** * Support for JPA's explicit (TREAT) down-casting. */ - JpaPath treatAs(Class treatJavaType) throws PathException; + JpaPath treatAs(Class treatJavaType); /** * Support for JPA's explicit (TREAT) down-casting. */ - JpaPath treatAs(EntityDomainType treatJavaType) throws PathException; + JpaPath treatAs(EntityDomainType treatJavaType); // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaPluralJoin.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaPluralJoin.java new file mode 100644 index 0000000000..25256a1490 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaPluralJoin.java @@ -0,0 +1,36 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.criteria; + +import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; + +import jakarta.persistence.criteria.Expression; +import jakarta.persistence.criteria.PluralJoin; +import jakarta.persistence.criteria.Predicate; + +/** + * Specialization of {@link JpaJoin} for {@link java.util.Set} typed attribute joins + * + * @author Steve Ebersole + */ +public interface JpaPluralJoin extends JpaJoin, PluralJoin { + @Override + PluralPersistentAttribute getAttribute(); + + JpaPluralJoin on(JpaExpression restriction); + + JpaPluralJoin on(Expression restriction); + + JpaPluralJoin on(JpaPredicate... restrictions); + + JpaPluralJoin on(Predicate... restrictions); + + JpaPluralJoin treatAs(Class treatAsType); + + JpaPluralJoin treatAs(EntityDomainType treatAsType); +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaSetJoin.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaSetJoin.java index 35f015f409..89fd5a99af 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaSetJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/JpaSetJoin.java @@ -6,6 +6,11 @@ */ package org.hibernate.query.criteria; +import java.util.Set; + +import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; + import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.SetJoin; @@ -15,7 +20,7 @@ import jakarta.persistence.criteria.SetJoin; * * @author Steve Ebersole */ -public interface JpaSetJoin extends JpaJoin, SetJoin { +public interface JpaSetJoin extends JpaPluralJoin, T>, SetJoin { JpaSetJoin on(JpaExpression restriction); @@ -26,4 +31,6 @@ public interface JpaSetJoin extends JpaJoin, SetJoin { JpaSetJoin on(Predicate... restrictions); JpaSetJoin treatAs(Class treatAsType); + + JpaSetJoin treatAs(EntityDomainType treatAsType); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java index cdf2d14125..e28dc33d69 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/BasicDotIdentifierConsumer.java @@ -10,6 +10,7 @@ import java.lang.reflect.Field; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.criteria.JpaPath; import org.hibernate.query.hql.HqlLogging; import org.hibernate.query.hql.spi.DotIdentifierConsumer; import org.hibernate.query.hql.spi.SemanticPathPart; @@ -93,6 +94,13 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer { currentPart = currentPart.resolvePathPart( identifier, isTerminal, creationState ); } + @Override + public void consumeTreat(String entityName, boolean isTerminal) { + final EntityDomainType entityDomainType = creationState.getCreationContext().getJpaMetamodel() + .entity( entityName ); + currentPart = ( (SqmPath) currentPart ).treatAs( entityDomainType ); + } + protected void reset() { pathSoFar = null; currentPart = createBasePart(); @@ -249,13 +257,13 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer { throw new ParsingException( "Could not interpret dot-ident : " + pathSoFar ); } - protected void validateAsRoot(SqmFrom pathRoot) { + protected void validateAsRoot(SqmFrom pathRoot) { } @Override - public SqmPath resolveIndexedAccess( - SqmExpression selector, + public SqmPath resolveIndexedAccess( + SqmExpression selector, boolean isTerminal, SqmCreationState processingState) { return currentPart.resolveIndexedAccess( selector, isTerminal, processingState ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/DomainPathPart.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/DomainPathPart.java index 53dcea9480..846d16e842 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/DomainPathPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/DomainPathPart.java @@ -144,8 +144,8 @@ public class DomainPathPart implements SemanticPathPart { } @Override - public SqmPath resolveIndexedAccess( - SqmExpression selector, + public SqmPath resolveIndexedAccess( + SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { throw new NotYetImplementedFor6Exception( getClass() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePath.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePath.java index 1db09d2acc..a283b82493 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/FullyQualifiedReflectivePath.java @@ -41,7 +41,7 @@ public class FullyQualifiedReflectivePath implements SemanticPathPart, FullyQual @Override public SqmPath resolveIndexedAccess( - SqmExpression selector, + SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { throw new UnsupportedOperationException( "Fully qualified reflective paths cannot contain indexed access" ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java index 8b7c052401..30c1a3596d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPathConsumer.java @@ -44,6 +44,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer { private final String alias; private ConsumerDelegate delegate; + private boolean treated; public QualifiedJoinPathConsumer( SqmRoot sqmRoot, @@ -78,6 +79,10 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer { ); } + public void setTreated(boolean treated) { + this.treated = treated; + } + @Override public SemanticPathPart getConsumedPart() { return delegate.getConsumedPart(); @@ -87,14 +92,20 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer { public void consumeIdentifier(String identifier, boolean isBase, boolean isTerminal) { if ( isBase ) { assert delegate == null; - delegate = resolveBase( identifier, isTerminal ); + delegate = resolveBase( identifier, !treated && isTerminal ); } else { assert delegate != null; - delegate.consumeIdentifier( identifier, isTerminal ); + delegate.consumeIdentifier( identifier, !treated && isTerminal ); } } + @Override + public void consumeTreat(String entityName, boolean isTerminal) { + assert delegate != null; + delegate.consumeTreat( entityName, isTerminal ); + } + private ConsumerDelegate resolveBase(String identifier, boolean isTerminal) { final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState(); final SqmPathRegistry pathRegistry = processingState.getPathRegistry(); @@ -187,6 +198,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer { private interface ConsumerDelegate { void consumeIdentifier(String identifier, boolean isTerminal); + void consumeTreat(String entityName, boolean isTerminal); SemanticPathPart getConsumedPart(); } @@ -226,6 +238,14 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer { ); } + @Override + public void consumeTreat(String entityName, boolean isTerminal) { + final EntityDomainType entityDomainType = creationState.getCreationContext().getJpaMetamodel() + .entity( entityName ); + currentPath = currentPath.treatAs( entityDomainType, isTerminal ? alias : null ); + creationState.getCurrentProcessingState().getPathRegistry().register( currentPath ); + } + @Override public SemanticPathPart getConsumedPart() { return currentPath; @@ -240,7 +260,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer { private final boolean fetch; private final String alias; - private NavigablePath path = new NavigablePath(); + private final StringBuilder path = new StringBuilder(); private SqmEntityJoin join; @@ -263,14 +283,17 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer { @Override public void consumeIdentifier(String identifier, boolean isTerminal) { - path = path.append( identifier ); - + if ( path.length() != 0 ) { + path.append( '.' ); + } + path.append( identifier ); if ( isTerminal ) { + final String fullPath = path.toString(); final EntityDomainType joinedEntityType = creationState.getCreationContext() .getJpaMetamodel() - .resolveHqlEntityReference( path.getFullPath() ); + .resolveHqlEntityReference( fullPath ); if ( joinedEntityType == null ) { - throw new SemanticException( "Could not resolve join path - " + path.getFullPath() ); + throw new SemanticException( "Could not resolve join path - " + fullPath ); } assert ! ( joinedEntityType instanceof SqmPolymorphicRootDescriptor ); @@ -284,6 +307,11 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer { } } + @Override + public void consumeTreat(String entityName, boolean isTerminal) { + throw new UnsupportedOperationException(); + } + @Override public SemanticPathPart getConsumedPart() { return join; diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPredicatePathConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPredicatePathConsumer.java index c92d0c2541..a4508486e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPredicatePathConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/QualifiedJoinPredicatePathConsumer.java @@ -11,8 +11,12 @@ import java.util.Locale; import org.hibernate.query.SemanticException; import org.hibernate.query.hql.spi.SemanticPathPart; import org.hibernate.query.hql.spi.SqmCreationState; +import org.hibernate.query.sqm.tree.SqmQuery; import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin; +import org.hibernate.query.sqm.tree.from.SqmRoot; +import org.hibernate.query.sqm.tree.select.SqmQuerySpec; +import org.hibernate.query.sqm.tree.select.SqmSubQuery; /** * Specialized consumer for processing domain model paths occurring as part @@ -34,8 +38,19 @@ public class QualifiedJoinPredicatePathConsumer extends BasicDotIdentifierConsum protected SemanticPathPart createBasePart() { return new BaseLocalSequencePart() { @Override - protected void validateAsRoot(SqmFrom pathRoot) { - if ( pathRoot.findRoot() != sqmJoin.findRoot() ) { + protected void validateAsRoot(SqmFrom pathRoot) { + final SqmRoot root = pathRoot.findRoot(); + if ( root != sqmJoin.findRoot() ) { + final SqmQuery processingQuery = getCreationState().getCurrentProcessingState().getProcessingQuery(); + if ( processingQuery instanceof SqmSubQuery ) { + final SqmQuerySpec querySpec = ( (SqmSubQuery) processingQuery ).getQuerySpec(); + // If this "foreign" from element is used in a sub query + // This is only an error if the from element is actually part of the sub query + if ( querySpec.getFromClause() == null || !querySpec.getFromClause().getRoots().contains( root ) ) { + super.validateAsRoot( pathRoot ); + return; + } + } throw new SemanticException( String.format( Locale.ROOT, diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java index 58d45a72df..66b896fa86 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java @@ -272,8 +272,6 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem private final Stack dotIdentifierConsumerStack; - private final Stack treatHandlerStack = new StandardStack<>( new TreatHandlerNormal() ); - private final Stack parameterDeclarationContextStack = new StandardStack<>(); private final Stack processingStateStack = new StandardStack<>(); @@ -282,12 +280,16 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem private final JavaType> mapJavaTypeDescriptor; private ParameterCollector parameterCollector; + private ParameterStyle parameterStyle; @SuppressWarnings("WeakerAccess") public SemanticQueryBuilder(SqmCreationOptions creationOptions, SqmCreationContext creationContext) { this.creationOptions = creationOptions; this.creationContext = creationContext; this.dotIdentifierConsumerStack = new StandardStack<>( new BasicDotIdentifierConsumer( this ) ); + this.parameterStyle = creationOptions.useStrictJpaCompliance() + ? ParameterStyle.UNKNOWN + : ParameterStyle.MIXED; this.integerDomainType = creationContext .getNodeBuilder() @@ -787,13 +789,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem } // visit from-clause first!!! - treatHandlerStack.push( new TreatHandlerFromClause() ); - try { - sqmQuerySpec.setFromClause( visitFromClause( (HqlParser.FromClauseContext) ctx.getChild( fromIndex ) ) ); - } - finally { - treatHandlerStack.pop(); - } + sqmQuerySpec.setFromClause( visitFromClause( (HqlParser.FromClauseContext) ctx.getChild( fromIndex ) ) ); final SqmSelectClause selectClause; if ( fromIndex == 1 ) { @@ -817,13 +813,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem int currentIndex = fromIndex + 1; final SqmWhereClause whereClause = new SqmWhereClause( creationContext.getNodeBuilder() ); if ( currentIndex < ctx.getChildCount() && ctx.getChild( currentIndex ) instanceof HqlParser.WhereClauseContext ) { - treatHandlerStack.push( new TreatHandlerNormal( DowncastLocation.WHERE ) ); - try { - whereClause.setPredicate( (SqmPredicate) ctx.getChild( currentIndex++ ).accept( this ) ); - } - finally { - treatHandlerStack.pop(); - } + whereClause.setPredicate( (SqmPredicate) ctx.getChild( currentIndex++ ).accept( this ) ); } sqmQuerySpec.setWhereClause( whereClause ); @@ -862,8 +852,6 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem @Override public SqmSelectClause visitSelectClause(HqlParser.SelectClauseContext ctx) { - treatHandlerStack.push( new TreatHandlerNormal( DowncastLocation.SELECT ) ); - // todo (6.0) : primer a select-clause-specific SemanticPathPart into the stack final int selectionListIndex; if ( ctx.getChild( 1 ) instanceof HqlParser.SelectionListContext ) { @@ -873,24 +861,19 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem selectionListIndex = 2; } - try { - final SqmSelectClause selectClause = new SqmSelectClause( - selectionListIndex == 2, - creationContext.getNodeBuilder() - ); - final HqlParser.SelectionListContext selectionListContext = (HqlParser.SelectionListContext) ctx.getChild( - selectionListIndex - ); - for ( ParseTree subCtx : selectionListContext.children ) { - if ( subCtx instanceof HqlParser.SelectionContext ) { - selectClause.addSelection( visitSelection( (HqlParser.SelectionContext) subCtx ) ); - } + final SqmSelectClause selectClause = new SqmSelectClause( + selectionListIndex == 2, + creationContext.getNodeBuilder() + ); + final HqlParser.SelectionListContext selectionListContext = (HqlParser.SelectionListContext) ctx.getChild( + selectionListIndex + ); + for ( ParseTree subCtx : selectionListContext.children ) { + if ( subCtx instanceof HqlParser.SelectionContext ) { + selectClause.addSelection( visitSelection( (HqlParser.SelectionContext) subCtx ) ); } - return selectClause; - } - finally { - treatHandlerStack.pop(); } + return selectClause; } @Override @@ -1409,30 +1392,23 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem @Override public SqmFromClause visitFromClause(HqlParser.FromClauseContext parserFromClause) { - treatHandlerStack.push( new TreatHandlerFromClause() ); - - try { - final SqmFromClause fromClause; - if ( parserFromClause == null ) { - fromClause = new SqmFromClause(); - } - else { - final int size = parserFromClause.getChildCount(); - // Shift 1 bit instead of division by 2 - final int estimatedSize = size >> 1; - fromClause = new SqmFromClause( estimatedSize ); - for ( int i = 0; i < size; i++ ) { - final ParseTree parseTree = parserFromClause.getChild( i ); - if ( parseTree instanceof HqlParser.FromClauseSpaceContext ) { - fromClause.addRoot( visitFromClauseSpace( (HqlParser.FromClauseSpaceContext) parseTree ) ); - } + final SqmFromClause fromClause; + if ( parserFromClause == null ) { + fromClause = new SqmFromClause(); + } + else { + final int size = parserFromClause.getChildCount(); + // Shift 1 bit instead of division by 2 + final int estimatedSize = size >> 1; + fromClause = new SqmFromClause( estimatedSize ); + for ( int i = 0; i < size; i++ ) { + final ParseTree parseTree = parserFromClause.getChild( i ); + if ( parseTree instanceof HqlParser.FromClauseSpaceContext ) { + fromClause.addRoot( visitFromClauseSpace( (HqlParser.FromClauseSpaceContext) parseTree ) ); } } - return fromClause; - } - finally { - treatHandlerStack.pop(); } + return fromClause; } @Override @@ -1679,10 +1655,6 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem //noinspection unchecked final SqmQualifiedJoin join = (SqmQualifiedJoin) qualifiedJoinRhsContext.getChild( 0 ).accept( this ); - // we need to set the alias here because the path could be treated - the treat operator is - // not consumed by the identifierConsumer - join.setExplicitAlias( alias ); - final HqlParser.QualifiedJoinPredicateContext qualifiedJoinPredicateContext = parserJoin.qualifiedJoinPredicate(); if ( join instanceof SqmEntityJoin ) { sqmRoot.addSqmJoin( join ); @@ -3129,6 +3101,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem @Override public SqmNamedParameter visitNamedParameter(HqlParser.NamedParameterContext ctx) { + parameterStyle = parameterStyle.withNamed(); final SqmNamedParameter param = new SqmNamedParameter<>( ctx.getChild( 1 ).getText(), parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(), @@ -3143,6 +3116,7 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem if ( ctx.getChildCount() == 1 ) { throw new SemanticException( "Encountered positional parameter which did not declare position (? instead of, e.g., ?1)" ); } + parameterStyle = parameterStyle.withPositional(); final SqmPositionalParameter param = new SqmPositionalParameter<>( Integer.parseInt( ctx.getChild( 1 ).getText() ), parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(), @@ -4128,15 +4102,20 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem @Override public SqmPath visitTreatedNavigablePath(HqlParser.TreatedNavigablePathContext ctx) { - final SqmPath sqmPath = consumeManagedTypeReference( (HqlParser.PathContext) ctx.getChild( 2 ) ); + final DotIdentifierConsumer consumer = dotIdentifierConsumerStack.getCurrent(); + if ( consumer instanceof QualifiedJoinPathConsumer ) { + ( (QualifiedJoinPathConsumer) consumer ).setTreated( true ); + } + consumeManagedTypeReference( (HqlParser.PathContext) ctx.getChild( 2 ) ); final String treatTargetName = ctx.getChild( 4 ).getText(); final String treatTargetEntityName = getCreationContext().getJpaMetamodel().qualifyImportableName( treatTargetName ); - final EntityDomainType treatTarget = getCreationContext().getJpaMetamodel().entity( treatTargetEntityName ); - SqmPath result = resolveTreatedPath( sqmPath, treatTarget ); + final boolean hasContinuation = ctx.getChildCount() == 7; + consumer.consumeTreat( treatTargetEntityName, !hasContinuation ); + SqmPath result = (SqmPath) consumer.getConsumedPart(); - if ( ctx.getChildCount() == 7 ) { + if ( hasContinuation ) { dotIdentifierConsumerStack.push( new BasicDotIdentifierConsumer( result, this ) { @Override @@ -4155,11 +4134,6 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem return result; } - @SuppressWarnings({ "unchecked", "rawtypes" }) - private SqmTreatedPath resolveTreatedPath(SqmPath sqmPath, EntityDomainType treatTarget) { - return sqmPath.treatAs( (EntityDomainType) treatTarget ); - } - @Override public SqmPath visitCollectionElementNavigablePath(HqlParser.CollectionElementNavigablePathContext ctx) { final SqmPath pluralAttributePath = consumeDomainPath( (HqlParser.PathContext) ctx.getChild( 2 ) ); @@ -4257,15 +4231,10 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem final SqmPath sqmPath = consumeDomainPath( parserPath ); final SqmPathSource pathSource = sqmPath.getReferencedPathSource(); - - try { - // use the `#sqmAs` call to validate the path is a ManagedType - pathSource.sqmAs( ManagedDomainType.class ); + if ( pathSource.getSqmPathType() instanceof ManagedDomainType ) { return sqmPath; } - catch (Exception e) { - throw new SemanticException( "Expecting ManagedType valued path [" + sqmPath.getNavigablePath() + "], but found : " + pathSource.getSqmPathType() ); - } + throw new SemanticException( "Expecting ManagedType valued path [" + sqmPath.getNavigablePath() + "], but found : " + pathSource.getSqmPathType() ); } private SqmPath consumePluralAttributeReference(HqlParser.PathContext parserPath) { @@ -4278,40 +4247,6 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem throw new SemanticException( "Expecting plural attribute valued path [" + sqmPath.getNavigablePath() + "], but found : " + sqmPath.getReferencedPathSource().getSqmPathType() ); } - private interface TreatHandler { - void addDowncast(SqmFrom sqmFrom, IdentifiableDomainType downcastTarget); - } - - private static class TreatHandlerNormal implements TreatHandler { - private final DowncastLocation downcastLocation; - - public TreatHandlerNormal() { - this( DowncastLocation.OTHER ); - } - - public TreatHandlerNormal(DowncastLocation downcastLocation) { - this.downcastLocation = downcastLocation; - } - - @Override - public void addDowncast( - SqmFrom sqmFrom, - IdentifiableDomainType downcastTarget) { -// ( (MutableUsageDetails) sqmFrom.getUsageDetails() ).addDownCast( false, downcastTarget, downcastLocation ); - throw new NotYetImplementedFor6Exception(); - } - } - - private static class TreatHandlerFromClause implements TreatHandler { - @Override - public void addDowncast( - SqmFrom sqmFrom, - IdentifiableDomainType downcastTarget) { -// ( (MutableUsageDetails) sqmFrom.getUsageDetails() ).addDownCast( true, downcastTarget, DowncastLocation.FROM ); - throw new NotYetImplementedFor6Exception(); - } - } - private void checkFQNEntityNameJpaComplianceViolationIfNeeded(String name, EntityDomainType entityDescriptor) { if ( getCreationOptions().useStrictJpaCompliance() && ! name.equals( entityDescriptor.getName() ) ) { // FQN is the only possible reason @@ -4321,4 +4256,59 @@ public class SemanticQueryBuilder extends HqlParserBaseVisitor implem ); } } + + private enum ParameterStyle { + UNKNOWN { + @Override + ParameterStyle withNamed() { + return NAMED; + } + + @Override + ParameterStyle withPositional() { + return POSITIONAL; + } + }, + NAMED { + @Override + ParameterStyle withNamed() { + return NAMED; + } + + @Override + ParameterStyle withPositional() { + throw new StrictJpaComplianceViolation( + "Cannot mix positional and named parameters", + StrictJpaComplianceViolation.Type.MIXED_POSITIONAL_NAMED_PARAMETERS + ); + } + }, + POSITIONAL { + @Override + ParameterStyle withNamed() { + throw new StrictJpaComplianceViolation( + "Cannot mix positional and named parameters", + StrictJpaComplianceViolation.Type.MIXED_POSITIONAL_NAMED_PARAMETERS + ); + } + + @Override + ParameterStyle withPositional() { + return POSITIONAL; + } + }, + MIXED { + @Override + ParameterStyle withNamed() { + return MIXED; + } + + @Override + ParameterStyle withPositional() { + return MIXED; + } + }; + abstract ParameterStyle withNamed(); + abstract ParameterStyle withPositional(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/hql/spi/DotIdentifierConsumer.java b/hibernate-core/src/main/java/org/hibernate/query/hql/spi/DotIdentifierConsumer.java index 081b0d1388..944fe65767 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/hql/spi/DotIdentifierConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/hql/spi/DotIdentifierConsumer.java @@ -22,6 +22,15 @@ public interface DotIdentifierConsumer { */ void consumeIdentifier(String identifier, boolean isBase, boolean isTerminal); + /** + * Responsible for consuming each part of the path. Called sequentially for + * each part. + * + * @param entityName The treat target entity name + * @param isTerminal Is this the terminus of the path (last token)? + */ + void consumeTreat(String entityName, boolean isTerminal); + /** * Get the currently consumed part. Generally called after the whole path * has been processed at which point this will return the final outcome of the diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheDisabledImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheDisabledImpl.java index 78fbbc9eff..7161d5fbe1 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheDisabledImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheDisabledImpl.java @@ -46,7 +46,7 @@ public class QueryInterpretationCacheDisabledImpl implements QueryInterpretation } @Override - public SelectQueryPlan resolveSelectQueryPlan(Key key, Supplier creator) { + public SelectQueryPlan resolveSelectQueryPlan(Key key, Supplier> creator) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java index 3e6d844bec..f7f6aed45d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryInterpretationCacheStandardImpl.java @@ -76,17 +76,18 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation } @Override - public SelectQueryPlan resolveSelectQueryPlan( + public SelectQueryPlan resolveSelectQueryPlan( Key key, - Supplier creator) { + Supplier> creator) { log.tracef( "QueryPlan#getSelectQueryPlan(%s)", key ); - final SelectQueryPlan cached = (SelectQueryPlan) queryPlanCache.get( key ); + @SuppressWarnings("unchecked") + final SelectQueryPlan cached = (SelectQueryPlan) queryPlanCache.get( key ); if ( cached != null ) { return cached; } - final SelectQueryPlan plan = creator.get(); + final SelectQueryPlan plan = creator.get(); queryPlanCache.put( key.prepareForStore(), plan ); return plan; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java index 822fc039ca..d2a8b6d3ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/internal/QueryParameterBindingsImpl.java @@ -240,8 +240,8 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings { final TypeConfiguration typeConfiguration = session.getFactory().getTypeConfiguration(); if ( binding.getBindType() instanceof JavaTypedExpressable ) { - final JavaTypedExpressable javaTypedExpressable = (JavaTypedExpressable) binding.getBindType(); - final JavaType jtd = javaTypedExpressable.getExpressableJavaTypeDescriptor(); + final JavaTypedExpressable javaTypedExpressable = (JavaTypedExpressable) binding.getBindType(); + final JavaType jtd = javaTypedExpressable.getExpressableJavaTypeDescriptor(); if ( jtd.getJavaTypeClass() != null ) { // avoid dynamic models return typeConfiguration.getBasicTypeForJavaType( jtd.getJavaTypeClass() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java index 7119bfe986..7669fbe324 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/TableGroupImpl.java @@ -131,7 +131,7 @@ public class TableGroupImpl implements TableGroup { NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization) { - final TableReference tableReference = getTableReference( navigablePath, tableExpression, allowFkOptimization ); + final TableReference tableReference = getTableReference( navigablePath, tableExpression, allowFkOptimization, true ); if ( tableReference == null ) { throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" ); } @@ -143,14 +143,15 @@ public class TableGroupImpl implements TableGroup { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { - if ( primaryTableReference.getTableReference( navigablePath , tableExpression, allowFkOptimization ) != null ) { + boolean allowFkOptimization, + boolean resolve) { + if ( primaryTableReference.getTableReference( navigablePath , tableExpression, allowFkOptimization, resolve ) != null ) { return primaryTableReference; } for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); - if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization ) != null ) { + if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { return primaryTableReference; } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderBasicPart.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderBasicPart.java index b0b6b762bb..b7298bb15e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderBasicPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderBasicPart.java @@ -68,7 +68,7 @@ public class CompleteFetchBuilderBasicPart implements CompleteFetchBuilder, Basi final String mappedColumn = referencedModelPart.getSelectionExpression(); final TableGroup tableGroup = creationState.getFromClauseAccess().getTableGroup( parent.getNavigablePath() ); - final TableReference tableReference = tableGroup.getTableReference( navigablePath, mappedTable ); + final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, mappedTable ); final String selectedAlias; final int jdbcPosition; diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderEntityValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderEntityValuedModelPart.java index 7380b133ec..08519da654 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderEntityValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderEntityValuedModelPart.java @@ -69,7 +69,7 @@ public class CompleteFetchBuilderEntityValuedModelPart final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() ); modelPart.forEachSelectable( (selectionIndex, selectableMapping) -> { - final TableReference tableReference = tableGroup.getTableReference( navigablePath, selectableMapping.getContainingTableExpression() ); + final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, selectableMapping.getContainingTableExpression() ); final String mappedColumn = selectableMapping.getSelectionExpression(); final String columnAlias = columnAliases.get( selectionIndex ); creationStateImpl.resolveSqlSelection( diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicModelPart.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicModelPart.java index 30f3b29982..c74deb6fef 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderBasicModelPart.java @@ -68,7 +68,7 @@ public class CompleteResultBuilderBasicModelPart final DomainResultCreationStateImpl creationStateImpl = impl( domainResultCreationState ); final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() ); - final TableReference tableReference = tableGroup.getTableReference( navigablePath, modelPart.getContainingTableExpression() ); + final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, modelPart.getContainingTableExpression() ); final String mappedColumn = modelPart.getSelectionExpression(); final SqlSelection sqlSelection = creationStateImpl.resolveSqlSelection( diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderCollectionStandard.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderCollectionStandard.java index 53b0a6523b..d10dd854f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderCollectionStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteResultBuilderCollectionStandard.java @@ -146,7 +146,7 @@ public class CompleteResultBuilderCollectionStandard implements CompleteResultBu creationStateImpl.resolveSqlSelection( creationStateImpl.resolveSqlExpression( SqlExpressionResolver.createColumnReferenceKey( - tableGroup.getTableReference( selectableMapping.getContainingTableExpression() ), + tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ), selectableMapping.getSelectionExpression() ), processingState -> { diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/DelayedFetchBuilderBasicPart.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/DelayedFetchBuilderBasicPart.java index 19ef19c389..dd407e086b 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/DelayedFetchBuilderBasicPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/DelayedFetchBuilderBasicPart.java @@ -58,7 +58,6 @@ public class DelayedFetchBuilderBasicPart parent, fetchPath, referencedModelPart, - true, null, FetchTiming.DELAYED, isEnhancedForLazyLoading, diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/EntityResultImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/EntityResultImpl.java index aa437292b7..50511c0d19 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/EntityResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/EntityResultImpl.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.function.Function; import org.hibernate.LockMode; +import org.hibernate.engine.FetchTiming; import org.hibernate.internal.util.MutableObject; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityValuedModelPart; @@ -19,7 +20,6 @@ import org.hibernate.query.NavigablePath; import org.hibernate.query.results.ResultsHelper; import org.hibernate.sql.ast.spi.SqlAstCreationState; 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.DomainResultCreationState; import org.hibernate.sql.results.graph.Fetch; @@ -37,7 +37,7 @@ public class EntityResultImpl implements EntityResult { private final NavigablePath navigablePath; private final EntityValuedModelPart entityValuedModelPart; - private final DomainResult identifierResult; + private final Fetch identifierFetch; private final BasicFetch discriminatorFetch; private final List fetches; @@ -102,17 +102,20 @@ public class EntityResultImpl implements EntityResult { } if ( idFetchRef.isNotSet() ) { - identifierResult = ResultsHelper.implicitIdentifierResult( - identifierMapping, + identifierFetch = ( (Fetchable) identifierMapping ).generateFetch( + this, new EntityIdentifierNavigablePath( navigablePath, ResultsHelper.attributeName( identifierMapping ) ), + FetchTiming.IMMEDIATE, + true, + null, creationState ); } else { - this.identifierResult = idFetchRef.get().asResult( creationState ); + this.identifierFetch = idFetchRef.get(); } } @@ -161,7 +164,7 @@ public class EntityResultImpl implements EntityResult { this, getNavigablePath(), lockMode, - identifierResult, + identifierFetch, discriminatorFetch, null, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java index 2b94137f60..9a089204d5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderLegacy.java @@ -148,7 +148,7 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue resolveSqlSelection( columnNames.get( selectionIndex ), createColumnReferenceKey( - tableGroup.getTableReference( selectableMapping.getContainingTableExpression() ), + tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ), selectableMapping.getSelectionExpression() ), selectableMapping.getJdbcMapping(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderStandard.java b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderStandard.java index acbb4663c3..74d108997d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/dynamic/DynamicFetchBuilderStandard.java @@ -65,7 +65,7 @@ public class DynamicFetchBuilderStandard final SqlExpressionResolver sqlExpressionResolver = domainResultCreationState.getSqlAstCreationState().getSqlExpressionResolver(); final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> { - final TableReference tableReference = ownerTableGroup.getTableReference( + final TableReference tableReference = ownerTableGroup.resolveTableReference( fetchPath, selectableMapping.getContainingTableExpression() ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderBasic.java b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderBasic.java index 5832a710f8..732e84a295 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderBasic.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/implicit/ImplicitFetchBuilderBasic.java @@ -67,7 +67,7 @@ public class ImplicitFetchBuilderBasic implements ImplicitFetchBuilder, BasicVal final Expression expression = creationStateImpl.resolveSqlExpression( createColumnReferenceKey( - parentTableGroup.getTableReference( fetchPath, table ), + parentTableGroup.resolveTableReference( fetchPath, table ), fetchable.getSelectionExpression() ), processingState -> { @@ -99,8 +99,6 @@ public class ImplicitFetchBuilderBasic implements ImplicitFetchBuilder, BasicVal parent, fetchPath, fetchable, - // todo (6.0) - we don't know - true, valueConverter, FetchTiming.IMMEDIATE, domainResultCreationState diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java index 2be04b64d3..6dae496ba2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/AbstractQuery.java @@ -101,7 +101,7 @@ import static org.hibernate.jpa.QueryHints.SPEC_HINT_TIMEOUT; */ @SuppressWarnings("WeakerAccess") public abstract class AbstractQuery implements QueryImplementor { - private static final EntityManagerMessageLogger log = HEMLogging.messageLogger( AbstractQuery.class ); + protected static final EntityManagerMessageLogger log = HEMLogging.messageLogger( AbstractQuery.class ); private final SharedSessionContractImplementor session; diff --git a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryInterpretationCache.java b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryInterpretationCache.java index 5a2c4f3344..3c74e64012 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/spi/QueryInterpretationCache.java +++ b/hibernate-core/src/main/java/org/hibernate/query/spi/QueryInterpretationCache.java @@ -35,7 +35,7 @@ public interface QueryInterpretationCache { HqlInterpretation resolveHqlInterpretation(String queryString, Function> creator); - SelectQueryPlan resolveSelectQueryPlan(Key key, Supplier creator); + SelectQueryPlan resolveSelectQueryPlan(Key key, Supplier> creator); NonSelectQueryPlan getNonSelectQueryPlan(Key key); void cacheNonSelectQueryPlan(Key key, NonSelectQueryPlan plan); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java index 0ef7d7692b..ffc6e87e09 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeNonSelectQueryPlanImpl.java @@ -13,18 +13,12 @@ import java.util.Set; import org.hibernate.action.internal.BulkOperationCleanupAction; import org.hibernate.engine.spi.SharedSessionContractImplementor; -import org.hibernate.metamodel.mapping.BasicValuedMapping; -import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.NonSelectQueryPlan; -import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; -import org.hibernate.query.spi.QueryParameterImplementor; +import org.hibernate.query.sql.spi.ParameterOccurrence; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; -import org.hibernate.sql.exec.internal.JdbcParameterImpl; import org.hibernate.sql.exec.internal.StandardJdbcMutationExecutor; import org.hibernate.sql.exec.spi.JdbcMutation; import org.hibernate.sql.exec.spi.JdbcMutationExecutor; @@ -39,12 +33,12 @@ public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan { private final String sql; private final Set affectedTableNames; - private final List> parameterList; + private final List parameterList; public NativeNonSelectQueryPlanImpl( String sql, Set affectedTableNames, - List> parameterList) { + List parameterList) { this.sql = sql; this.affectedTableNames = affectedTableNames; this.parameterList = parameterList; @@ -66,26 +60,12 @@ public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan { jdbcParameterBinders = new ArrayList<>( parameterList.size() ); jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); - for ( QueryParameterImplementor param : parameterList ) { - QueryParameterBinding binding = queryParameterBindings.getBinding( param ); - AllowableParameterType type = binding.getBindType(); - if ( type == null ) { - type = param.getHibernateType(); - } - if ( type == null ) { - type = executionContext.getSession().getTypeConfiguration().getBasicTypeForJavaType( Object.class ); - } - - final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping(); - final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); - - jdbcParameterBinders.add( jdbcParameter ); - - jdbcParameterBindings.addBinding( - jdbcParameter, - new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() ) - ); - } + jdbcParameterBindings.registerNativeQueryParameters( + queryParameterBindings, + parameterList, + jdbcParameterBinders, + executionContext.getSession().getFactory() + ); } final JdbcMutation jdbcMutation = new NativeJdbcMutation( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java index cd8b1b65d3..906b63db60 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java @@ -11,11 +11,13 @@ import java.time.Instant; import java.time.LocalDateTime; import java.time.OffsetDateTime; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -30,6 +32,8 @@ import org.hibernate.LockOptions; import org.hibernate.MappingException; import org.hibernate.QueryException; import org.hibernate.ScrollMode; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.query.spi.NativeQueryInterpreter; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -37,6 +41,7 @@ import org.hibernate.graph.GraphSemantic; import org.hibernate.graph.RootGraph; import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.internal.AbstractSharedSessionContract; +import org.hibernate.internal.util.MathHelper; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.jpa.internal.util.LockModeTypeHelper; @@ -69,6 +74,7 @@ import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.ParameterMetadataImplementor; import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryInterpretationCache; +import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor; @@ -79,6 +85,7 @@ import org.hibernate.query.sql.spi.NativeSelectQueryDefinition; import org.hibernate.query.sql.spi.NativeSelectQueryPlan; import org.hibernate.query.sql.spi.NonSelectInterpretationsKey; import org.hibernate.query.sql.spi.ParameterInterpretation; +import org.hibernate.query.sql.spi.ParameterOccurrence; import org.hibernate.query.sql.spi.SelectInterpretationsKey; import org.hibernate.sql.exec.internal.CallbackImpl; import org.hibernate.sql.exec.spi.Callback; @@ -110,7 +117,7 @@ public class NativeQueryImpl private final String sqlString; private final ParameterMetadataImplementor parameterMetadata; - private final List> occurrenceOrderedParamList; + private final List parameterOccurrences; private final QueryParameterBindings parameterBindings; private final ResultSetMappingImpl resultSetMapping; @@ -190,7 +197,7 @@ public class NativeQueryImpl this.sqlString = parameterInterpretation.getAdjustedSqlString(); this.parameterMetadata = parameterInterpretation.toParameterMetadata( session ); - this.occurrenceOrderedParamList = parameterInterpretation.getOccurrenceOrderedParameters(); + this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences(); this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory(), @@ -327,7 +334,7 @@ public class NativeQueryImpl this.sqlString = parameterInterpretation.getAdjustedSqlString(); this.parameterMetadata = parameterInterpretation.toParameterMetadata( session ); - this.occurrenceOrderedParamList = parameterInterpretation.getOccurrenceOrderedParameters(); + this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences(); this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory(), @@ -386,7 +393,7 @@ public class NativeQueryImpl this.sqlString = parameterInterpretation.getAdjustedSqlString(); this.parameterMetadata = parameterInterpretation.toParameterMetadata( session ); - this.occurrenceOrderedParamList = parameterInterpretation.getOccurrenceOrderedParameters(); + this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences(); this.parameterBindings = QueryParameterBindingsImpl.from( parameterMetadata, session.getFactory(), @@ -571,11 +578,9 @@ public class NativeQueryImpl @Override protected List doList() { - //noinspection unchecked return resolveSelectQueryPlan().performList( this ); } - @SuppressWarnings("unchecked") private SelectQueryPlan resolveSelectQueryPlan() { final QueryInterpretationCache.Key cacheKey = generateSelectInterpretationsKey( resultSetMapping ); if ( cacheKey != null ) { @@ -590,10 +595,11 @@ public class NativeQueryImpl } private NativeSelectQueryPlan createQueryPlan(ResultSetMapping resultSetMapping) { - final NativeSelectQueryDefinition queryDefinition = new NativeSelectQueryDefinition() { + final String sqlString = expandParameterLists(); + final NativeSelectQueryDefinition queryDefinition = new NativeSelectQueryDefinition() { @Override public String getSqlString() { - return NativeQueryImpl.this.getQueryString(); + return sqlString; } @Override @@ -602,8 +608,8 @@ public class NativeQueryImpl } @Override - public List> getQueryParameterList() { - return NativeQueryImpl.this.occurrenceOrderedParamList; + public List getQueryParameterOccurrences() { + return NativeQueryImpl.this.parameterOccurrences; } @Override @@ -622,6 +628,142 @@ public class NativeQueryImpl .createQueryPlan( queryDefinition, getSessionFactory() ); } + private String expandParameterLists() { + if ( parameterOccurrences == null || parameterOccurrences.isEmpty() ) { + return sqlString; + } + // HHH-1123 + // Some DBs limit number of IN expressions. For now, warn... + final Dialect dialect = getSessionFactory().getServiceRegistry().getService( JdbcServices.class ).getJdbcEnvironment().getDialect(); + final boolean paddingEnabled = getSessionFactory().getSessionFactoryOptions().inClauseParameterPaddingEnabled(); + final int inExprLimit = dialect.getInExpressionCountLimit(); + + StringBuilder sb = null; + + // Handle parameter lists + int offset = 0; + for ( ParameterOccurrence occurrence : parameterOccurrences ) { + final QueryParameterImplementor queryParameter = occurrence.getParameter(); + final QueryParameterBinding binding = parameterBindings.getBinding( queryParameter ); + if ( !binding.isMultiValued() ) { + continue; + } + final Collection bindValues = binding.getBindValues(); + + int bindValueCount = bindValues.size(); + int bindValueMaxCount = determineBindValueMaxCount( paddingEnabled, inExprLimit, bindValueCount ); + + if ( inExprLimit > 0 && bindValueCount > inExprLimit ) { + log.tooManyInExpressions( + dialect.getClass().getName(), + inExprLimit, + queryParameter.getName() == null + ? queryParameter.getPosition().toString() + : queryParameter.getName(), + bindValueCount + ); + } + + final int sourcePosition = occurrence.getSourcePosition(); + if ( sourcePosition < 0 ) { + continue; + } + + // check if placeholder is already immediately enclosed in parentheses + // (ignoring whitespace) + boolean isEnclosedInParens = true; + for ( int i = sourcePosition - 1; i >= 0; i-- ) { + final char ch = sqlString.charAt( i ); + if ( !Character.isWhitespace( ch ) ) { + isEnclosedInParens = ch == '('; + break; + } + } + if ( isEnclosedInParens ) { + for ( int i = sourcePosition + 1; i < sqlString.length(); i++ ) { + final char ch = sqlString.charAt( i ); + if ( !Character.isWhitespace( ch ) ) { + isEnclosedInParens = ch == ')'; + break; + } + } + } + + if ( bindValueCount == 1 && isEnclosedInParens ) { + // short-circuit for performance when only 1 value and the + // placeholder is already enclosed in parentheses... + continue; + } + + if ( sb == null ) { + sb = new StringBuilder( sqlString.length() + 20 ); + sb.append( sqlString ); + } + + final String expansionListAsString; + // HHH-8901 + if ( bindValueMaxCount == 0 ) { + if ( isEnclosedInParens ) { + expansionListAsString = "null"; + } + else { + expansionListAsString = "(null)"; + } + } + else { + // Shift 1 bit instead of multiplication by 2 + char[] chars; + if ( isEnclosedInParens ) { + chars = new char[( bindValueMaxCount << 1 ) - 1]; + chars[0] = '?'; + for ( int i = 1; i < bindValueMaxCount; i++ ) { + final int index = i << 1; + chars[index - 1] = ','; + chars[index] = '?'; + } + } + else { + chars = new char[( bindValueMaxCount << 1 ) + 1]; + chars[0] = '('; + chars[1] = '?'; + for ( int i = 1; i < bindValueMaxCount; i++ ) { + final int index = i << 1; + chars[index] = ','; + chars[index + 1] = '?'; + } + chars[chars.length - 1] = ')'; + } + + expansionListAsString = new String(chars); + } + + final int start = sourcePosition + offset; + final int end = start + 1; + sb.replace( start, end, expansionListAsString ); + offset += expansionListAsString.length() - 1; + } + return sb == null ? sqlString : sb.toString(); + } + + public static int determineBindValueMaxCount(boolean paddingEnabled, int inExprLimit, int bindValueCount) { + int bindValueMaxCount = bindValueCount; + + final boolean inClauseParameterPaddingEnabled = paddingEnabled && bindValueCount > 2; + + if ( inClauseParameterPaddingEnabled ) { + int bindValuePaddingCount = MathHelper.ceilingPowerOfTwo( bindValueCount ); + + if ( inExprLimit > 0 && bindValuePaddingCount > inExprLimit ) { + bindValuePaddingCount = inExprLimit; + } + + if ( bindValueCount < bindValuePaddingCount ) { + bindValueMaxCount = bindValuePaddingCount; + } + } + return bindValueMaxCount; + } + private SelectInterpretationsKey generateSelectInterpretationsKey(JdbcValuesMappingProducer resultSetMapping) { if ( !isCacheable( this ) ) { return null; @@ -635,8 +777,7 @@ public class NativeQueryImpl ); } - @SuppressWarnings("RedundantIfStatement") - private static boolean isCacheable(NativeQueryImpl query) { + private static boolean isCacheable(NativeQueryImpl query) { // todo (6.0): unless we move the limit rendering from DeferredResultSetAccess to NativeSelectQueryPlanImpl // we don't need to consider the limit here at all because that is applied on demand. // It certainly is better for performance to include the limit early, but then we might trash the cache @@ -644,7 +785,8 @@ public class NativeQueryImpl // return false; // } - return true; + // For now, don't cache plans that have parameter lists + return !query.parameterBindings.hasAnyMultiValuedBindings(); } private static boolean hasLimit(Limit limit) { @@ -669,7 +811,8 @@ public class NativeQueryImpl } if ( queryPlan == null ) { - queryPlan = new NativeNonSelectQueryPlanImpl( sqlString, querySpaces, occurrenceOrderedParamList ); + final String sqlString = expandParameterLists(); + queryPlan = new NativeNonSelectQueryPlanImpl( sqlString, querySpaces, parameterOccurrences ); if ( cacheKey != null ) { getSession().getFactory().getQueryEngine().getInterpretationCache().cacheNonSelectQueryPlan( cacheKey, queryPlan ); } @@ -680,6 +823,10 @@ public class NativeQueryImpl protected NonSelectInterpretationsKey generateNonSelectInterpretationsKey() { + if ( !isCacheable( this ) ) { + return null; + } + // todo (6.0) - should this account for query-spaces in determining "cacheable"? return new NonSelectInterpretationsKey( getQueryString(), @@ -1412,7 +1559,7 @@ public class NativeQueryImpl private static class ParameterInterpretationImpl implements ParameterInterpretation { private final String sqlString; - private final List> parameterList; + private final List parameterList; private final Map> positionalParameters; private final Map> namedParameters; @@ -1424,7 +1571,7 @@ public class NativeQueryImpl } @Override - public List> getOccurrenceOrderedParameters() { + public List getOrderedParameterOccurrences() { return parameterList; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java index 1a7aafb1f7..59ff1cc454 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeSelectQueryPlanImpl.java @@ -15,20 +15,14 @@ import java.util.Set; import org.hibernate.ScrollMode; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.internal.EmptyScrollableResults; -import org.hibernate.metamodel.mapping.BasicValuedMapping; -import org.hibernate.metamodel.mapping.JdbcMapping; -import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.query.results.ResultSetMapping; import org.hibernate.query.spi.DomainQueryExecutionContext; -import org.hibernate.query.spi.QueryParameterBinding; import org.hibernate.query.spi.QueryParameterBindings; -import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.sql.spi.NativeSelectQueryPlan; +import org.hibernate.query.sql.spi.ParameterOccurrence; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; -import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; -import org.hibernate.sql.exec.internal.JdbcParameterImpl; import org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl; import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBindings; @@ -44,14 +38,14 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { private final String sql; private final Set affectedTableNames; - private final List> parameterList; + private final List parameterList; private final JdbcValuesMappingProducer resultSetMapping; public NativeSelectQueryPlanImpl( String sql, Set affectedTableNames, - List> parameterList, + List parameterList, ResultSetMapping resultSetMapping, SessionFactoryImplementor sessionFactory) { final ResultSetMappingProcessor processor = new ResultSetMappingProcessor( resultSetMapping, sessionFactory ); @@ -85,26 +79,12 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { jdbcParameterBinders = new ArrayList<>( parameterList.size() ); jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); - for ( QueryParameterImplementor param : parameterList ) { - QueryParameterBinding binding = queryParameterBindings.getBinding( param ); - AllowableParameterType type = binding.getBindType(); - if ( type == null ) { - type = param.getHibernateType(); - } - if ( type == null ) { - type = executionContext.getSession().getTypeConfiguration().getBasicTypeForJavaType( Object.class ); - } - - final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping(); - final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); - - jdbcParameterBinders.add( jdbcParameter ); - - jdbcParameterBindings.addBinding( - jdbcParameter, - new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() ) - ); - } + jdbcParameterBindings.registerNativeQueryParameters( + queryParameterBindings, + parameterList, + jdbcParameterBinders, + executionContext.getSession().getFactory() + ); } executionContext.getSession().autoFlushIfRequired( affectedTableNames ); @@ -150,26 +130,11 @@ public class NativeSelectQueryPlanImpl implements NativeSelectQueryPlan { jdbcParameterBinders = new ArrayList<>( parameterList.size() ); jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); - queryParameterBindings.visitBindings( - (param, binding) -> { - AllowableParameterType type = binding.getBindType(); - if ( type == null ) { - type = param.getHibernateType(); - } - if ( type == null ) { - type = executionContext.getSession().getTypeConfiguration().getBasicTypeForJavaType( Object.class ); - } - - final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping(); - final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); - - jdbcParameterBinders.add( jdbcParameter ); - - jdbcParameterBindings.addBinding( - jdbcParameter, - new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() ) - ); - } + jdbcParameterBindings.registerNativeQueryParameters( + queryParameterBindings, + parameterList, + jdbcParameterBinders, + executionContext.getSession().getFactory() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java index 21c15df875..24186c1030 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/ParameterRecognizerImpl.java @@ -18,6 +18,7 @@ import org.hibernate.query.QueryParameter; import org.hibernate.query.internal.QueryParameterNamedImpl; import org.hibernate.query.internal.QueryParameterPositionalImpl; import org.hibernate.query.spi.QueryParameterImplementor; +import org.hibernate.query.sql.spi.ParameterOccurrence; import org.hibernate.query.sql.spi.ParameterRecognizer; /** @@ -37,8 +38,8 @@ public class ParameterRecognizerImpl implements ParameterRecognizer { private int ordinalParameterImplicitPosition; - private List> parameterList; - private StringBuilder sqlStringBuffer = new StringBuilder(); + private List parameterList; + private final StringBuilder sqlStringBuffer = new StringBuilder(); @SuppressWarnings("WeakerAccess") public ParameterRecognizerImpl(SessionFactoryImplementor factory) { @@ -77,7 +78,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer { return positionalQueryParameters; } - public List> getParameterList() { + public List getParameterList() { return parameterList; } @@ -117,7 +118,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer { parameterList = new ArrayList<>(); } - parameterList.add( parameter ); + parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) ); sqlStringBuffer.append( "?" ); } @@ -148,7 +149,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer { parameterList = new ArrayList<>(); } - parameterList.add( parameter ); + parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) ); sqlStringBuffer.append( "?" ); } @@ -183,7 +184,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer { parameterList = new ArrayList<>(); } - parameterList.add( parameter ); + parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) ); sqlStringBuffer.append( "?" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/SQLQueryParser.java b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/SQLQueryParser.java index 595aed0a1b..202f6b0934 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/internal/SQLQueryParser.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/internal/SQLQueryParser.java @@ -165,9 +165,6 @@ public class SQLQueryParser { result.append( '{' ).append( aliasPath ).append( '}' ); } } - - // Possibly handle :something parameters for the query ? - return result.toString(); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeSelectQueryDefinition.java b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeSelectQueryDefinition.java index 7ccaa4368e..316d5c23a4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeSelectQueryDefinition.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/NativeSelectQueryDefinition.java @@ -26,7 +26,7 @@ public interface NativeSelectQueryDefinition { * @apiNote This returns query parameters in the order they were * encountered - potentially including "duplicate references" to a single parameter */ - List> getQueryParameterList(); + List getQueryParameterOccurrences(); ResultSetMapping getResultSetMapping(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterInterpretation.java index a68d4b18a8..1a9ec42f4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterInterpretation.java @@ -20,7 +20,7 @@ public interface ParameterInterpretation { * Access to the defined parameters in the order they were encountered, * potentially including "duplicate references" to a single parameter */ - List> getOccurrenceOrderedParameters(); + List getOrderedParameterOccurrences(); /** * Create the ParameterMetadata representation of this interpretation diff --git a/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterOccurrence.java b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterOccurrence.java new file mode 100644 index 0000000000..0be95dbe4f --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sql/spi/ParameterOccurrence.java @@ -0,0 +1,31 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.query.sql.spi; + +import org.hibernate.query.spi.QueryParameterImplementor; + +/** + * @author Christian Beikov + */ +public final class ParameterOccurrence { + + private final QueryParameterImplementor parameter; + private final int sourcePosition; + + public ParameterOccurrence(QueryParameterImplementor parameter, int sourcePosition) { + this.parameter = parameter; + this.sourcePosition = sourcePosition; + } + + public QueryParameterImplementor getParameter() { + return parameter; + } + + public int getSourcePosition() { + return sourcePosition; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java index b64634ba2c..17646334f9 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/SqmPathSource.java @@ -6,11 +6,9 @@ */ package org.hibernate.query.sqm; -import java.util.Locale; import jakarta.persistence.metamodel.Bindable; import org.hibernate.metamodel.model.domain.DomainType; -import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.sqm.tree.SqmExpressableAccessor; import org.hibernate.query.sqm.tree.domain.SqmPath; @@ -52,20 +50,4 @@ public interface SqmPathSource extends SqmExpressable, Bindable, SqmExp default SqmExpressable getExpressable() { return (SqmExpressable) getSqmPathType(); } - - default X sqmAs(Class targetType) { - if ( targetType.isInstance( this ) ) { - //noinspection unchecked - return (X) this; - } - - throw new IllegalArgumentException( - String.format( - Locale.ROOT, - "`%s` cannot be treated as `%s`", - getClass().getName(), - targetType.getName() - ) - ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/StrictJpaComplianceViolation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/StrictJpaComplianceViolation.java index 858b043862..7588b2ae45 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/StrictJpaComplianceViolation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/StrictJpaComplianceViolation.java @@ -31,6 +31,7 @@ public class StrictJpaComplianceViolation extends SemanticException { IDENTIFICATION_VARIABLE_NOT_DECLARED_IN_FROM_CLAUSE( "use of an alias not declared in the FROM clause" ), FQN_ENTITY_NAME( "use of FQN for entity name" ), IMPLICIT_TREAT( "use of implicit treat" ), + MIXED_POSITIONAL_NAMED_PARAMETERS( "mix of positional and named parameters" ), ; private final String description; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java index e8f97f9d49..10488a2eab 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/DomainParameterXref.java @@ -47,16 +47,16 @@ public class DomainParameterXref { final Map> xrefMap = new TreeMap<>( (o1, o2) -> { if ( o1 instanceof SqmNamedParameter ) { - final SqmNamedParameter one = (SqmNamedParameter) o1; - final SqmNamedParameter another = (SqmNamedParameter) o2; - - return one.getName().compareTo( another.getName() ); + final SqmNamedParameter one = (SqmNamedParameter) o1; + return o2 instanceof SqmNamedParameter + ? one.getName().compareTo( ((SqmNamedParameter) o2).getName() ) + : -1; } else if ( o1 instanceof SqmPositionalParameter ) { - final SqmPositionalParameter one = (SqmPositionalParameter) o1; - final SqmPositionalParameter another = (SqmPositionalParameter) o2; - - return one.getPosition().compareTo( another.getPosition() ); + final SqmPositionalParameter one = (SqmPositionalParameter) o1; + return o2 instanceof SqmPositionalParameter + ? one.getPosition().compareTo( ( (SqmPositionalParameter) o2 ).getPosition() ) + : 1; } else if ( o1 instanceof SqmJpaCriteriaParameterWrapper && o2 instanceof SqmJpaCriteriaParameterWrapper ) { @@ -101,7 +101,7 @@ public class DomainParameterXref { sqmParameter, p -> { if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper ) { - return ( (SqmJpaCriteriaParameterWrapper) sqmParameter ).getJpaCriteriaParameter(); + return ( (SqmJpaCriteriaParameterWrapper) sqmParameter ).getJpaCriteriaParameter(); } else if ( sqmParameter.getName() != null ) { return QueryParameterNamedImpl.fromSqm( sqmParameter ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java index 7d172a4f9d..aec4510e75 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/QuerySqmImpl.java @@ -670,7 +670,6 @@ public class QuerySqmImpl return lockMode != null && lockMode.greaterThan( LockMode.READ ); } - @SuppressWarnings("unchecked") private SelectQueryPlan resolveSelectQueryPlan() { // resolve (or make) the QueryPlan. This QueryPlan might be an aggregation of multiple plans. // diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java index 6483d7d5cf..2103e47508 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MatchingIdSelectionHelper.java @@ -201,7 +201,7 @@ public class MatchingIdSelectionHelper { final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter( entityDescriptor, - sqmMutationStatement.getTarget().getExplicitAlias(), + sqmMutationStatement.getTarget(), domainParameterXref, executionContext.getQueryOptions(), executionContext.getSession().getLoadQueryInfluencers(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MultiTableSqmMutationConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MultiTableSqmMutationConverter.java index 17b75c7755..12a7dc8532 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MultiTableSqmMutationConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/MultiTableSqmMutationConverter.java @@ -14,7 +14,6 @@ import org.hibernate.engine.spi.LoadQueryInfluencers; import org.hibernate.internal.util.collections.Stack; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.MappingModelExpressable; -import org.hibernate.query.NavigablePath; import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.sqm.internal.DomainParameterXref; @@ -23,6 +22,7 @@ import org.hibernate.query.sqm.sql.internal.DomainResultProducer; import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl; import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl; import org.hibernate.query.sqm.tree.expression.SqmParameter; +import org.hibernate.query.sqm.tree.from.SqmRoot; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.select.SqmSelectClause; import org.hibernate.query.sqm.tree.update.SqmAssignment; @@ -63,7 +63,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter sqmRoot, DomainParameterXref domainParameterXref, QueryOptions queryOptions, LoadQueryInfluencers loadQueryInfluencers, @@ -71,8 +71,8 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter sqmRoot, String sourceAlias, DomainParameterXref domainParameterXref, QueryOptions queryOptions, @@ -101,10 +101,9 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter lhs, String subNavigable, String alias) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java index 8c57dbfd23..8a20c4653a 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java @@ -12,6 +12,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -88,6 +89,7 @@ import org.hibernate.query.sqm.tree.domain.SqmCorrelatedRootJoin; import org.hibernate.query.sqm.tree.domain.SqmCorrelation; import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression; import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression; +import org.hibernate.query.sqm.tree.domain.SqmTreatedRoot; import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification; import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.entity.EntityPersister; @@ -597,6 +599,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base final SqmStatement sqmStatement = getStatement(); //noinspection unchecked final T statement = (T) sqmStatement.accept( this ); + pruneTableGroupJoins(); return new StandardSqmTranslation<>( statement, getJdbcParamsBySqmParam(), @@ -1988,13 +1991,13 @@ public abstract class BaseSqmToSqlAstConverter extends Base additionalRestrictions, new ComparisonPredicate( new ColumnReference( - parentTableGroup.getTableReference( navigablePath, selectable.getContainingTableExpression() ), + parentTableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ), selectable, sessionFactory ), ComparisonOperator.EQUAL, new ColumnReference( - tableGroup.getTableReference( navigablePath, selectable.getContainingTableExpression() ), + tableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ), selectable, sessionFactory ) @@ -2009,14 +2012,14 @@ public abstract class BaseSqmToSqlAstConverter extends Base (index, selectable) -> { lhs.add( new ColumnReference( - parentTableGroup.getTableReference( navigablePath, selectable.getContainingTableExpression() ), + parentTableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ), selectable, sessionFactory ) ); rhs.add( new ColumnReference( - tableGroup.getTableReference( navigablePath, selectable.getContainingTableExpression() ), + tableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ), selectable, sessionFactory ) @@ -2095,13 +2098,58 @@ public abstract class BaseSqmToSqlAstConverter extends Base return creationContext.getDomainModel().getEntityDescriptor( entityDomainType.getHibernateEntityName() ); } + private final Map> tableGroupTreatUsages = new IdentityHashMap<>(); + + protected void registerUsage(SqmFrom sqmFrom, TableGroup tableGroup) { + final EntityDomainType treatedType; + if ( sqmFrom instanceof SqmTreatedPath ) { + treatedType = ( (SqmTreatedPath) sqmFrom ).getTreatTarget(); + } + else if ( sqmFrom.getReferencedPathSource().getSqmPathType() instanceof EntityDomainType ) { + treatedType = (EntityDomainType) sqmFrom.getReferencedPathSource().getSqmPathType(); + } + else { + return; + } + final Set treatedEntityNames = tableGroupTreatUsages.computeIfAbsent( + tableGroup, + tg -> new HashSet<>( 1 ) + ); + treatedEntityNames.add( treatedType.getHibernateEntityName() ); + } + + protected void pruneTableGroupJoins() { + for ( Map.Entry> entry : tableGroupTreatUsages.entrySet() ) { + final TableGroup tableGroup = entry.getKey(); + final Set treatedEntityNames = entry.getValue(); + final ModelPartContainer modelPart = tableGroup.getModelPart(); + final EntityPersister tableGroupPersister; + if ( modelPart instanceof PluralAttributeMapping ) { + tableGroupPersister = (EntityPersister) ( (PluralAttributeMapping) modelPart ) + .getElementDescriptor() + .getPartMappingType(); + } + else { + tableGroupPersister = (EntityPersister) modelPart.getPartMappingType(); + } + tableGroupPersister.pruneForSubclasses( tableGroup, treatedEntityNames ); + } + } + protected void consumeExplicitJoins(SqmFrom sqmFrom, TableGroup lhsTableGroup) { if ( log.isTraceEnabled() ) { log.tracef( "Visiting explicit joins for `%s`", sqmFrom.getNavigablePath() ); } sqmFrom.visitSqmJoins( - sqmJoin -> consumeExplicitJoin( sqmJoin, lhsTableGroup, lhsTableGroup, true ) + sqmJoin -> { + registerUsage( (SqmFrom) sqmJoin.getLhs(), lhsTableGroup ); + consumeExplicitJoin( sqmJoin, lhsTableGroup, lhsTableGroup, true ); + } ); + for ( SqmFrom sqmTreat : sqmFrom.getSqmTreats() ) { + registerUsage( sqmTreat, lhsTableGroup ); + consumeExplicitJoins( sqmTreat, lhsTableGroup ); + } } @SuppressWarnings("WeakerAccess") @@ -2221,7 +2269,8 @@ public abstract class BaseSqmToSqlAstConverter extends Base private NavigablePath getJoinNavigablePath( NavigablePath sqmJoinNavigablePath, - NavigablePath parentNavigablePath, String partName) { + NavigablePath parentNavigablePath, + String partName) { if ( parentNavigablePath == null ) { return sqmJoinNavigablePath; } @@ -2327,7 +2376,13 @@ public abstract class BaseSqmToSqlAstConverter extends Base JpaPath sqmPath, boolean useInnerJoin, Consumer implicitJoinChecker) { - final JpaPath parentPath = sqmPath.getParentPath(); + final JpaPath parentPath; + if ( sqmPath instanceof SqmTreatedPath ) { + parentPath = ( (SqmTreatedPath) sqmPath ).getWrappedPath(); + } + else { + parentPath = sqmPath.getParentPath(); + } final TableGroup tableGroup = fromClauseIndex.findTableGroup( parentPath.getNavigablePath() ); if ( tableGroup == null ) { final TableGroup parentTableGroup = prepareReusablePath( @@ -2336,17 +2391,31 @@ public abstract class BaseSqmToSqlAstConverter extends Base useInnerJoin, implicitJoinChecker ); + if ( parentPath instanceof SqmTreatedPath ) { + fromClauseIndex.register( (SqmPath) parentPath, parentTableGroup ); + } final TableGroup newTableGroup = createTableGroup( parentTableGroup, (SqmPath) parentPath, useInnerJoin ); if ( newTableGroup != null ) { implicitJoinChecker.accept( newTableGroup ); + if ( sqmPath instanceof SqmFrom ) { + registerUsage( (SqmFrom) sqmPath, newTableGroup ); + } } return newTableGroup; } + else if ( sqmPath instanceof SqmTreatedPath ) { + fromClauseIndex.register( (SqmPath) sqmPath, tableGroup ); + if ( sqmPath instanceof SqmFrom ) { + registerUsage( (SqmFrom) sqmPath, tableGroup ); + } + } + else if ( parentPath instanceof SqmFrom ) { + registerUsage( (SqmFrom) parentPath, tableGroup ); + } return tableGroup; } private void prepareForSelection(SqmPath joinedPath) { - final SqmPath lhsPath = joinedPath.getLhs(); final FromClauseIndex fromClauseIndex = getFromClauseIndex(); final TableGroup tableGroup = fromClauseIndex.findTableGroup( joinedPath.getNavigablePath() ); if ( tableGroup == null ) { @@ -2354,24 +2423,29 @@ public abstract class BaseSqmToSqlAstConverter extends Base final NavigablePath navigablePath; if ( CollectionPart.Nature.fromNameExact( joinedPath.getNavigablePath().getUnaliasedLocalName() ) != null ) { - navigablePath = lhsPath.getLhs().getNavigablePath(); + navigablePath = joinedPath.getLhs().getLhs().getNavigablePath(); + } + else if ( joinedPath instanceof SqmTreatedRoot ) { + navigablePath = ( (SqmTreatedRoot) joinedPath ).getWrappedPath().getNavigablePath(); } else { - navigablePath = lhsPath.getNavigablePath(); + navigablePath = joinedPath.getLhs().getNavigablePath(); } // todo (6.0): check again if this really is a "requirement" by the JPA spec and document the reference if it is. // The additional join will filter rows, so there would be no way to just select the FK for a nullable association final boolean useInnerJoin = true; // INNER join semantics are required per the JPA spec for select items. - createTableGroup( fromClauseIndex.getTableGroup( navigablePath ), joinedPath, useInnerJoin ); - } - // When we select a treated path, we must add the type restriction as where clause predicate - if ( joinedPath instanceof SqmTreatedPath ) { - final SqmTreatedPath treatedPath = (SqmTreatedPath) joinedPath; - // todo (6.0): the persister and the table group should participate so we can optimize the joins/selects - currentQuerySpec().applyPredicate( - createTreatTypeRestriction( treatedPath.getWrappedPath(), treatedPath.getTreatTarget() ) + final TableGroup createdTableGroup = createTableGroup( + fromClauseIndex.getTableGroup( navigablePath ), + joinedPath, + useInnerJoin ); + if ( createdTableGroup != null && joinedPath instanceof SqmTreatedPath ) { + fromClauseIndex.register( joinedPath, createdTableGroup ); + } + } + else if ( joinedPath instanceof SqmFrom ) { + registerUsage( (SqmFrom) joinedPath, tableGroup ); } } @@ -3000,7 +3074,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base (selectionIndex, selectionMapping) -> { tupleElements.add( new ColumnReference( - tableGroup.getTableReference( + tableGroup.resolveTableReference( navigablePath, selectionMapping.getContainingTableExpression() ), @@ -5254,9 +5328,18 @@ public abstract class BaseSqmToSqlAstConverter extends Base tableGroup ); if ( manyToManyFilterPredicate != null ) { - assert tableGroup.getTableReferenceJoins() != null && - tableGroup.getTableReferenceJoins().size() == 1; - tableGroup.getTableReferenceJoins().get( 0 ).applyPredicate( manyToManyFilterPredicate ); + TableGroupJoin elementTableGroupJoin = null; + for ( TableGroupJoin nestedTableGroupJoin : tableGroup.getNestedTableGroupJoins() ) { + final NavigablePath navigablePath = nestedTableGroupJoin.getNavigablePath(); + if ( navigablePath.getParent() == tableGroup.getNavigablePath() + && CollectionPart.Nature.ELEMENT.getName().equals( navigablePath.getUnaliasedLocalName() ) ) { + elementTableGroupJoin = nestedTableGroupJoin; + break; + } + } + + assert elementTableGroupJoin != null; + elementTableGroupJoin.applyPredicate( manyToManyFilterPredicate ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java index 489ac1407e..a244dec39d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/EntityValuedPathInterpretation.java @@ -12,7 +12,6 @@ import java.util.List; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.BasicValuedModelPart; -import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; @@ -20,27 +19,19 @@ import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ModelPart; -import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.SelectableConsumer; -import org.hibernate.metamodel.mapping.internal.EntityCollectionPart; import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor; import org.hibernate.query.NavigablePath; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath; -import org.hibernate.query.sqm.tree.domain.SqmPath; -import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstWalker; -import org.hibernate.sql.ast.spi.FromClauseAccess; -import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTupleContainer; import org.hibernate.sql.ast.tree.from.TableGroup; -import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableReference; -import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey; @@ -53,16 +44,9 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta public static EntityValuedPathInterpretation from( SqmEntityValuedSimplePath sqmPath, SqmToSqlAstConverter sqlAstCreationState) { - final SqmPath realPath; - if ( CollectionPart.Nature.fromNameExact( sqmPath.getNavigablePath().getUnaliasedLocalName() ) != null ) { - realPath = sqmPath.getLhs(); - } - else { - realPath = sqmPath; - } final TableGroup tableGroup = sqlAstCreationState .getFromClauseAccess() - .findTableGroup( realPath.getLhs().getNavigablePath() ); + .findTableGroup( sqmPath.getLhs().getNavigablePath() ); final EntityValuedModelPart mapping = (EntityValuedModelPart) sqlAstCreationState .getFromClauseAccess() @@ -124,19 +108,6 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta final String lhsTable; final ModelPart lhsPart; boolean useKeyPart = associationMapping.getSideNature() == ForeignKeyDescriptor.Nature.KEY; - if ( mapping instanceof EntityCollectionPart ) { - // EntityCollectionPart always returns TARGET, but sometimes we still want to use the KEY (element) side. - // With a collection table, we can always use the TARGET for referring to the collection, - // but in case of a one-to-many without collection table, this would be problematic, - // as we can't use e.g. the primary key of the owner entity as substitution. - final TableGroup pluralTableGroup = sqlAstCreationState.getFromClauseAccess() - .findTableGroup( navigablePath.getParent() ); - final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) pluralTableGroup.getModelPart(); - if ( pluralAttributeMapping.getSeparateCollectionTable() == null ) { - useKeyPart = true; - tableGroup = pluralTableGroup; - } - } if ( useKeyPart ) { lhsTable = fkDescriptor.getKeyTable(); lhsPart = fkDescriptor.getKeyPart(); @@ -281,46 +252,4 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta return (EntityValuedModelPart) super.getExpressionType(); } - @Override - public DomainResult createDomainResult(String resultVariable, DomainResultCreationState creationState) { - final EntityValuedModelPart mappingType = getExpressionType(); - if ( mappingType instanceof EntityAssociationMapping ) { - final NavigablePath navigablePath = getNavigablePath(); - - // for a to-one or to-many we may not have yet joined to the association table, - // but we need to because the association is a root return and needs to select - // all of the entity columns - - final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); - final FromClauseAccess fromClauseAccess = sqlAstCreationState.getFromClauseAccess(); - final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mappingType; - final TableGroup tableGroup = fromClauseAccess.resolveTableGroup( - navigablePath, - np -> { - final TableGroup parentTableGroup; - if ( getExpressionType() instanceof CollectionPart ) { - parentTableGroup = fromClauseAccess.findTableGroup( np.getParent().getParent() ); - } - else { - parentTableGroup = getTableGroup(); - } - - final TableGroupJoin tableGroupJoin = associationMapping.createTableGroupJoin( - navigablePath, - parentTableGroup, - null, - SqlAstJoinType.INNER, - false, - sqlAstCreationState - ); - parentTableGroup.addTableGroupJoin( tableGroupJoin ); - return tableGroupJoin.getJoinedGroup(); - } - ); - - return associationMapping.createDomainResult( navigablePath, tableGroup, resultVariable, creationState ); - } - - return super.createDomainResult( resultVariable, creationState ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmAttributeJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmAttributeJoin.java index 4ae9c15f7e..aeb95a23b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmAttributeJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmAttributeJoin.java @@ -11,6 +11,7 @@ import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.Predicate; import org.hibernate.metamodel.model.domain.PersistentAttribute; +import org.hibernate.query.NavigablePath; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaPredicate; import org.hibernate.query.sqm.NodeBuilder; @@ -48,9 +49,28 @@ public abstract class AbstractSqmAttributeJoin SqmJoinType joinType, boolean fetched, NodeBuilder nodeBuilder) { + this( + lhs, + SqmCreationHelper.buildSubNavigablePath( lhs, joinedNavigable.getName(), alias ), + joinedNavigable, + alias, + joinType, + fetched, + nodeBuilder + ); + } + + protected AbstractSqmAttributeJoin( + SqmFrom lhs, + NavigablePath navigablePath, + SqmJoinable joinedNavigable, + String alias, + SqmJoinType joinType, + boolean fetched, + NodeBuilder nodeBuilder) { //noinspection unchecked super( - SqmCreationHelper.buildSubNavigablePath( lhs, joinedNavigable.getName(), alias ), + navigablePath, (SqmPathSource) joinedNavigable, lhs, alias, diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java index 1b8a33a7e8..f24550b67d 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -39,7 +40,6 @@ import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.SemanticException; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.UnknownPathException; -import org.hibernate.query.hql.spi.SemanticPathPart; import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.sqm.spi.SqmCreationHelper; import org.hibernate.query.sqm.tree.SqmJoinType; @@ -58,6 +58,7 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements private String alias; private List> joins; + private List> treats; protected AbstractSqmFrom( NavigablePath navigablePath, @@ -168,6 +169,38 @@ public abstract class AbstractSqmFrom extends AbstractSqmPath implements } } + @Override + public boolean hasTreats() { + return treats != null && !treats.isEmpty(); + } + + @Override + public List> getSqmTreats() { + return treats == null ? Collections.emptyList() : treats; + } + + protected > X findTreat(EntityDomainType targetType, String alias) { + if ( treats != null ) { + for ( SqmFrom treat : treats ) { + if ( treat.getModel() == targetType ) { + if ( treat.getExplicitAlias() == null && alias == null + || Objects.equals( treat.getExplicitAlias(), alias ) ) { + //noinspection unchecked + return (X) treat; + } + } + } + } + return null; + } + + protected > X addTreat(X treat) { + if ( treats == null ) { + treats = new ArrayList<>(); + } + treats.add( treat ); + return treat; + } // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // JPA diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java index a7cf74ae10..a433a60f92 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPath.java @@ -59,18 +59,6 @@ public abstract class AbstractSqmPath extends AbstractSqmExpression implem return (SqmPathSource) super.getNodeType(); } - @SuppressWarnings("WeakerAccess") - protected AbstractSqmPath(SqmPathSource referencedPathSource, SqmPath lhs, NodeBuilder nodeBuilder) { - this( - lhs == null - ? new NavigablePath( referencedPathSource.getPathName() ) - : lhs.getNavigablePath().append( referencedPathSource.getPathName() ), - referencedPathSource, - lhs, - nodeBuilder - ); - } - @Override public NavigablePath getNavigablePath() { return navigablePath; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPluralJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPluralJoin.java index e15badcb03..bd41c9ece0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPluralJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPluralJoin.java @@ -9,8 +9,10 @@ package org.hibernate.query.sqm.tree.domain; import jakarta.persistence.criteria.PluralJoin; import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; +import org.hibernate.query.NavigablePath; import org.hibernate.query.criteria.JpaJoin; import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.SqmJoinable; import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.from.SqmFrom; @@ -27,14 +29,18 @@ public abstract class AbstractSqmPluralJoin extends AbstractSqmAttributeJ SqmJoinType joinType, boolean fetched, NodeBuilder nodeBuilder) { - super( - lhs, - joinedNavigable, - alias, - joinType, - fetched, - nodeBuilder - ); + super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder ); + } + + protected AbstractSqmPluralJoin( + SqmFrom lhs, + NavigablePath navigablePath, + PluralPersistentAttribute joinedNavigable, + String alias, + SqmJoinType joinType, + boolean fetched, + NodeBuilder nodeBuilder) { + super( lhs, navigablePath, joinedNavigable, alias, joinType, fetched, nodeBuilder ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java index 43760181e4..c042705b25 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmBagJoin.java @@ -12,7 +12,7 @@ import jakarta.persistence.criteria.Predicate; import org.hibernate.metamodel.model.domain.BagPersistentAttribute; import org.hibernate.metamodel.model.domain.EntityDomainType; -import org.hibernate.query.PathException; +import org.hibernate.query.NavigablePath; import org.hibernate.query.criteria.JpaCollectionJoin; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaPredicate; @@ -36,6 +36,17 @@ public class SqmBagJoin extends AbstractSqmPluralJoin, E> super( lhs, attribute, alias, sqmJoinType, fetched, nodeBuilder ); } + protected SqmBagJoin( + SqmFrom lhs, + NavigablePath navigablePath, + BagPersistentAttribute attribute, + String alias, + SqmJoinType joinType, + boolean fetched, + NodeBuilder nodeBuilder) { + super( lhs, navigablePath, attribute, alias, joinType, fetched, nodeBuilder ); + } + @Override public BagPersistentAttribute getReferencedPathSource() { return (BagPersistentAttribute) super.getReferencedPathSource(); @@ -85,15 +96,27 @@ public class SqmBagJoin extends AbstractSqmPluralJoin, E> } @Override - public SqmTreatedBagJoin treatAs(EntityDomainType treatTarget) throws PathException { - //noinspection unchecked - return new SqmTreatedBagJoin( this, treatTarget, null ); + public SqmTreatedBagJoin treatAs(EntityDomainType treatTarget) { + return treatAs( treatTarget, null ); } @Override - public SqmAttributeJoin makeCopy(SqmCreationProcessingState creationProcessingState) { - //noinspection unchecked - return new SqmBagJoin( + public SqmTreatedBagJoin treatAs(Class treatJavaType, String alias) { + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias ); + } + + @Override + public SqmTreatedBagJoin treatAs(EntityDomainType treatTarget, String alias) { + final SqmTreatedBagJoin treat = findTreat( treatTarget, alias ); + if ( treat == null ) { + return addTreat( new SqmTreatedBagJoin<>( this, treatTarget, alias ) ); + } + return treat; + } + + @Override + public SqmAttributeJoin makeCopy(SqmCreationProcessingState creationProcessingState) { + return new SqmBagJoin<>( creationProcessingState.getPathRegistry().findFromByPath( getLhs().getNavigablePath() ), getReferencedPathSource(), getExplicitAlias(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java index c0fc839c59..33b8b32d4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmListJoin.java @@ -12,6 +12,8 @@ import jakarta.persistence.criteria.Predicate; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.ListPersistentAttribute; +import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; +import org.hibernate.query.NavigablePath; import org.hibernate.query.PathException; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaListJoin; @@ -39,6 +41,17 @@ public class SqmListJoin super( lhs, listAttribute, alias, sqmJoinType, fetched, nodeBuilder ); } + protected SqmListJoin( + SqmFrom lhs, + NavigablePath navigablePath, + ListPersistentAttribute listAttribute, + String alias, + SqmJoinType joinType, + boolean fetched, + NodeBuilder nodeBuilder) { + super( lhs, navigablePath, listAttribute, alias, joinType, fetched, nodeBuilder ); + } + @Override public ListPersistentAttribute getReferencedPathSource() { return (ListPersistentAttribute) super.getReferencedPathSource(); @@ -88,12 +101,26 @@ public class SqmListJoin @Override public SqmTreatedListJoin treatAs(Class treatAsType) { - return treatAs( nodeBuilder().getDomainModel().entity( treatAsType ) ); + return treatAs( nodeBuilder().getDomainModel().entity( treatAsType ), null ); } @Override - public SqmTreatedListJoin treatAs(EntityDomainType treatTarget) throws PathException { - return new SqmTreatedListJoin<>( this, treatTarget, null ); + public SqmTreatedListJoin treatAs(EntityDomainType treatTarget) { + return treatAs( treatTarget, null ); + } + + @Override + public SqmTreatedListJoin treatAs(Class treatJavaType, String alias) { + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias ); + } + + @Override + public SqmTreatedListJoin treatAs(EntityDomainType treatTarget, String alias) { + final SqmTreatedListJoin treat = findTreat( treatTarget, alias ); + if ( treat == null ) { + return addTreat( new SqmTreatedListJoin<>( this, treatTarget, alias ) ); + } + return treat; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java index 08435e55ac..5898b2a831 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmMapJoin.java @@ -13,6 +13,8 @@ import jakarta.persistence.criteria.Predicate; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.MapPersistentAttribute; +import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; +import org.hibernate.query.NavigablePath; import org.hibernate.query.PathException; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaMapJoin; @@ -40,6 +42,17 @@ public class SqmMapJoin super( lhs, pluralValuedNavigable, alias, sqmJoinType, fetched, nodeBuilder ); } + protected SqmMapJoin( + SqmFrom lhs, + NavigablePath navigablePath, + MapPersistentAttribute pluralValuedNavigable, + String alias, + SqmJoinType joinType, + boolean fetched, + NodeBuilder nodeBuilder) { + super( lhs, navigablePath, pluralValuedNavigable, alias, joinType, fetched, nodeBuilder ); + } + @Override public MapPersistentAttribute getReferencedPathSource() { return(MapPersistentAttribute) super.getReferencedPathSource(); @@ -103,13 +116,27 @@ public class SqmMapJoin } @Override - public SqmTreatedMapJoin treatAs(Class treatJavaType) throws PathException { + public SqmTreatedMapJoin treatAs(Class treatJavaType) { return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) ); } @Override - public SqmTreatedMapJoin treatAs(EntityDomainType treatTarget) throws PathException { - return new SqmTreatedMapJoin<>( this, treatTarget, null ); + public SqmTreatedMapJoin treatAs(EntityDomainType treatTarget) { + return treatAs( treatTarget, null ); + } + + @Override + public SqmTreatedMapJoin treatAs(Class treatJavaType, String alias) { + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias ); + } + + @Override + public SqmTreatedMapJoin treatAs(EntityDomainType treatTarget, String alias) { + final SqmTreatedMapJoin treat = findTreat( treatTarget, alias ); + if ( treat == null ) { + return addTreat( new SqmTreatedMapJoin<>( this, treatTarget, alias ) ); + } + return treat; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java index 29a726fcaf..4e1414abac 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmPath.java @@ -114,10 +114,10 @@ public interface SqmPath extends SqmExpression, SemanticPathPart, JpaPath< } @Override - SqmTreatedPath treatAs(Class treatJavaType) throws PathException; + SqmPath treatAs(Class treatJavaType); @Override - SqmTreatedPath treatAs(EntityDomainType treatTarget) throws PathException; + SqmPath treatAs(EntityDomainType treatTarget); default SqmRoot findRoot() { final SqmPath lhs = getLhs(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java index e18193828c..b0b452d883 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSetJoin.java @@ -12,7 +12,9 @@ import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.Predicate; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; import org.hibernate.metamodel.model.domain.SetPersistentAttribute; +import org.hibernate.query.NavigablePath; import org.hibernate.query.PathException; import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaPredicate; @@ -39,6 +41,16 @@ public class SqmSetJoin super( lhs, pluralValuedNavigable, alias, sqmJoinType, fetched, nodeBuilder ); } + protected SqmSetJoin( + SqmFrom lhs, + NavigablePath navigablePath, + SetPersistentAttribute pluralValuedNavigable, + String alias, SqmJoinType joinType, + boolean fetched, + NodeBuilder nodeBuilder) { + super( lhs, navigablePath, pluralValuedNavigable, alias, joinType, fetched, nodeBuilder ); + } + @Override public SetPersistentAttribute getReferencedPathSource() { return (SetPersistentAttribute) super.getReferencedPathSource(); @@ -85,8 +97,22 @@ public class SqmSetJoin } @Override - public SqmTreatedSetJoin treatAs(EntityDomainType treatTarget) throws PathException { - return new SqmTreatedSetJoin<>( this, treatTarget, null ); + public SqmTreatedSetJoin treatAs(EntityDomainType treatTarget) { + return treatAs( treatTarget, null ); + } + + @Override + public SqmTreatedSetJoin treatAs(Class treatJavaType, String alias) { + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias ); + } + + @Override + public SqmTreatedSetJoin treatAs(EntityDomainType treatTarget, String alias) { + final SqmTreatedSetJoin treat = findTreat( treatTarget, alias ); + if ( treat == null ) { + return addTreat( new SqmTreatedSetJoin<>( this, treatTarget, alias ) ); + } + return treat; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java index 2dfc9fa8a1..f70a6e161f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java @@ -10,9 +10,11 @@ import java.util.Locale; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; +import org.hibernate.query.NavigablePath; import org.hibernate.query.PathException; import org.hibernate.query.hql.spi.SqmCreationProcessingState; import org.hibernate.query.sqm.NodeBuilder; +import org.hibernate.query.sqm.SqmJoinable; import org.hibernate.query.sqm.sql.internal.DomainResultProducer; import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; @@ -32,6 +34,17 @@ public class SqmSingularJoin extends AbstractSqmAttributeJoin implemen super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder ); } + protected SqmSingularJoin( + SqmFrom lhs, + NavigablePath navigablePath, + SingularPersistentAttribute joinedNavigable, + String alias, + SqmJoinType joinType, + boolean fetched, + NodeBuilder nodeBuilder) { + super( lhs, navigablePath, joinedNavigable, alias, joinType, fetched, nodeBuilder ); + } + @Override public SingularPersistentAttribute getReferencedPathSource() { return (SingularPersistentAttribute) super.getReferencedPathSource(); @@ -49,13 +62,27 @@ public class SqmSingularJoin extends AbstractSqmAttributeJoin implemen } @Override - public SqmTreatedSingularJoin treatAs(Class treatJavaType) throws PathException { + public SqmTreatedSingularJoin treatAs(Class treatJavaType) { return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) ); } @Override - public SqmTreatedSingularJoin treatAs(EntityDomainType treatTarget) throws PathException { - return new SqmTreatedSingularJoin<>( this, treatTarget, null ); + public SqmTreatedSingularJoin treatAs(EntityDomainType treatTarget) { + return treatAs( treatTarget, null ); + } + + @Override + public SqmTreatedSingularJoin treatAs(Class treatJavaType, String alias) { + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias ); + } + + @Override + public SqmTreatedSingularJoin treatAs(EntityDomainType treatTarget, String alias) { + final SqmTreatedSingularJoin treat = findTreat( treatTarget, alias ); + if ( treat == null ) { + return addTreat( new SqmTreatedSingularJoin<>( this, treatTarget, alias ) ); + } + return treat; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java index 9c193b94be..8b7a2661a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedBagJoin.java @@ -8,6 +8,7 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.metamodel.model.domain.BagPersistentAttribute; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.TreatedNavigablePath; import org.hibernate.query.hql.spi.SqmCreationProcessingState; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; @@ -28,6 +29,10 @@ public class SqmTreatedBagJoin extends SqmBagJoin impleme //noinspection unchecked super( wrappedPath.getLhs(), + wrappedPath.getNavigablePath().treatAs( + treatTarget.getHibernateEntityName(), + alias + ), (BagPersistentAttribute) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedCrossJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedCrossJoin.java index bf84bcafc4..c2044ca521 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedCrossJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedCrossJoin.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.TreatedNavigablePath; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.from.SqmCrossJoin; import org.hibernate.query.sqm.tree.from.SqmJoin; @@ -20,10 +21,11 @@ public class SqmTreatedCrossJoin extends SqmCrossJoin impleme public SqmTreatedCrossJoin( SqmCrossJoin wrappedPath, - String alias, - EntityDomainType treatTarget) { + EntityDomainType treatTarget, + String alias) { //noinspection unchecked super( + wrappedPath.getNavigablePath().treatAs( treatTarget.getHibernateEntityName(), alias ), (EntityDomainType) wrappedPath.getReferencedPathSource().getSqmPathType(), alias, wrappedPath.getRoot() diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java index 9482645ebe..a90bb45ca4 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedEntityJoin.java @@ -7,7 +7,9 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.TreatedNavigablePath; import org.hibernate.query.sqm.SqmPathSource; +import org.hibernate.query.sqm.spi.SqmCreationHelper; import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.from.SqmEntityJoin; import org.hibernate.query.sqm.tree.from.SqmJoin; @@ -22,12 +24,15 @@ public class SqmTreatedEntityJoin extends SqmEntityJoin imple public SqmTreatedEntityJoin( SqmEntityJoin wrappedPath, EntityDomainType treatTarget, - String alias, - SqmJoinType joinType) { + String alias) { super( + wrappedPath.getNavigablePath().treatAs( + treatTarget.getHibernateEntityName(), + alias + ), treatTarget, alias, - joinType, + wrappedPath.getSqmJoinType(), wrappedPath.getRoot() ); this.wrappedPath = wrappedPath; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java index 577fe3bee4..f8f34eede3 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedListJoin.java @@ -8,6 +8,7 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.ListPersistentAttribute; +import org.hibernate.query.TreatedNavigablePath; import org.hibernate.query.hql.spi.SqmCreationProcessingState; import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.sqm.SqmPathSource; @@ -30,6 +31,10 @@ public class SqmTreatedListJoin extends SqmListJoin imple //noinspection unchecked super( wrappedPath.getLhs(), + wrappedPath.getNavigablePath().treatAs( + treatTarget.getHibernateEntityName(), + alias + ), (ListPersistentAttribute) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java index 0a9f907bb4..1920bee3c2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedMapJoin.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.TreatedNavigablePath; import org.hibernate.query.hql.spi.SqmCreationProcessingState; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.from.SqmJoin; @@ -26,6 +27,10 @@ public class SqmTreatedMapJoin extends SqmMapJoin //noinspection unchecked super( wrappedPath.getLhs(), + wrappedPath.getNavigablePath().treatAs( + treatTarget.getHibernateEntityName(), + alias + ), ( (SqmMapJoin) wrappedPath ).getModel(), alias, wrappedPath.getSqmJoinType(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java index d6faf74657..9ce5024d1f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedRoot.java @@ -8,6 +8,7 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.NavigablePath; +import org.hibernate.query.TreatedNavigablePath; import org.hibernate.query.hql.spi.SemanticPathPart; import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.sqm.NodeBuilder; @@ -27,13 +28,14 @@ public class SqmTreatedRoot extends SqmRoot implements SqmTre @SuppressWarnings({ "unchecked", "rawtypes" }) public SqmTreatedRoot( SqmRoot wrappedPath, - EntityDomainType treatTarget, - NodeBuilder nodeBuilder) { + EntityDomainType treatTarget) { super( - wrappedPath.getNavigablePath(), + wrappedPath.getNavigablePath().treatAs( + treatTarget.getHibernateEntityName() + ), (EntityDomainType) wrappedPath.getReferencedPathSource(), null, - nodeBuilder + wrappedPath.nodeBuilder() ); this.wrappedPath = wrappedPath; this.treatTarget = treatTarget; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java index de78ee4798..3681f9b439 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSetJoin.java @@ -8,6 +8,7 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.SetPersistentAttribute; +import org.hibernate.query.TreatedNavigablePath; import org.hibernate.query.hql.spi.SqmCreationProcessingState; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; @@ -28,6 +29,10 @@ public class SqmTreatedSetJoin extends SqmSetJoin impleme //noinspection unchecked super( wrappedPath.getLhs(), + wrappedPath.getNavigablePath().treatAs( + treatTarget.getHibernateEntityName(), + alias + ), (SetPersistentAttribute) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java index 0fa7416443..393801fd46 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSimplePath.java @@ -8,6 +8,7 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.PathException; +import org.hibernate.query.TreatedNavigablePath; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; @@ -27,7 +28,9 @@ public class SqmTreatedSimplePath EntityDomainType treatTarget, NodeBuilder nodeBuilder) { super( - wrappedPath.getNavigablePath(), + wrappedPath.getNavigablePath().treatAs( + treatTarget.getHibernateEntityName() + ), (EntityDomainType) wrappedPath.getReferencedPathSource(), wrappedPath.getLhs(), nodeBuilder @@ -42,7 +45,9 @@ public class SqmTreatedSimplePath EntityDomainType treatTarget, NodeBuilder nodeBuilder) { super( - wrappedPath.getNavigablePath(), + wrappedPath.getNavigablePath().treatAs( + treatTarget.getHibernateEntityName() + ), (EntityDomainType) wrappedPath.getReferencedPathSource(), wrappedPath.getLhs(), nodeBuilder diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java index a44e7be728..655b1b1972 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmTreatedSingularJoin.java @@ -8,6 +8,7 @@ package org.hibernate.query.sqm.tree.domain; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; +import org.hibernate.query.TreatedNavigablePath; import org.hibernate.query.hql.spi.SqmCreationProcessingState; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.from.SqmAttributeJoin; @@ -28,6 +29,10 @@ public class SqmTreatedSingularJoin extends SqmSingularJoin) wrappedPath.getAttribute(), alias, wrappedPath.getSqmJoinType(), diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java index e22a752443..0e69da0a20 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmEnumLiteral.java @@ -42,7 +42,7 @@ public class SqmEnumLiteral> extends AbstractSqmExpression setExpressableType( this ); } - public Enum getEnumValue() { + public E getEnumValue() { return enumValue; } @@ -74,8 +74,8 @@ public class SqmEnumLiteral> extends AbstractSqmExpression } @Override - public SqmPath resolveIndexedAccess( - SqmExpression selector, + public SqmPath resolveIndexedAccess( + SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { throw new SemanticException( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java index f82ba3bfa6..1c2c5c0fd7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFieldLiteral.java @@ -221,8 +221,8 @@ public class SqmFieldLiteral implements SqmExpression, SqmExpressable, } @Override - public SqmPath resolveIndexedAccess( - SqmExpression selector, + public SqmPath resolveIndexedAccess( + SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { throw new SemanticException( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java index 8a3f21f1fc..a00ea9dd52 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmFunction.java @@ -187,8 +187,8 @@ public abstract class SqmFunction extends AbstractSqmExpression } @Override - public SqmPath resolveIndexedAccess( - SqmExpression selector, + public SqmPath resolveIndexedAccess( + SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { throw new UnsupportedOperationException(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java index 0a6623ec81..13231a2aef 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/SqmLiteralEntityType.java @@ -72,8 +72,8 @@ public class SqmLiteralEntityType } @Override - public SqmPath resolveIndexedAccess( - SqmExpression selector, + public SqmPath resolveIndexedAccess( + SqmExpression selector, boolean isTerminal, SqmCreationState creationState) { throw new HqlInterpretationException( "Cannot dereference an entity name" ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmAttributeJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmAttributeJoin.java index 3b049ba945..720d855b3e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmAttributeJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmAttributeJoin.java @@ -7,10 +7,12 @@ package org.hibernate.query.sqm.tree.from; import org.hibernate.HibernateException; +import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.query.criteria.JpaFetch; import org.hibernate.query.criteria.JpaJoin; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.hql.spi.SqmCreationProcessingState; +import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.predicate.SqmPredicate; import org.hibernate.type.descriptor.java.JavaType; @@ -36,6 +38,12 @@ public interface SqmAttributeJoin extends SqmQualifiedJoin, JpaFetch SqmAttributeJoin treatAs(Class treatJavaType); + + @Override + SqmAttributeJoin treatAs(EntityDomainType treatTarget); + SqmAttributeJoin makeCopy(SqmCreationProcessingState creationProcessingState); class NotJoinableException extends HibernateException { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java index b83d570170..141afbe296 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmCrossJoin.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm.tree.from; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.NavigablePath; import org.hibernate.query.PathException; import org.hibernate.query.hql.spi.SqmCreationProcessingState; import org.hibernate.query.hql.spi.SqmPathRegistry; @@ -29,8 +30,21 @@ public class SqmCrossJoin extends AbstractSqmFrom implements SqmJoin joinedEntityDescriptor, String alias, SqmRoot sqmRoot) { + this( + buildRootNavigablePath( joinedEntityDescriptor.getHibernateEntityName(), alias ), + joinedEntityDescriptor, + alias, + sqmRoot + ); + } + + protected SqmCrossJoin( + NavigablePath navigablePath, + EntityDomainType joinedEntityDescriptor, + String alias, + SqmRoot sqmRoot) { super( - buildRootNavigablePath( alias, joinedEntityDescriptor.getHibernateEntityName() ), + navigablePath, joinedEntityDescriptor, sqmRoot, alias, @@ -88,7 +102,21 @@ public class SqmCrossJoin extends AbstractSqmFrom implements SqmJoin SqmTreatedCrossJoin treatAs(EntityDomainType treatTarget) throws PathException { - return new SqmTreatedCrossJoin<>( this, null, treatTarget ); + final SqmTreatedCrossJoin treat = findTreat( treatTarget, null ); + if ( treat == null ) { + return addTreat( new SqmTreatedCrossJoin<>( this, treatTarget, null ) ); + } + return treat; + } + + @Override + public SqmFrom treatAs(Class treatJavaType, String alias) { + throw new UnsupportedOperationException(); + } + + @Override + public SqmFrom treatAs(EntityDomainType treatTarget, String alias) { + throw new UnsupportedOperationException(); } public SqmCrossJoin makeCopy(SqmCreationProcessingState creationProcessingState) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java index ebb3caa157..d776a6e4d8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmEntityJoin.java @@ -7,6 +7,7 @@ package org.hibernate.query.sqm.tree.from; import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.NavigablePath; import org.hibernate.query.PathException; import org.hibernate.query.criteria.JpaEntityJoin; import org.hibernate.query.hql.spi.SqmCreationProcessingState; @@ -34,14 +35,22 @@ public class SqmEntityJoin extends AbstractSqmJoin implements SqmQualif String alias, SqmJoinType joinType, SqmRoot sqmRoot) { - super( + this( SqmCreationHelper.buildRootNavigablePath( joinedEntityDescriptor.getHibernateEntityName(), alias ), joinedEntityDescriptor, - sqmRoot, alias, joinType, - sqmRoot.nodeBuilder() + sqmRoot ); + } + + protected SqmEntityJoin( + NavigablePath navigablePath, + EntityDomainType joinedEntityDescriptor, + String alias, + SqmJoinType joinType, + SqmRoot sqmRoot) { + super( navigablePath, joinedEntityDescriptor, sqmRoot, alias, joinType, sqmRoot.nodeBuilder() ); this.sqmRoot = sqmRoot; } @@ -106,7 +115,21 @@ public class SqmEntityJoin extends AbstractSqmJoin implements SqmQualif } @Override public SqmTreatedEntityJoin treatAs(EntityDomainType treatTarget) throws PathException { - return new SqmTreatedEntityJoin<>( this, treatTarget, null, getSqmJoinType() ); + final SqmTreatedEntityJoin treat = findTreat( treatTarget, null ); + if ( treat == null ) { + return addTreat( new SqmTreatedEntityJoin<>( this, treatTarget, null ) ); + } + return treat; + } + + @Override + public SqmFrom treatAs(Class treatJavaType, String alias) { + throw new UnsupportedOperationException(); + } + + @Override + public SqmFrom treatAs(EntityDomainType treatTarget, String alias) { + throw new UnsupportedOperationException(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFrom.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFrom.java index ca2961172c..76cc6069dd 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFrom.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFrom.java @@ -19,7 +19,11 @@ import jakarta.persistence.metamodel.MapAttribute; import jakarta.persistence.metamodel.SetAttribute; import jakarta.persistence.metamodel.SingularAttribute; +import org.hibernate.metamodel.model.domain.EntityDomainType; +import org.hibernate.query.PathException; import org.hibernate.query.criteria.JpaFrom; +import org.hibernate.query.criteria.JpaJoin; +import org.hibernate.query.criteria.JpaPath; import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.tree.SqmVisitableNode; @@ -65,6 +69,23 @@ public interface SqmFrom extends SqmVisitableNode, SqmPath, JpaFrom> consumer); + @Override + SqmFrom treatAs(Class treatAsType); + + @Override + SqmFrom treatAs(EntityDomainType treatAsType); + + SqmFrom treatAs(Class treatJavaType, String alias); + + SqmFrom treatAs(EntityDomainType treatTarget, String alias); + + boolean hasTreats(); + + /** + * The treats associated with this SqmFrom + */ + List> getSqmTreats(); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // JPA diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java index 501562cfe7..02ea0f21d7 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmRoot.java @@ -13,7 +13,6 @@ import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.NavigablePath; import org.hibernate.query.PathException; -import org.hibernate.query.criteria.JpaEntityJoin; import org.hibernate.query.criteria.JpaRoot; import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.SemanticQueryWalker; @@ -23,7 +22,6 @@ import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.domain.AbstractSqmFrom; import org.hibernate.query.sqm.tree.domain.SqmCorrelatedRoot; import org.hibernate.query.sqm.tree.domain.SqmPath; -import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.query.sqm.tree.domain.SqmTreatedRoot; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; @@ -150,7 +148,7 @@ public class SqmRoot extends AbstractSqmFrom implements JpaRoot, Doma return false; } } - return true; + return !hasTreats(); } @Override @@ -160,14 +158,27 @@ public class SqmRoot extends AbstractSqmFrom implements JpaRoot, Doma @Override public SqmTreatedRoot treatAs(Class treatJavaType) throws PathException { - return (SqmTreatedRoot) treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) ); + return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) ); } @Override - public SqmTreatedPath treatAs(EntityDomainType treatTarget) throws PathException { - return new SqmTreatedRoot<>( this, treatTarget, nodeBuilder() ); + public SqmTreatedRoot treatAs(EntityDomainType treatTarget) throws PathException { + final SqmTreatedRoot treat = findTreat( treatTarget, null ); + if ( treat == null ) { + return addTreat( new SqmTreatedRoot<>( this, treatTarget ) ); + } + return treat; } + @Override + public SqmFrom treatAs(Class treatJavaType, String alias) { + throw new UnsupportedOperationException(); + } + + @Override + public SqmFrom treatAs(EntityDomainType treatTarget, String alias) { + throw new UnsupportedOperationException(); + } @Override public DomainResult createDomainResult( diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java index 4f14e210b6..a2cd63cbd9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/spi/AbstractSqlAstTranslator.java @@ -3387,6 +3387,7 @@ public abstract class AbstractSqlAstTranslator implemen // sqlAppender.appendSql( tableGroup.getGroupAlias() ); // } + processNestedTableGroupJoins( tableGroup ); processTableGroupJoins( tableGroup ); ModelPartContainer modelPart = tableGroup.getModelPart(); if ( modelPart instanceof AbstractEntityPersister ) { @@ -3429,6 +3430,7 @@ public abstract class AbstractSqlAstTranslator implemen if ( !realTableGroup ) { renderTableReferenceJoins( tableGroup ); + processNestedTableGroupJoins( tableGroup ); } processTableGroupJoins( tableGroup ); @@ -3453,6 +3455,9 @@ public abstract class AbstractSqlAstTranslator implemen private boolean hasTableGroupsToRender(List nestedTableGroupJoins) { for ( TableGroupJoin nestedTableGroupJoin : nestedTableGroupJoins ) { final TableGroup joinedGroup = nestedTableGroupJoin.getJoinedGroup(); + if ( joinedGroup instanceof VirtualTableGroup ) { + return !joinedGroup.getTableGroupJoins().isEmpty() || !joinedGroup.getNestedTableGroupJoins().isEmpty(); + } if ( !( joinedGroup instanceof LazyTableGroup ) || ( (LazyTableGroup) joinedGroup ).isInitialized() ) { return true; } @@ -3531,6 +3536,7 @@ public abstract class AbstractSqlAstTranslator implemen if ( joinedGroup instanceof VirtualTableGroup ) { processTableGroupJoins( tableGroupJoin.getJoinedGroup() ); + processNestedTableGroupJoins( tableGroupJoin.getJoinedGroup() ); } else if ( !( joinedGroup instanceof LazyTableGroup ) || ( (LazyTableGroup) joinedGroup ).getUnderlyingTableGroup() != null ) { appendSql( WHITESPACE ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java index 300b62813f..942d139b85 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/cte/CteTableGroup.java @@ -73,7 +73,8 @@ public class CteTableGroup implements TableGroup { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { + boolean allowFkOptimization, + boolean resolve) { if ( cteTableReference.getTableExpression().equals( tableExpression ) ) { return cteTableReference; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractColumnReferenceQualifier.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractColumnReferenceQualifier.java index ee11a9e876..f5bcce2e83 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractColumnReferenceQualifier.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/AbstractColumnReferenceQualifier.java @@ -34,7 +34,8 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc final TableReference tableReference = getTableReferenceInternal( navigablePath, tableExpression, - allowFkOptimization + allowFkOptimization, + true ); if ( tableReference == null ) { throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" ); @@ -47,18 +48,21 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { - return getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization ); + boolean allowFkOptimization, + boolean resolve) { + return getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); } protected TableReference getTableReferenceInternal( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { + boolean allowFkOptimization, + boolean resolve) { final TableReference primaryTableReference = getPrimaryTableReference().getTableReference( navigablePath, tableExpression, - allowFkOptimization + allowFkOptimization, + resolve ); if ( primaryTableReference != null) { return primaryTableReference; @@ -68,7 +72,8 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc final TableReference tableReference = tableJoin.getJoinedTableReference().getTableReference( navigablePath, tableExpression, - allowFkOptimization + allowFkOptimization, + resolve ); if ( tableReference != null) { return tableReference; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CollectionTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CollectionTableGroup.java new file mode 100644 index 0000000000..79871da3ab --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CollectionTableGroup.java @@ -0,0 +1,106 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.from; + +import java.util.function.BiFunction; +import java.util.function.Predicate; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.ast.spi.SqlAliasBase; + +/** + * A table group for collection tables of plural attributes. + * + * @author Christian Beikov + */ +public class CollectionTableGroup extends StandardTableGroup { + + private TableGroup indexTableGroup; + private TableGroup elementTableGroup; + + public CollectionTableGroup( + boolean canUseInnerJoins, + NavigablePath navigablePath, + PluralAttributeMapping tableGroupProducer, + boolean fetched, + String sourceAlias, + TableReference primaryTableReference, + boolean realTableGroup, + SqlAliasBase sqlAliasBase, + Predicate tableReferenceJoinNameChecker, + BiFunction tableReferenceJoinCreator, + SessionFactoryImplementor sessionFactory) { + super( + canUseInnerJoins, + navigablePath, + tableGroupProducer, + fetched, + sourceAlias, + primaryTableReference, + realTableGroup, + sqlAliasBase, + tableReferenceJoinNameChecker, + tableReferenceJoinCreator, + sessionFactory + ); + } + + public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin) { + assert this.indexTableGroup == null; + this.indexTableGroup = indexTableGroupJoin.getJoinedGroup(); + addNestedTableGroupJoin( indexTableGroupJoin ); + } + + public void registerElementTableGroup(TableGroupJoin elementTableGroupJoin) { + assert this.elementTableGroup == null; + this.elementTableGroup = elementTableGroupJoin.getJoinedGroup(); + addNestedTableGroupJoin( elementTableGroupJoin ); + } + + @Override + protected TableReference getTableReferenceInternal( + NavigablePath navigablePath, + String tableExpression, + boolean allowFkOptimization, + boolean resolve) { + final TableReference tableReference = super.getTableReferenceInternal( + navigablePath, + tableExpression, + allowFkOptimization, + resolve + ); + if ( tableReference != null ) { + return tableReference; + } + if ( indexTableGroup != null ) { + final TableReference indexTableReference = indexTableGroup.getTableReference( + navigablePath, + tableExpression, + allowFkOptimization, + resolve + ); + if ( indexTableReference != null ) { + return indexTableReference; + } + } + if ( elementTableGroup != null ) { + final TableReference elementTableReference = elementTableGroup.getTableReference( + navigablePath, + tableExpression, + allowFkOptimization, + resolve + ); + if ( elementTableReference != null ) { + return elementTableReference; + } + } + return null; + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ColumnReferenceQualifier.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ColumnReferenceQualifier.java index 1edd67301b..a816aab804 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ColumnReferenceQualifier.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ColumnReferenceQualifier.java @@ -16,21 +16,26 @@ public interface ColumnReferenceQualifier { return resolveTableReference( navigablePath, tableExpression, true ); } + default TableReference resolveTableReference(String tableExpression) { + return resolveTableReference( null, tableExpression, true ); + } + TableReference resolveTableReference( NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization); default TableReference getTableReference(NavigablePath navigablePath, String tableExpression) { - return getTableReference( navigablePath, tableExpression, true ); + return getTableReference( navigablePath, tableExpression, true, false ); + } + + default TableReference getTableReference(String tableExpression) { + return getTableReference( null, tableExpression, true, false ); } TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization); - - default TableReference getTableReference(String tableExpression) { - return getTableReference( null, tableExpression, true ); - } + boolean allowFkOptimization, + boolean resolve); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java index 4ea83ca8bc..bd001721d4 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CompositeTableGroup.java @@ -140,8 +140,9 @@ public class CompositeTableGroup implements VirtualTableGroup { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { - return underlyingTableGroup.getTableReference( navigablePath, tableExpression, allowFkOptimization ); + boolean allowFkOptimization, + boolean resolve) { + return underlyingTableGroup.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); } @Override @@ -152,7 +153,8 @@ public class CompositeTableGroup implements VirtualTableGroup { final TableReference tableReference = underlyingTableGroup.getTableReference( navigablePath, tableExpression, - allowFkOptimization + allowFkOptimization, + true ); if ( tableReference != null ) { return tableReference; @@ -160,7 +162,7 @@ public class CompositeTableGroup implements VirtualTableGroup { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization ); + .getTableReference( navigablePath, tableExpression, allowFkOptimization, true ); if ( primaryTableReference != null ) { return primaryTableReference; } @@ -168,7 +170,7 @@ public class CompositeTableGroup implements VirtualTableGroup { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization ); + .getTableReference( navigablePath, tableExpression, allowFkOptimization, true ); if ( primaryTableReference != null ) { return primaryTableReference; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java index f3ece8655d..bfd3edc951 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedTableGroup.java @@ -60,11 +60,13 @@ public class CorrelatedTableGroup extends AbstractTableGroup { protected TableReference getTableReferenceInternal( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { + boolean allowFkOptimization, + boolean resolve) { final TableReference primaryTableReference = correlatedTableGroup.getPrimaryTableReference().getTableReference( navigablePath, tableExpression, - allowFkOptimization + allowFkOptimization, + resolve ); if ( primaryTableReference != null ) { return primaryTableReference; @@ -72,7 +74,7 @@ public class CorrelatedTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization ); + .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); if ( groupTableReference != null ) { return groupTableReference; } @@ -80,7 +82,7 @@ public class CorrelatedTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization ); + .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); if ( groupTableReference != null ) { return groupTableReference; } @@ -89,7 +91,8 @@ public class CorrelatedTableGroup extends AbstractTableGroup { final TableReference tableReference = tableReferenceJoin.getJoinedTableReference().getTableReference( navigablePath, tableExpression, - allowFkOptimization + allowFkOptimization, + resolve ); if ( tableReference != null ) { return tableReference; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java index d300b767bb..fdb34c3f06 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/LazyTableGroup.java @@ -180,21 +180,23 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements } @Override - public TableReference getTableReferenceInternal( + protected TableReference getTableReferenceInternal( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { + boolean allowFkOptimization, + boolean resolve) { if ( allowFkOptimization && ( navigablePath == null || navigablePathChecker.test( navigablePath, tableExpression ) ) ) { final TableReference reference = parentTableGroup.getTableReference( navigablePath, tableExpression, - allowFkOptimization + allowFkOptimization, + resolve ); if ( reference != null ) { return reference; } } - return getTableGroup().getTableReference( navigablePath, tableExpression, allowFkOptimization ); + return getTableGroup().getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java index 60ead12a3f..502af0e49d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MutatingTableReferenceGroupWrapper.java @@ -63,7 +63,8 @@ public class MutatingTableReferenceGroupWrapper implements VirtualTableGroup { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { + boolean allowFkOptimization, + boolean resolve) { return mutatingTableReference.getTableExpression().equals( tableExpression ) ? mutatingTableReference : null; @@ -74,7 +75,7 @@ public class MutatingTableReferenceGroupWrapper implements VirtualTableGroup { NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization) { - return getTableReference( navigablePath, tableExpression, allowFkOptimization ); + return getTableReference( navigablePath, tableExpression, allowFkOptimization, true ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java new file mode 100644 index 0000000000..fbf4065a18 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/OneToManyTableGroup.java @@ -0,0 +1,177 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.sql.ast.tree.from; + +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.query.NavigablePath; +import org.hibernate.sql.results.graph.DomainResult; +import org.hibernate.sql.results.graph.DomainResultCreationState; + +/** + * A table group for one-to-many plural attributes. + * Delegates by default to the element table group, + * but also provides access to the index table group table references. + * + * @author Christian Beikov + */ +public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implements TableGroup { + private final SessionFactoryImplementor sessionFactory; + private final PluralAttributeMapping pluralAttributeMapping; + private final TableGroup elementTableGroup; + private TableGroup indexTableGroup; + + public OneToManyTableGroup( + PluralAttributeMapping pluralAttributeMapping, + TableGroup elementTableGroup, + SessionFactoryImplementor sessionFactory) { + this.pluralAttributeMapping = pluralAttributeMapping; + this.elementTableGroup = elementTableGroup; + this.sessionFactory = sessionFactory; + } + + @Override + public PluralAttributeMapping getExpressionType() { + return pluralAttributeMapping; + } + + @Override + public PluralAttributeMapping getModelPart() { + return pluralAttributeMapping; + } + + @Override + protected SessionFactoryImplementor getSessionFactory() { + return sessionFactory; + } + + public TableGroup getElementTableGroup() { + return elementTableGroup; + } + + public void registerIndexTableGroup(TableGroupJoin indexTableGroupJoin) { + assert this.indexTableGroup == null; + this.indexTableGroup = indexTableGroupJoin.getJoinedGroup(); + addNestedTableGroupJoin( indexTableGroupJoin ); + } + + @Override + public String getGroupAlias() { + return elementTableGroup.getGroupAlias(); + } + + @Override + public String getSourceAlias() { + return elementTableGroup.getSourceAlias(); + } + + @Override + public void applyAffectedTableNames(Consumer nameCollector) { + elementTableGroup.applyAffectedTableNames( nameCollector ); + } + + @Override + public TableReference getPrimaryTableReference() { + return elementTableGroup.getPrimaryTableReference(); + } + + @Override + public List getTableReferenceJoins() { + return elementTableGroup.getTableReferenceJoins(); + } + + @Override + public NavigablePath getNavigablePath() { + return elementTableGroup.getNavigablePath().getParent(); + } + + @Override + public List getTableGroupJoins() { + return elementTableGroup.getTableGroupJoins(); + } + + @Override + public List getNestedTableGroupJoins() { + return elementTableGroup.getNestedTableGroupJoins(); + } + + @Override + public boolean canUseInnerJoins() { + return elementTableGroup.canUseInnerJoins(); + } + + @Override + public void addTableGroupJoin(TableGroupJoin join) { + if ( join.getJoinedGroup() != elementTableGroup ) { + elementTableGroup.addTableGroupJoin( join ); + } + } + + @Override + public void addNestedTableGroupJoin(TableGroupJoin join) { + if ( join.getJoinedGroup() != elementTableGroup ) { + elementTableGroup.addNestedTableGroupJoin( join ); + } + } + + @Override + public void visitTableGroupJoins(Consumer consumer) { + elementTableGroup.visitTableGroupJoins( consumer ); + } + + @Override + public void visitNestedTableGroupJoins(Consumer consumer) { + elementTableGroup.visitNestedTableGroupJoins( consumer ); + } + + @Override + public DomainResult createDomainResult(String resultVariable, DomainResultCreationState creationState) { + return elementTableGroup.createDomainResult( resultVariable, creationState ); + } + + @Override + public void applySqlSelections(DomainResultCreationState creationState) { + elementTableGroup.applySqlSelections( creationState ); + } + + @Override + public boolean isRealTableGroup() { + return elementTableGroup.isRealTableGroup(); + } + + @Override + public boolean isFetched() { + return elementTableGroup.isFetched(); + } + + @Override + protected TableReference getTableReferenceInternal( + NavigablePath navigablePath, + String tableExpression, + boolean allowFkOptimization, + boolean resolve) { + final TableReference tableReference = elementTableGroup.getTableReference( + navigablePath, + tableExpression, + allowFkOptimization, + resolve + ); + if ( tableReference != null || indexTableGroup == null ) { + return tableReference; + } + + return indexTableGroup.getTableReference( + navigablePath, + tableExpression, + allowFkOptimization, + resolve + ); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/RootTableGroupProducer.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/RootTableGroupProducer.java index 35ba775955..1cc43a8137 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/RootTableGroupProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/RootTableGroupProducer.java @@ -15,6 +15,7 @@ import org.hibernate.query.NavigablePath; import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationState; +import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.tree.predicate.Predicate; /** @@ -40,5 +41,6 @@ public interface RootTableGroupProducer extends TableGroupProducer, ModelPartCon String explicitSourceAlias, Supplier> additionalPredicateCollectorAccess, SqlAliasBase sqlAliasBase, - SqlAstCreationState creationState, SqlAstCreationContext creationContext); + SqlExpressionResolver expressionResolver, + SqlAstCreationContext creationContext); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java index fb1a7c4c0d..27022f277f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardTableGroup.java @@ -131,13 +131,16 @@ public class StandardTableGroup extends AbstractTableGroup { } @Override - public TableReference getTableReferenceInternal( + protected TableReference getTableReferenceInternal( NavigablePath navigablePath, - String tableExpression, boolean allowFkOptimization) { + String tableExpression, + boolean allowFkOptimization, + boolean resolve) { final TableReference tableReference = primaryTableReference.getTableReference( navigablePath, tableExpression, - allowFkOptimization + allowFkOptimization, + resolve ); if ( tableReference != null ) { return tableReference; @@ -149,25 +152,25 @@ public class StandardTableGroup extends AbstractTableGroup { final TableReferenceJoin join = tableJoins.get( i ); assert join != null; final TableReference resolveTableReference = join.getJoinedTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization ); + .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); if ( resolveTableReference != null ) { return resolveTableReference; } } } - return potentiallyCreateTableReference( tableExpression ); + return resolve ? potentiallyCreateTableReference( tableExpression ) : null; } for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); - if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization ) != null ) { + if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { return primaryTableReference; } } for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); - if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization ) != null ) { + if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { return primaryTableReference; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReference.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReference.java index dbe0a70f74..68ffa0069e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReference.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableReference.java @@ -23,6 +23,7 @@ public class TableReference implements SqlAstNode, ColumnReferenceQualifier { private final String identificationVariable; private final boolean isOptional; + private String prunedTableExpression; public TableReference( String tableExpression, @@ -35,7 +36,7 @@ public class TableReference implements SqlAstNode, ColumnReferenceQualifier { } public String getTableExpression() { - return tableExpression; + return prunedTableExpression == null ? tableExpression : prunedTableExpression; } public String getIdentificationVariable() { @@ -46,6 +47,10 @@ public class TableReference implements SqlAstNode, ColumnReferenceQualifier { return isOptional; } + public void setPrunedTableExpression(String prunedTableExpression) { + this.prunedTableExpression = prunedTableExpression; + } + @Override public void accept(SqlAstWalker sqlTreeWalker) { sqlTreeWalker.visitTableReference( this ); @@ -66,7 +71,8 @@ public class TableReference implements SqlAstNode, ColumnReferenceQualifier { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { + boolean allowFkOptimization, + boolean resolve) { if ( this.tableExpression.equals( tableExpression ) ) { return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java index 9ab3322abd..d6088f779b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableGroup.java @@ -137,7 +137,8 @@ public class UnionTableGroup implements VirtualTableGroup { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { + boolean allowFkOptimization, + boolean resolve) { return resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); } @@ -146,7 +147,7 @@ public class UnionTableGroup implements VirtualTableGroup { NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization) { - if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization ) != null ) { + if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, true ) != null ) { return tableReference; } if ( nestedTableGroupJoins != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java index 37d54785b2..6e77b42ba7 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/UnionTableReference.java @@ -41,7 +41,8 @@ public class UnionTableReference extends TableReference { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { + boolean allowFkOptimization, + boolean resolve) { if ( hasTableExpression( tableExpression ) ) { return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java index c6b9dce525..ff0fec6aa1 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/exec/spi/JdbcParameterBindings.java @@ -10,13 +10,26 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; +import java.util.stream.Stream; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.spi.JdbcServices; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.metamodel.mapping.BasicValuedMapping; import org.hibernate.metamodel.mapping.Bindable; +import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.model.domain.AllowableParameterType; import org.hibernate.query.internal.BindingTypeHelper; +import org.hibernate.query.spi.QueryParameterBinding; +import org.hibernate.query.spi.QueryParameterBindings; +import org.hibernate.query.spi.QueryParameterImplementor; +import org.hibernate.query.sql.internal.NativeQueryImpl; +import org.hibernate.query.sql.spi.ParameterOccurrence; import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl; +import org.hibernate.sql.exec.internal.JdbcParameterImpl; /** * Access to all of the externalized JDBC parameter bindings @@ -97,4 +110,59 @@ public interface JdbcParameterBindings { session ); } + + default void registerNativeQueryParameters( + QueryParameterBindings queryParameterBindings, + List parameterList, + List jdbcParameterBinders, + SessionFactoryImplementor factory) { + final Dialect dialect = factory.getServiceRegistry().getService( JdbcServices.class ).getJdbcEnvironment().getDialect(); + final boolean paddingEnabled = factory.getSessionFactoryOptions().inClauseParameterPaddingEnabled(); + final int inExprLimit = dialect.getInExpressionCountLimit(); + + for ( ParameterOccurrence occurrence : parameterList ) { + final QueryParameterImplementor param = occurrence.getParameter(); + final QueryParameterBinding binding = queryParameterBindings.getBinding( param ); + AllowableParameterType type = binding.getBindType(); + if ( type == null ) { + type = param.getHibernateType(); + } + if ( type == null ) { + type = factory.getTypeConfiguration().getBasicTypeForJavaType( Object.class ); + } + final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping(); + + if ( binding.isMultiValued() ) { + final Collection bindValues = binding.getBindValues(); + final int bindValueCount = bindValues.size(); + Object lastBindValue = null; + for ( Object bindValue : bindValues ) { + final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); + jdbcParameterBinders.add( jdbcParameter ); + addBinding( jdbcParameter, new JdbcParameterBindingImpl( jdbcMapping, bindValue ) ); + lastBindValue = bindValue; + } + final int bindValueMaxCount = NativeQueryImpl.determineBindValueMaxCount( + paddingEnabled, + inExprLimit, + bindValueCount + ); + if ( bindValueMaxCount != bindValueCount ) { + for ( int i = bindValueCount; i < bindValueMaxCount; i++ ) { + final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); + jdbcParameterBinders.add( jdbcParameter ); + addBinding( jdbcParameter, new JdbcParameterBindingImpl( jdbcMapping, lastBindValue ) ); + } + } + } + else { + final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping ); + jdbcParameterBinders.add( jdbcParameter ); + addBinding( + jdbcParameter, + new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() ) + ); + } + } + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/AbstractFetchParent.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/AbstractFetchParent.java index ad1d86e844..6e0bd3e9fb 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/AbstractFetchParent.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/AbstractFetchParent.java @@ -26,8 +26,9 @@ public abstract class AbstractFetchParent implements FetchParent { this.navigablePath = navigablePath; } - protected void afterInitialize(DomainResultCreationState creationState) { - this.fetches = creationState.visitFetches( this ); + public void afterInitialize(FetchParent fetchParent, DomainResultCreationState creationState) { + assert fetches == null; + this.fetches = creationState.visitFetches( fetchParent ); } public FetchableContainer getFetchContainer() { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/FetchParent.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/FetchParent.java index 487080cc36..7d3cbb727e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/FetchParent.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/FetchParent.java @@ -65,7 +65,8 @@ public interface FetchParent extends DomainResultGraphNode { fetchParentType = fetchableEntityType; } if ( fetchParentType != fetchableEntityType ) { - return new TreatedNavigablePath( getNavigablePath(), fetchableEntityType.getEntityName() ) + // todo (6.0): if the fetchParentType is a subtype of fetchableEntityType this shouldn't be necessary + return getNavigablePath().treatAs( fetchableEntityType.getEntityName() ) .append( fetchableName ); } return getNavigablePath().append( fetchableName ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicFetch.java index 1d90cafa02..ed8def2dd3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/basic/BasicFetch.java @@ -30,7 +30,6 @@ public class BasicFetch implements Fetch, BasicResultGraphNode { private final NavigablePath navigablePath; private final FetchParent fetchParent; private final BasicValuedModelPart valuedMapping; - private final boolean nullable; private final DomainResultAssembler assembler; @@ -41,7 +40,6 @@ public class BasicFetch implements Fetch, BasicResultGraphNode { FetchParent fetchParent, NavigablePath fetchablePath, BasicValuedModelPart valuedMapping, - boolean nullable, BasicValueConverter valueConverter, FetchTiming fetchTiming, DomainResultCreationState creationState) { @@ -50,7 +48,6 @@ public class BasicFetch implements Fetch, BasicResultGraphNode { fetchParent, fetchablePath, valuedMapping, - nullable, valueConverter, fetchTiming, true, @@ -63,12 +60,10 @@ public class BasicFetch implements Fetch, BasicResultGraphNode { FetchParent fetchParent, NavigablePath fetchablePath, BasicValuedModelPart valuedMapping, - boolean nullable, BasicValueConverter valueConverter, FetchTiming fetchTiming, boolean canBasicPartFetchBeDelayed, DomainResultCreationState creationState) { - this.nullable = nullable; this.navigablePath = fetchablePath; this.fetchParent = fetchParent; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java index 4b88781cbe..dfa7e41351 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EntityCollectionPartTableGroup.java @@ -114,8 +114,9 @@ public class EntityCollectionPartTableGroup implements TableGroup { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { - return collectionTableGroup.getTableReference( navigablePath, tableExpression, allowFkOptimization ); + boolean allowFkOptimization, + boolean resolve) { + return collectionTableGroup.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java index 422779d5b5..9fda8eba0b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/AbstractEmbeddableInitializer.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.function.Supplier; import org.hibernate.NotYetImplementedFor6Exception; +import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; @@ -30,7 +31,7 @@ import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer; import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; -import org.hibernate.tuple.IdentifierAttribute; +import org.hibernate.type.descriptor.java.spi.EntityJavaTypeDescriptor; /** * @author Steve Ebersole @@ -133,13 +134,15 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA return; } - // Special handling for non-aggregated attribute _identifierMapper - // The _identifierMapper attribute uses the actual entity instance as container, so we use the fetch parent - // The identifier domain result on the other hand for that attribute has no fetch parent, - // but that's no issue because that attribute uses the id class as container class + // Special handling for non-aggregated attributes which use the actual entity instance as container, + // which we access through the fetch parent access. + // If this model part is an identifier, we must construct the instance as this is called during resolveKey final EmbeddableMappingType embeddableTypeDescriptor = embeddedModelPartDescriptor.getEmbeddableTypeDescriptor(); if ( fetchParentAccess != null && embeddableTypeDescriptor.getMappedJavaTypeDescriptor().getJavaTypeClass() - .isAssignableFrom( fetchParentAccess.getInitializedPart().getJavaTypeDescriptor().getJavaTypeClass() ) ) { + .isAssignableFrom( fetchParentAccess.getInitializedPart().getJavaTypeDescriptor().getJavaTypeClass() ) + && embeddableTypeDescriptor.getMappedJavaTypeDescriptor() instanceof EntityJavaTypeDescriptor + && !( embeddedModelPartDescriptor instanceof CompositeIdentifierMapping ) + && !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embeddedModelPartDescriptor.getFetchableName() ) ) { fetchParentAccess.resolveInstance( rowProcessingState ); compositeInstance = fetchParentAccess.getInitializedInstance(); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java index 76a0cf7497..2c06963dcd 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableFetchImpl.java @@ -38,7 +38,6 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab private final FetchTiming fetchTiming; private final TableGroup tableGroup; private final boolean hasTableGroup; - private final boolean nullable; public EmbeddableFetchImpl( NavigablePath navigablePath, @@ -46,7 +45,6 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab FetchParent fetchParent, FetchTiming fetchTiming, boolean hasTableGroup, - boolean nullable, DomainResultCreationState creationState) { super( embeddedPartDescriptor.getEmbeddableTypeDescriptor(), navigablePath ); this.embeddedPartDescriptor = embeddedPartDescriptor; @@ -54,7 +52,6 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab this.fetchParent = fetchParent; this.fetchTiming = fetchTiming; this.hasTableGroup = hasTableGroup; - this.nullable = nullable; this.tableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup( getNavigablePath(), @@ -66,7 +63,7 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab getNavigablePath(), lhsTableGroup, null, - nullable ? SqlAstJoinType.LEFT : SqlAstJoinType.INNER, + SqlAstJoinType.INNER, true, creationState.getSqlAstCreationState() ); @@ -76,7 +73,7 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab ); - afterInitialize( creationState ); + afterInitialize( this, creationState ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java index 508458d877..2d132caad3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/embeddable/internal/EmbeddableResultImpl.java @@ -61,7 +61,7 @@ public class EmbeddableResultImpl extends AbstractFetchParent implements Embe } ); - afterInitialize( creationState ); + afterInitialize( this, creationState ); // after-after-initialize :D containsAnyNonScalars = determineIfContainedAnyScalars( fetches ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java index 92c2847a04..9c311e7c60 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityInitializer.java @@ -6,12 +6,9 @@ */ package org.hibernate.sql.results.graph.entity; -import java.util.ArrayList; import java.util.IdentityHashMap; -import java.util.List; import java.util.Map; import java.util.function.Consumer; -import java.util.function.Supplier; import org.hibernate.HibernateException; import org.hibernate.LockMode; @@ -22,6 +19,7 @@ import org.hibernate.cache.spi.access.EntityDataAccess; import org.hibernate.cache.spi.entry.CacheEntry; import org.hibernate.engine.spi.EntityEntry; 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.PersistentAttributeInterceptor; @@ -40,22 +38,23 @@ import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.UniqueKeyLoadable; import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.LazyInitializer; import org.hibernate.proxy.map.MapProxy; import org.hibernate.query.NavigablePath; -import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.results.graph.AbstractFetchParentAccess; 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.Initializer; import org.hibernate.sql.results.internal.NullValueAssembler; import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingState; import org.hibernate.sql.results.jdbc.spi.RowProcessingState; import org.hibernate.stat.spi.StatisticsImplementor; +import org.hibernate.type.AssociationType; import org.hibernate.type.BasicType; +import org.hibernate.type.Type; import org.hibernate.type.TypeHelper; import static org.hibernate.internal.log.LoggingHelper.toLoggableString; @@ -79,8 +78,6 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces private final NavigablePath navigablePath; private final LockMode lockMode; - private final List identifierInitializers = new ArrayList<>(); - private final DomainResultAssembler identifierAssembler; private final DomainResultAssembler discriminatorAssembler; private final DomainResultAssembler versionAssembler; @@ -104,7 +101,7 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces EntityResultGraphNode resultDescriptor, NavigablePath navigablePath, LockMode lockMode, - DomainResult identifierResult, + Fetch identifierFetch, Fetch discriminatorFetch, DomainResult rowIdResult, AssemblerCreationState creationState) { @@ -125,50 +122,10 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces this.lockMode = lockMode; assert lockMode != null; - if ( identifierResult != null ) { - this.identifierAssembler = identifierResult.createResultAssembler( - new AssemblerCreationState() { - @Override - public LockMode determineEffectiveLockMode(String identificationVariable) { - return creationState.determineEffectiveLockMode( identificationVariable ); - } - - @Override - public Initializer resolveInitializer( - NavigablePath navigablePath, - ModelPart fetchedModelPart, - Supplier producer) { - for ( int i = 0; i < identifierInitializers.size(); i++ ) { - final Initializer existing = identifierInitializers.get( i ); - if ( existing.getNavigablePath().equals( navigablePath ) - && fetchedModelPart.getNavigableRole() - .equals( existing.getInitializedPart().getNavigableRole() ) ) { - assert fetchedModelPart == existing.getInitializedPart(); - return existing; - } - } - -// // also check the non-identifier initializers -// final Initializer otherExisting = creationState.resolveInitializer( -// navigablePath, -// () -> null -// ); -// -// if ( otherExisting != null ) { -// identifierInitializers.add( otherExisting ); -// return otherExisting; -// } - - final Initializer initializer = creationState.resolveInitializer( navigablePath, fetchedModelPart, producer ); - identifierInitializers.add( initializer ); - return initializer; - } - - @Override - public SqlAstCreationContext getSqlAstCreationContext() { - return creationState.getSqlAstCreationContext(); - } - } + if ( identifierFetch != null ) { + this.identifierAssembler = identifierFetch.createAssembler( + this, + creationState ); } else { @@ -418,36 +375,12 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces return id; } - initializeIdentifier( rowProcessingState ); return identifierAssembler.assemble( rowProcessingState, jdbcValuesSourceProcessingState.getProcessingOptions() ); } - @SuppressWarnings("WeakerAccess") - protected void initializeIdentifier(RowProcessingState rowProcessingState) { - if ( EntityLoadingLogger.TRACE_ENABLED ) { - EntityLoadingLogger.LOGGER.tracef( - "(%s) Beginning Initializer#initializeIdentifier process for entity (%s) ", - StringHelper.collapse( this.getClass().getName() ), - getNavigablePath() - ); - } - - identifierInitializers.forEach( initializer -> initializer.resolveKey( rowProcessingState ) ); - identifierInitializers.forEach( initializer -> initializer.resolveInstance( rowProcessingState ) ); - identifierInitializers.forEach( initializer -> initializer.initializeInstance( rowProcessingState ) ); - - if ( EntityLoadingLogger.TRACE_ENABLED ) { - EntityLoadingLogger.LOGGER.tracef( - "(%s) Fiish Initializer#initializeIdentifier process for entity (%s) ", - StringHelper.collapse( this.getClass().getName() ), - getNavigablePath() - ); - } - } - @Override public void resolveInstance(RowProcessingState rowProcessingState) { if ( missing ) { @@ -728,6 +661,35 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces persistenceContext.addEntity( entityKey, toInitialize ); + // Also register possible unique key entries + for ( Type propertyType : concreteDescriptor.getPropertyTypes() ) { + if ( propertyType instanceof AssociationType ) { + final AssociationType associationType = (AssociationType) propertyType; + final String ukName = associationType.getLHSPropertyName(); + if ( ukName != null ) { + final int index = ( (UniqueKeyLoadable) concreteDescriptor ).getPropertyIndex( ukName ); + final Type type = concreteDescriptor.getPropertyTypes()[index]; + + // polymorphism not really handled completely correctly, + // perhaps...well, actually its ok, assuming that the + // entity name used in the lookup is the same as the + // the one used here, which it will be + + if ( resolvedEntityState[index] != null ) { + final EntityUniqueKey euk = new EntityUniqueKey( + concreteDescriptor.getRootEntityDescriptor().getEntityName(), + //polymorphism comment above + ukName, + resolvedEntityState[index], + type, + session.getFactory() + ); + session.getPersistenceContextInternal().addEntity( euk, toInitialize ); + } + } + } + } + final Object version; if ( versionAssembler != null ) { @@ -959,7 +921,6 @@ public abstract class AbstractEntityInitializer extends AbstractFetchParentAcces entityInstanceForNotify = null; missing = false; resolvedEntityState = null; - identifierInitializers.forEach( initializer -> initializer.finishUpRow( rowProcessingState ) ); clearParentResolutionListeners(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java index 493adf663f..eed9553e86 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractEntityResultGraphNode.java @@ -12,6 +12,7 @@ import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityRowIdMapping; import org.hibernate.metamodel.mapping.EntityValuedModelPart; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; @@ -23,6 +24,9 @@ import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.results.graph.AbstractFetchParent; import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResultCreationState; +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.basic.BasicFetch; import org.hibernate.type.descriptor.java.JavaType; @@ -35,54 +39,60 @@ import static org.hibernate.query.results.ResultsHelper.attributeName; */ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent implements EntityResultGraphNode { private final EntityValuedModelPart referencedModelPart; - private final DomainResult identifierResult; - private final BasicFetch discriminatorFetch; - private final DomainResult rowIdResult; + private Fetch identifierFetch; + private BasicFetch discriminatorFetch; + private DomainResult rowIdResult; - public AbstractEntityResultGraphNode( - EntityValuedModelPart referencedModelPart, - NavigablePath navigablePath, - DomainResultCreationState creationState) { + public AbstractEntityResultGraphNode(EntityValuedModelPart referencedModelPart, NavigablePath navigablePath) { super( referencedModelPart.getEntityMappingType(), navigablePath ); this.referencedModelPart = referencedModelPart; + } + @Override + public void afterInitialize(FetchParent fetchParent, DomainResultCreationState creationState) { final EntityMappingType entityDescriptor = referencedModelPart.getEntityMappingType(); - - final TableGroup entityTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().findTableGroup( navigablePath ); - final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); - + final NavigablePath navigablePath = getNavigablePath(); + final TableGroup entityTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess() + .getTableGroup( navigablePath ); final EntityIdentifierNavigablePath identifierNavigablePath = new EntityIdentifierNavigablePath( navigablePath, attributeName( identifierMapping ) ); if ( navigablePath.getParent() == null && !creationState.forceIdentifierSelection() ) { - identifierResult = null; + identifierFetch = null; visitIdentifierMapping( identifierNavigablePath, creationState, identifierMapping, entityTableGroup ); } else if ( referencedModelPart instanceof ToOneAttributeMapping ) { // If we don't do this here, LazyTableGroup#getTableReferenceInternal would have to use the target table in case {id} is encountered if ( ( (ToOneAttributeMapping) referencedModelPart ).canJoinForeignKey( identifierMapping ) ) { - identifierResult = ( (ToOneAttributeMapping) referencedModelPart ).getForeignKeyDescriptor() - .createKeyDomainResult( - navigablePath, - creationState.getSqlAstCreationState() - .getFromClauseAccess() - .findTableGroup( navigablePath.getParent() ), + final ForeignKeyDescriptor foreignKeyDescriptor = ( (ToOneAttributeMapping) referencedModelPart ).getForeignKeyDescriptor(); + identifierFetch = ( (Fetchable) foreignKeyDescriptor.getKeyPart() ) + .generateFetch( + fetchParent, + fetchParent.getNavigablePath() + .append( ( (Fetchable) foreignKeyDescriptor.getKeyPart() ).getFetchableName() ), + FetchTiming.IMMEDIATE, + true, + null, creationState ); } else { - identifierResult = identifierMapping.createDomainResult( + identifierFetch = ( (Fetchable) identifierMapping ).generateFetch( + fetchParent, identifierNavigablePath, - ( (LazyTableGroup) entityTableGroup ).getTableGroup(), + FetchTiming.IMMEDIATE, + true, null, creationState ); } } else { - identifierResult = identifierMapping.createDomainResult( + identifierFetch = ( (Fetchable) identifierMapping ).generateFetch( + fetchParent, identifierNavigablePath, - entityTableGroup, + FetchTiming.IMMEDIATE, + true, null, creationState ); @@ -92,7 +102,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent // No need to fetch the discriminator if this type does not have subclasses if ( discriminatorMapping != null && entityDescriptor.hasSubclasses() ) { discriminatorFetch = discriminatorMapping.generateFetch( - this, + fetchParent, navigablePath.append( EntityDiscriminatorMapping.ROLE_NAME ), FetchTiming.IMMEDIATE, true, @@ -116,6 +126,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent creationState ); } + super.afterInitialize( fetchParent, creationState ); } private void visitIdentifierMapping( @@ -171,8 +182,8 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent return getEntityValuedModelPart().getEntityMappingType().getMappedJavaTypeDescriptor(); } - public DomainResult getIdentifierResult() { - return identifierResult; + public Fetch getIdentifierFetch() { + return identifierFetch; } public BasicFetch getDiscriminatorFetch() { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchImpl.java index cde021fb78..1e7dd296bb 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedFetchImpl.java @@ -23,14 +23,17 @@ import org.hibernate.sql.results.graph.entity.EntityInitializer; public class EntityDelayedFetchImpl extends AbstractNonJoinedEntityFetch { private final DomainResult keyResult; + private final boolean selectByUniqueKey; public EntityDelayedFetchImpl( FetchParent fetchParent, ToOneAttributeMapping fetchedAttribute, NavigablePath navigablePath, - DomainResult keyResult) { + DomainResult keyResult, + boolean selectByUniqueKey) { super( navigablePath, fetchedAttribute, fetchParent ); this.keyResult = keyResult; + this.selectByUniqueKey = selectByUniqueKey; } @Override @@ -52,8 +55,10 @@ public class EntityDelayedFetchImpl extends AbstractNonJoinedEntityFetch { navigablePath, getEntityValuedModelPart(), () -> new EntityDelayedFetchInitializer( + parentAccess, navigablePath, getEntityValuedModelPart(), + selectByUniqueKey, keyResult.createResultAssembler( creationState ) ) ); 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 786ce7f8f9..b4b4b8f7b4 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 @@ -12,6 +12,7 @@ import org.hibernate.LockMode; import org.hibernate.NotYetImplementedFor6Exception; import org.hibernate.bytecode.enhance.spi.LazyPropertyInitializer; import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.EntityUniqueKey; import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.event.spi.EventSource; @@ -20,10 +21,12 @@ import org.hibernate.loader.entity.CacheEntityLoaderHelper; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.persister.entity.UniqueKeyLoadable; import org.hibernate.proxy.HibernateProxy; import org.hibernate.query.NavigablePath; import org.hibernate.sql.results.graph.AbstractFetchParentAccess; import org.hibernate.sql.results.graph.DomainResultAssembler; +import org.hibernate.sql.results.graph.FetchParentAccess; import org.hibernate.sql.results.graph.entity.EntityInitializer; import org.hibernate.sql.results.graph.entity.EntityValuedFetchable; import org.hibernate.sql.results.graph.entity.LoadingEntityEntry; @@ -36,19 +39,25 @@ import org.hibernate.stat.spi.StatisticsImplementor; */ public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess implements EntityInitializer { + private final FetchParentAccess parentAccess; private final NavigablePath navigablePath; - private final EntityValuedFetchable referencedModelPart; + private final ToOneAttributeMapping referencedModelPart; + private final boolean selectByUniqueKey; private final DomainResultAssembler identifierAssembler; private Object entityInstance; private Object identifier; public EntityDelayedFetchInitializer( + FetchParentAccess parentAccess, NavigablePath fetchedNavigable, - EntityValuedFetchable referencedModelPart, + ToOneAttributeMapping referencedModelPart, + boolean selectByUniqueKey, DomainResultAssembler identifierAssembler) { + this.parentAccess = parentAccess; this.navigablePath = fetchedNavigable; this.referencedModelPart = referencedModelPart; + this.selectByUniqueKey = selectByUniqueKey; this.identifierAssembler = identifierAssembler; } @@ -79,26 +88,63 @@ public class EntityDelayedFetchInitializer extends AbstractFetchParentAccess imp entityInstance = null; } else { + final SharedSessionContractImplementor session = rowProcessingState.getSession(); final EntityPersister concreteDescriptor = referencedModelPart.getEntityMappingType().getEntityPersister(); - final EntityKey entityKey = new EntityKey( identifier, concreteDescriptor ); - final PersistenceContext persistenceContext = rowProcessingState.getSession().getPersistenceContext(); + if ( !selectByUniqueKey ) { + final EntityKey entityKey = new EntityKey( identifier, concreteDescriptor ); + final PersistenceContext persistenceContext = session.getPersistenceContext(); - final LoadingEntityEntry loadingEntityLocally = persistenceContext.getLoadContexts() - .findLoadingEntityEntry( entityKey ); - if ( loadingEntityLocally != null ) { - entityInstance = loadingEntityLocally.getEntityInstance(); + final LoadingEntityEntry loadingEntityLocally = persistenceContext.getLoadContexts() + .findLoadingEntityEntry( entityKey ); + if ( loadingEntityLocally != null ) { + entityInstance = loadingEntityLocally.getEntityInstance(); + } } - else { - if ( referencedModelPart.isOptional() ) { + if ( entityInstance == null ) { + if ( referencedModelPart.isOptional() && parentAccess != null && parentAccess.getInitializedPart() + .findContainingEntityMapping() + .getEntityPersister() + .getBytecodeEnhancementMetadata() + .isEnhancedForLazyLoading() ) { entityInstance = LazyPropertyInitializer.UNFETCHED_PROPERTY; } else { - entityInstance = rowProcessingState.getSession().internalLoad( - concreteDescriptor.getEntityName(), - identifier, - false, - false - ); + if ( selectByUniqueKey ) { + final String uniqueKeyPropertyName = referencedModelPart.getBidirectionalAttributeName(); + final EntityUniqueKey euk = new EntityUniqueKey( + concreteDescriptor.getEntityName(), + uniqueKeyPropertyName, + identifier, + concreteDescriptor.getIdentifierType(), + session.getFactory() + ); + final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); + entityInstance = persistenceContext.getEntity( euk ); + if ( entityInstance == null ) { + entityInstance = ( (UniqueKeyLoadable) concreteDescriptor ).loadByUniqueKey( + uniqueKeyPropertyName, + identifier, + session + ); + + // If the entity was not in the Persistence Context, but was found now, + // add it to the Persistence Context + if ( entityInstance != null ) { + persistenceContext.addEntity( euk, entityInstance ); + } + } + if ( entityInstance != null ) { + entityInstance = persistenceContext.proxyFor( entityInstance ); + } + } + else { + entityInstance = session.internalLoad( + concreteDescriptor.getEntityName(), + identifier, + false, + false + ); + } if ( entityInstance instanceof HibernateProxy ) { ( (HibernateProxy) entityInstance ).getHibernateLazyInitializer() diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedResultImpl.java index 515c6f8791..0975692e0a 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityDelayedResultImpl.java @@ -64,8 +64,10 @@ public class EntityDelayedResultImpl implements DomainResult { getNavigablePath(), entityValuedModelPart, () -> new EntityDelayedFetchInitializer( + null, getNavigablePath(), (ToOneAttributeMapping) entityValuedModelPart, + false, identifierResult.createResultAssembler( creationState ) ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java index 363aed415c..6c62aee628 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityFetchJoinedImpl.java @@ -42,6 +42,7 @@ public class EntityFetchJoinedImpl extends AbstractNonLazyEntityFetch { null, creationState ); + this.entityResult.afterInitialize( this, creationState ); } @Override @@ -56,7 +57,7 @@ public class EntityFetchJoinedImpl extends AbstractNonLazyEntityFetch { getReferencedModePart(), getNavigablePath(), creationState.determineEffectiveLockMode( sourceAlias ), - entityResult.getIdentifierResult(), + entityResult.getIdentifierFetch(), entityResult.getDiscriminatorFetch(), creationState ) diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java index 3b9917b231..5a32885d40 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityJoinedFetchInitializer.java @@ -26,20 +26,20 @@ public class EntityJoinedFetchInitializer extends AbstractEntityInitializer { private final boolean isEnhancedForLazyLoading; - protected EntityJoinedFetchInitializer( + public EntityJoinedFetchInitializer( EntityResultGraphNode resultDescriptor, ModelPart referencedModelPart, NavigablePath navigablePath, LockMode lockMode, - DomainResult identifierResult, - Fetch discriminatorResult, + Fetch identifierFetch, + Fetch discriminatorFetch, AssemblerCreationState creationState) { super( resultDescriptor, navigablePath, lockMode, - identifierResult, - discriminatorResult, + identifierFetch, + discriminatorFetch, null, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java index 374ba31e62..50f1e132e1 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultImpl.java @@ -48,15 +48,9 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode implements E String resultVariable, EntityMappingType targetType, DomainResultCreationState creationState) { - super( - entityValuedModelPart, - navigablePath, - creationState - ); + super( entityValuedModelPart, navigablePath ); this.tableGroup = tableGroup; this.resultVariable = resultVariable; - - afterInitialize( creationState ); } @Override @@ -100,7 +94,7 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode implements E this, getNavigablePath(), getLockMode( creationState ), - getIdentifierResult(), + getIdentifierFetch(), getDiscriminatorFetch(), getRowIdResult(), creationState diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java index 0dce0a665f..bbb117fe03 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultInitializer.java @@ -10,6 +10,7 @@ import org.hibernate.LockMode; import org.hibernate.query.NavigablePath; import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.DomainResult; +import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.sql.results.graph.entity.AbstractEntityInitializer; import org.hibernate.sql.results.graph.entity.EntityResultGraphNode; @@ -26,7 +27,7 @@ public class EntityResultInitializer extends AbstractEntityInitializer { EntityResultGraphNode resultDescriptor, NavigablePath navigablePath, LockMode lockMode, - DomainResult identifierResult, + Fetch identifierFetch, BasicFetch discriminatorFetch, DomainResult rowIdResult, AssemblerCreationState creationState) { @@ -34,7 +35,7 @@ public class EntityResultInitializer extends AbstractEntityInitializer { resultDescriptor, navigablePath, lockMode, - identifierResult, + identifierFetch, discriminatorFetch, rowIdResult, creationState diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java index bb7dca81c0..039e07f016 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/EntityResultJoinedSubclassImpl.java @@ -36,7 +36,7 @@ public class EntityResultJoinedSubclassImpl extends EntityResultImpl { this, getNavigablePath(), getLockMode( creationState ), - getIdentifierResult(), + getIdentifierFetch(), getDiscriminatorFetch(), getRowIdResult(), creationState 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 e37a1b3958..f19e40bf88 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 @@ -44,15 +44,15 @@ public class EntitySelectFetchByUniqueKeyInitializer extends EntitySelectFetchIn return; } final String entityName = concreteDescriptor.getEntityName(); - String uniqueKeyPropertyName = fetchedAttribute.getBidirectionalAttributeName(); + final String uniqueKeyPropertyName = fetchedAttribute.getBidirectionalAttributeName(); final SharedSessionContractImplementor session = rowProcessingState.getSession(); - EntityUniqueKey euk = new EntityUniqueKey( + final EntityUniqueKey euk = new EntityUniqueKey( entityName, uniqueKeyPropertyName, entityIdentifier, - concreteDescriptor.getIdentifierType(), + concreteDescriptor.getPropertyType( uniqueKeyPropertyName ), session.getFactory() ); final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); 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 28c131b42e..e972bca9ba 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 @@ -20,6 +20,7 @@ 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.query.EntityIdentifierNavigablePath; import org.hibernate.query.NavigablePath; import org.hibernate.sql.results.graph.AbstractFetchParentAccess; import org.hibernate.sql.results.graph.DomainResultAssembler; @@ -79,10 +80,24 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl @Override public void resolveInstance(RowProcessingState rowProcessingState) { + // Defer the select by default to the initialize phase + // We only need to select in this phase if this is part of an identifier + NavigablePath np = navigablePath.getParent(); + while ( np != null ) { + if ( np instanceof EntityIdentifierNavigablePath ) { + initializeInstance( rowProcessingState ); + return; + } + np = np.getParent(); + } } @Override public void initializeInstance(RowProcessingState rowProcessingState) { + if ( entityInstance != null ) { + return; + } + List attributeMappings; if ( parentAccess instanceof EmbeddableInitializer ) { attributeMappings = ( (EmbeddableInitializer) parentAccess ).getInitializedPart() @@ -97,10 +112,6 @@ public class EntitySelectFetchInitializer extends AbstractFetchParentAccess impl return; } - if ( entityInstance != null ) { - return; - } - final Object entityIdentifier = identifierAssembler.assemble( rowProcessingState ); if ( entityIdentifier == null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java index a66e34719d..a5ad58b1e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/internal/domain/CircularFetchImpl.java @@ -108,16 +108,16 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association { getNavigablePath(), referencedModelPart, () -> { - if ( selectByUniqueKey ) { - return new EntitySelectFetchByUniqueKeyInitializer( - parentAccess, - fetchable, - getNavigablePath(), - entityMappingType.getEntityPersister(), - resultAssembler - ); - } if ( timing == FetchTiming.IMMEDIATE ) { + if ( selectByUniqueKey ) { + return new EntitySelectFetchByUniqueKeyInitializer( + parentAccess, + fetchable, + getNavigablePath(), + entityMappingType.getEntityPersister(), + resultAssembler + ); + } final EntityPersister entityPersister = entityMappingType.getEntityPersister(); if ( entityPersister.isBatchLoadable() ) { return new BatchEntitySelectFetchInitializer( @@ -140,8 +140,10 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association { } else { return new EntityDelayedFetchInitializer( + parentAccess, getReferencedPath(), fetchable, + selectByUniqueKey, resultAssembler ); } @@ -238,6 +240,7 @@ public class CircularFetchImpl implements BiDirectionalFetch, Association { @Override public Object assemble(RowProcessingState rowProcessingState, JdbcValuesSourceProcessingOptions options) { + initializer.resolveInstance( rowProcessingState ); return initializer.getInitializedInstance(); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java index e8d5a5c580..e8fe814ab3 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/spi/ListResultsConsumer.java @@ -62,6 +62,7 @@ public class ListResultsConsumer implements ResultsConsumer, R> { RowProcessingStateStandardImpl rowProcessingState, RowReader rowReader) { final PersistenceContext persistenceContext = session.getPersistenceContext(); + RuntimeException ex = null; try { persistenceContext.getLoadContexts().register( jdbcValuesSourceProcessingState ); @@ -123,11 +124,30 @@ public class ListResultsConsumer implements ResultsConsumer, R> { return results; } - finally { - rowReader.finishUp( jdbcValuesSourceProcessingState ); - jdbcValues.finishUp( session ); - persistenceContext.initializeNonLazyCollections(); + catch (RuntimeException e) { + ex = e; } + finally { + try { + rowReader.finishUp( jdbcValuesSourceProcessingState ); + jdbcValues.finishUp( session ); + persistenceContext.initializeNonLazyCollections(); + } + catch (RuntimeException e) { + if ( ex != null ) { + ex.addSuppressed( e ); + } + else { + ex = e; + } + } + finally { + if ( ex != null ) { + throw ex; + } + } + } + throw new IllegalStateException( "Should not reach this!" ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java index db9a34cd7b..8030da2f51 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/MutableMutabilityPlan.java @@ -16,6 +16,14 @@ import org.hibernate.SharedSessionContract; * @author Steve Ebersole */ public abstract class MutableMutabilityPlan implements MutabilityPlan { + + public static final MutableMutabilityPlan INSTANCE = new MutableMutabilityPlan() { + @Override + protected Object deepCopyNotNull(Object value) { + return value; + } + }; + @Override public boolean isMutable() { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaTypeDescriptor.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaTypeDescriptor.java new file mode 100644 index 0000000000..6b74d3a5e1 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/EntityJavaTypeDescriptor.java @@ -0,0 +1,73 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.type.descriptor.java.spi; + +import org.hibernate.type.descriptor.WrapperOptions; +import org.hibernate.type.descriptor.java.AbstractClassJavaTypeDescriptor; +import org.hibernate.type.descriptor.java.MutabilityPlan; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.JdbcTypeDescriptorIndicators; + +/** + * EntityJavaTypeDescriptor uses object identity for equals/hashCode as we ensure that internally. + * + * @author Christian Beikov + */ +public class EntityJavaTypeDescriptor extends AbstractClassJavaTypeDescriptor { + + public EntityJavaTypeDescriptor(Class type, MutabilityPlan mutabilityPlan) { + super( type, mutabilityPlan ); + } + + @Override + public JdbcType getRecommendedJdbcType(JdbcTypeDescriptorIndicators context) { + throw new JdbcTypeRecommendationException( + "Could not determine recommended JdbcType for `" + getJavaType().getTypeName() + "`" + ); + } + + @Override + public int extractHashCode(T value) { + return System.identityHashCode( value ); + } + + @Override + public boolean areEqual(T one, T another) { + return one == another; + } + + @Override + public String toString(T value) { + return value.toString(); + } + + @Override + public T fromString(CharSequence string) { + throw new UnsupportedOperationException( + "Conversion from String strategy not known for this Java type : " + getJavaType().getTypeName() + ); + } + + @Override + public X unwrap(T value, Class type, WrapperOptions options) { + throw new UnsupportedOperationException( + "Unwrap strategy not known for this Java type : " + getJavaType().getTypeName() + ); + } + + @Override + public T wrap(X value, WrapperOptions options) { + throw new UnsupportedOperationException( + "Wrap strategy not known for this Java type : " + getJavaType().getTypeName() + ); + } + + @Override + public String toString() { + return "EntityJavaTypeDescriptor(" + getJavaType().getTypeName() + ")"; + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java index 5b5b67a585..335ebbba2c 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java +++ b/hibernate-core/src/main/java/org/hibernate/type/descriptor/java/spi/JavaTypeRegistry.java @@ -145,12 +145,8 @@ public class JavaTypeRegistry implements JavaTypeDescriptorBaseline.BaselineTarg return determinedPlan; } - return new MutableMutabilityPlan() { - @Override - protected J deepCopyNotNull(J value) { - return value; - } - }; + //noinspection unchecked + return (MutabilityPlan) MutableMutabilityPlan.INSTANCE; }, typeConfiguration @@ -159,8 +155,16 @@ public class JavaTypeRegistry implements JavaTypeDescriptorBaseline.BaselineTarg ); } - @SuppressWarnings("unchecked") public JavaType resolveManagedTypeDescriptor(Type javaType) { + return resolveManagedTypeDescriptor( javaType, false ); + } + + public JavaType resolveEntityTypeDescriptor(Type javaType) { + return resolveManagedTypeDescriptor( javaType, true ); + } + + @SuppressWarnings("unchecked") + private JavaType resolveManagedTypeDescriptor(Type javaType, boolean entity) { return resolveDescriptor( javaType, () -> { @@ -181,14 +185,10 @@ public class JavaTypeRegistry implements JavaTypeDescriptorBaseline.BaselineTarg mutabilityPlan = determinedPlan; } else { - mutabilityPlan = new MutableMutabilityPlan() { - @Override - protected J deepCopyNotNull(J value) { - return value; - } - }; + mutabilityPlan = (MutabilityPlan) MutableMutabilityPlan.INSTANCE; } - return new JavaTypeDescriptorBasicAdaptor<>( javaTypeClass, mutabilityPlan ); + return entity ? new EntityJavaTypeDescriptor<>( javaTypeClass, mutabilityPlan ) + : new JavaTypeDescriptorBasicAdaptor<>( javaTypeClass, mutabilityPlan ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/EagerKeyManyToOneTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/EagerKeyManyToOneTest.java index 1e1f53fb38..3c3ab830ec 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/EagerKeyManyToOneTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/EagerKeyManyToOneTest.java @@ -69,10 +69,9 @@ public class EagerKeyManyToOneTest { CardField cf = card.getField(); assertSame( card, cf.getPrimaryKey().getCard() ); - statementInspector.assertExecutedCount( 2 ); + statementInspector.assertExecutedCount( 1 ); // Since Key have no additional state, it's not necessary to join their tables statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 2 ); - statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); } catch (StackOverflowError soe) { fail( "eager + key-many-to-one caused stack-overflow in annotations" ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/basic/JoinFetchElementCollectionTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/basic/JoinFetchElementCollectionTest.java index 89397b78ab..8fea03f933 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/collection/basic/JoinFetchElementCollectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/collection/basic/JoinFetchElementCollectionTest.java @@ -11,9 +11,9 @@ import java.util.Set; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.orm.junit.DomainModel; -import org.hibernate.testing.orm.junit.FailureExpected; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -27,11 +27,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue; @SessionFactory public class JoinFetchElementCollectionTest { - @Test - @TestForIssue(jiraKey = "HHH-8206") - @FailureExpected(jiraKey = "HHH-8206", reason = "This is not explicitly supported, however should arguably throw an exception") - public void testJoinFetchesByPath(SessionFactoryScope scope) { - Set emailAddresses = new HashSet(); + private Set emailAddresses; + + @BeforeAll + public void prepareData(SessionFactoryScope scope) { + Set emailAddresses = new HashSet<>(); emailAddresses.add( new EmailAddress( "test1@test.com" ) ); emailAddresses.add( new EmailAddress( "test2@test.com" ) ); emailAddresses.add( new EmailAddress( "test3@test.com" ) ); @@ -49,6 +49,12 @@ public class JoinFetchElementCollectionTest { user = (User) session.merge( user ); } ); + this.emailAddresses = emailAddresses; + } + + @Test + @TestForIssue(jiraKey = "HHH-8206") + public void testJoinFetchesByPath(SessionFactoryScope scope) { // Session 2: Retrieve the user object and check if the sets have the expected values scope.inTransaction( session -> { @@ -67,24 +73,6 @@ public class JoinFetchElementCollectionTest { @Test @TestForIssue(jiraKey = "HHH-5465") public void testJoinFetchElementCollection(SessionFactoryScope scope) { - Set emailAddresses = new HashSet(); - emailAddresses.add( new EmailAddress( "test1@test.com" ) ); - emailAddresses.add( new EmailAddress( "test2@test.com" ) ); - emailAddresses.add( new EmailAddress( "test3@test.com" ) ); - - // Session 1: Insert a user with email addresses but no emailAddresses2 - scope.inTransaction( - session -> { - User user = new User(); - user.setName( "john" ); - Contact contact = new Contact(); - contact.setName( "John Doe" ); - contact.setEmailAddresses( emailAddresses ); - contact = (Contact) session.merge( contact ); - user.setContact( contact ); - user = (User) session.merge( user ); - } - ); // Session 2: Retrieve the user object and check if the sets have the expected values scope.inTransaction( session -> { diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/TreatKeywordTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/TreatKeywordTest.java similarity index 88% rename from hibernate-core/src/test/java/org/hibernate/test/hql/TreatKeywordTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/hql/TreatKeywordTest.java index 9ec8621b64..ae96f1c98a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/hql/TreatKeywordTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/TreatKeywordTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.hql; +package org.hibernate.orm.test.hql; import java.util.List; @@ -13,6 +13,10 @@ import org.hibernate.Session; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.test.hql.Animal; +import org.hibernate.test.hql.Cat; +import org.hibernate.test.hql.Dog; +import org.hibernate.test.hql.Human; import org.junit.Test; import static junit.framework.TestCase.assertEquals; @@ -23,7 +27,7 @@ import static junit.framework.TestCase.assertEquals; * @author Etienne Miret * @author Steve Ebersole * - * @see org.hibernate.test.jpa.ql.TreatKeywordTest + * @see org.hibernate.orm.test.jpa.ql.TreatKeywordTest */ public class TreatKeywordTest extends BaseCoreFunctionalTestCase { diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/TreatJoinTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/TreatJoinTest.java index b247d58282..55bcf0f324 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/TreatJoinTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/TreatJoinTest.java @@ -81,8 +81,9 @@ public class TreatJoinTest extends BaseEntityManagerFunctionalTestCase { CriteriaQuery query = cb.createQuery( Bid.class ); Root bid = query.from( Bid.class ); - cb.treat( bid.join( "item" ), Book.class ); - cb.treat( bid.join( "item" ), Car.class ); + Join item = bid.join( "item" ); + cb.treat( item, Book.class ); + cb.treat( item, Car.class ); query.select( bid ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/DestinationEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/DestinationEntity.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/jpa/ql/DestinationEntity.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/DestinationEntity.java index c134de4dac..11e83c7b35 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/DestinationEntity.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/DestinationEntity.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.jpa.ql; +package org.hibernate.orm.test.jpa.ql; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/FromEntity.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/FromEntity.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/test/jpa/ql/FromEntity.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/FromEntity.java index e519bf1a80..28f57a066a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/FromEntity.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/FromEntity.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.jpa.ql; +package org.hibernate.orm.test.jpa.ql; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/IdentificationVariablesTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/IdentificationVariablesTest.java similarity index 98% rename from hibernate-core/src/test/java/org/hibernate/test/jpa/ql/IdentificationVariablesTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/IdentificationVariablesTest.java index 9fe0cb07a9..9aefd6b64e 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/IdentificationVariablesTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/IdentificationVariablesTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.jpa.ql; +package org.hibernate.orm.test.jpa.ql; import jakarta.persistence.Column; import jakarta.persistence.DiscriminatorColumn; diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/JPAQLComplianceTest.java similarity index 97% rename from hibernate-core/src/test/java/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/JPAQLComplianceTest.java index 5f4732464b..936455e940 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/JPAQLComplianceTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/JPAQLComplianceTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.jpa.ql; +package org.hibernate.orm.test.jpa.ql; import java.util.ArrayList; import java.util.List; @@ -210,7 +210,7 @@ public class JPAQLComplianceTest extends AbstractJPATest { TransactionUtil2.inTransaction( sessionFactory(), - s -> s.createQuery( "from Item" ).list().forEach( result -> s.delete( result ) ) + s -> s.createQuery( "select i from Item i" ).list().forEach( result -> s.delete( result ) ) ); } diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/MapIssueTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/MapIssueTest.java similarity index 82% rename from hibernate-core/src/test/java/org/hibernate/test/jpa/ql/MapIssueTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/MapIssueTest.java index 9e4eed09f9..997b3a0afc 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/MapIssueTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/MapIssueTest.java @@ -4,10 +4,11 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.jpa.ql; +package org.hibernate.orm.test.jpa.ql; import org.hibernate.Session; -import org.hibernate.dialect.PostgreSQL81Dialect; +import org.hibernate.dialect.PostgreSQLDialect; + import org.hibernate.test.jpa.AbstractJPATest; import org.hibernate.test.jpa.MapContent; import org.hibernate.test.jpa.MapOwner; @@ -29,7 +30,7 @@ public class MapIssueTest extends AbstractJPATest { } @Test - @RequiresDialect(value = PostgreSQL81Dialect.class, comment = "Requires support for using a correlated column in a join condition which H2 apparently does not support.") + @RequiresDialect(value = PostgreSQLDialect.class, comment = "Requires support for using a correlated column in a join condition which H2 apparently does not support. For simplicity just run this on PostgreSQL") public void testWhereSubqueryMapKeyIsEntityWhereWithKey() { Session s = openSession(); s.beginTransaction(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/NamedNativeQueryTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/NamedNativeQueryTest.java similarity index 96% rename from hibernate-core/src/test/java/org/hibernate/test/jpa/ql/NamedNativeQueryTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/NamedNativeQueryTest.java index 07faa6574e..73e849e85a 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/NamedNativeQueryTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/NamedNativeQueryTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.jpa.ql; +package org.hibernate.orm.test.jpa.ql; import java.util.ArrayList; import java.util.Collections; @@ -17,6 +17,7 @@ import org.hibernate.dialect.SQLServerDialect; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.After; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -59,6 +60,16 @@ public class NamedNativeQueryTest extends BaseCoreFunctionalTestCase { return list; } + @After + public void cleanup() { + Session session = openSession(); + session.getTransaction().begin(); + session.createQuery( "delete DestinationEntity" ).executeUpdate(); + session.createQuery( "delete FromEntity" ).executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + @Test public void testSingleSelect() { final String name = "Name"; diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/TreatKeywordTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/TreatKeywordTest.java similarity index 99% rename from hibernate-core/src/test/java/org/hibernate/test/jpa/ql/TreatKeywordTest.java rename to hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/TreatKeywordTest.java index a67df968b4..b030b89a08 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/ql/TreatKeywordTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/ql/TreatKeywordTest.java @@ -4,7 +4,7 @@ * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or . */ -package org.hibernate.test.jpa.ql; +package org.hibernate.orm.test.jpa.ql; import jakarta.persistence.DiscriminatorColumn; import jakarta.persistence.DiscriminatorType; @@ -147,7 +147,7 @@ public class TreatKeywordTest extends BaseCoreFunctionalTestCase { @Test @TestForIssue( jiraKey = "HHH-9862" ) - @FailureExpected( jiraKey = "HHH-9862" ) +// @FailureExpected( jiraKey = "HHH-9862" ) public void testRestrictionsOnJoinedSubclasses() { Session s = openSession(); s.beginTransaction(); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/querycache/AbstractQueryCacheResultTransformerTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/querycache/AbstractQueryCacheResultTransformerTest.java index 37432e7296..ceeae5762b 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/querycache/AbstractQueryCacheResultTransformerTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/querycache/AbstractQueryCacheResultTransformerTest.java @@ -6,6 +6,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.ListJoin; import jakarta.persistence.criteria.MapJoin; @@ -746,11 +747,11 @@ public abstract class AbstractQueryCacheResultTransformerTest { CriteriaBuilder builder = s.getCriteriaBuilder(); JpaCriteriaQuery criteria = (JpaCriteriaQuery) builder.createQuery( Object[].class ); JpaRoot root = criteria.from( Student.class ); - root.join( "preferredCourse", JoinType.LEFT ); + Join preferredCourse = root.join( "preferredCourse", JoinType.LEFT ); root.fetch( "enrolments", JoinType.LEFT ); criteria.orderBy( builder.asc( root.get( "studentNumber" ) ) ); - criteria.multiselect( root, root.get( "preferredCourse" ) ); + criteria.multiselect( root, preferredCourse ); return criteria; } }; @@ -759,7 +760,7 @@ public abstract class AbstractQueryCacheResultTransformerTest { @Override public Query getQuery(Session s) { return s.createQuery( - "select s, s.preferredCourse from Student s left join fetch s.enrolments left join s.preferredCourse order by s.studentNumber" + "select s, pc from Student s left join fetch s.enrolments left join s.preferredCourse pc order by s.studentNumber" ); } }; @@ -1506,22 +1507,15 @@ public abstract class AbstractQueryCacheResultTransformerTest { JpaCriteriaQuery criteria = (JpaCriteriaQuery) builder.createQuery( Object[].class ); Root root = criteria.from( Student.class ); - root.join( "preferredCourse", JoinType.LEFT ); + Join preferredCourse = root.join( "preferredCourse", JoinType.LEFT ); - criteria.multiselect( root, root.get( "preferredCourse" ) ); + criteria.multiselect( root, preferredCourse ); criteria.orderBy( builder.asc( root.get( "studentNumber" ) ) ); return criteria; } }; - HqlExecutor hqlExecutorUnaliased = new HqlExecutor() { - @Override - protected Query getQuery(Session s) { - return s.createQuery( - "select s, s.preferredCourse from Student s left join s.preferredCourse order by s.studentNumber" ); - } - }; HqlExecutor hqlExecutorAliased = new HqlExecutor() { @Override protected Query getQuery(Session s) { @@ -1539,8 +1533,7 @@ public abstract class AbstractQueryCacheResultTransformerTest { assertEquals( shermanExpected, shermanObjects[0] ); assertNull( shermanObjects[1] ); }; - runTest( hqlExecutorUnaliased, criteriaExecutor, checker, false, scope ); - runTest( hqlExecutorAliased, null, checker, false, scope ); + runTest( hqlExecutorAliased, criteriaExecutor, checker, false, scope ); } @Test diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java b/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java index 34acfeff9e..6896eb9e92 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/function/OrderByFragmentFunction.java @@ -141,11 +141,12 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { + boolean allowFkOptimization, + boolean resolve) { if ( tableExpression.equals( normalTableExpression ) ) { tableExpression = auditTableExpression; } - return delegate.getTableReference( navigablePath, tableExpression, allowFkOptimization ); + return delegate.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); } @Override