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 681aa45376..74e89a7842 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( null, table, true, true ); + final TableReference tableReference = tableGroup.getTableReference( null, table, true ); return tableReference == null ? null : tableReference.getIdentificationVariable(); } 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 d6cf57370c..3ff4da72e5 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 @@ -291,4 +291,8 @@ public abstract class AbstractCompositeIdentifierMapping return false; } + @Override + public boolean containsTableReference(String tableExpression) { + return entityMapping.containsTableReference( tableExpression ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java index 963928a345..88f7eef5e3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/AttributeMapping.java @@ -21,7 +21,7 @@ import org.hibernate.type.descriptor.java.MutabilityPlanExposer; * @author Steve Ebersole */ public interface AttributeMapping - extends ValuedModelPart, Fetchable, DatabaseSnapshotContributor, PropertyBasedMapping, MutabilityPlanExposer { + extends OwnedValuedModelPart, Fetchable, DatabaseSnapshotContributor, PropertyBasedMapping, MutabilityPlanExposer { /** * The name of the mapped attribute */ diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicEntityIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicEntityIdentifierMapping.java index 58b06c4b6b..561cb3ec80 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicEntityIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/BasicEntityIdentifierMapping.java @@ -18,4 +18,11 @@ public interface BasicEntityIdentifierMapping extends SingleAttributeIdentifierM default int getFetchableKey() { return -1; } + + @Override + boolean isNullable(); + + @Override + boolean isInsertable(); + } 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 ede271a0d4..fe0e8a4aed 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 @@ -10,6 +10,7 @@ import java.util.function.IntFunction; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; +import org.hibernate.metamodel.mapping.internal.VirtualIdEmbeddable; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -42,7 +43,9 @@ public interface ForeignKeyDescriptor extends VirtualModelPart, ValuedModelPart ValuedModelPart getTargetPart(); - default ModelPart getPart(Nature nature) { + boolean isKeyPart(ValuedModelPart modelPart); + + default ValuedModelPart getPart(Nature nature) { if ( nature == Nature.KEY ) { return getKeyPart(); } @@ -76,27 +79,45 @@ public interface ForeignKeyDescriptor extends VirtualModelPart, ValuedModelPart /** * Create a DomainResult for the referring-side of the fk + * The table group must be the one containing the target. */ DomainResult createKeyDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup targetTableGroup, + FetchParent fetchParent, + DomainResultCreationState creationState); + + /** + * Create a DomainResult for the referring-side of the fk + * The table group must be the one containing the target. + * The {@link Nature} is the association side of the foreign key i.e. {@link Association#getSideNature()}. + */ + DomainResult createKeyDomainResult( + NavigablePath navigablePath, + TableGroup targetTableGroup, + Nature fromSide, FetchParent fetchParent, DomainResultCreationState creationState); /** * Create a DomainResult for the target-side of the fk + * The table group must be the one containing the target */ DomainResult createTargetDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup targetTableGroup, FetchParent fetchParent, DomainResultCreationState creationState); - DomainResult createDomainResult( + /** + * Create a DomainResult for the referring-side of the fk + * The table group must be the one containing the target. + */ + @Override + DomainResult createDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, - Nature side, - FetchParent fetchParent, + TableGroup targetTableGroup, + String resultVariable, DomainResultCreationState creationState); Predicate generateJoinPredicate( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/OwnedValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/OwnedValuedModelPart.java new file mode 100644 index 0000000000..9858f291cf --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/OwnedValuedModelPart.java @@ -0,0 +1,14 @@ +/* + * 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.metamodel.mapping; + +/** + * Marker interface for valued model parts that have a declaring/owner type. + */ +public interface OwnedValuedModelPart extends ValuedModelPart { + MappingType getDeclaringType(); +} diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java index 1c5bdad280..aba430044e 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/PluralAttributeMapping.java @@ -121,7 +121,7 @@ public interface PluralAttributeMapping @Override default DomainResult createSnapshotDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup parentTableGroup, String resultVariable, DomainResultCreationState creationState) { return new BasicResult( 0, null, getJavaType() ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java index 31753c4852..a8f849be24 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractDomainPath.java @@ -63,7 +63,11 @@ public abstract class AbstractDomainPath implements DomainPath { SqlAstCreationState creationState) { if ( referenceModelPart instanceof BasicValuedModelPart ) { final BasicValuedModelPart selection = (BasicValuedModelPart) referenceModelPart; - final TableReference tableReference = tableGroup.resolveTableReference( getNavigablePath(), selection.getContainingTableExpression() ); + final TableReference tableReference = tableGroup.resolveTableReference( + getNavigablePath(), + selection, + selection.getContainingTableExpression() + ); return creationState.getSqlExpressionResolver().resolveSqlExpression( createColumnReferenceKey( tableReference, selection.getSelectionExpression() ), processingState -> new ColumnReference( diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEntityCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEntityCollectionPart.java index 2f7b85ca8c..d1f93ac6a2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEntityCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/AbstractEntityCollectionPart.java @@ -308,7 +308,7 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa @Override public boolean containsTableReference(String tableExpression) { - return getCollectionDescriptor().getAttributeMapping().containsTableReference( tableExpression ); + return getAssociatedEntityMappingType().containsTableReference( tableExpression ); } public TableGroup createTableGroupInternal( @@ -319,7 +319,6 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa final SqlAliasBase sqlAliasBase, SqlAstCreationState creationState) { final SqlAstCreationContext creationContext = creationState.getCreationContext(); - final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver(); final TableReference primaryTableReference = getEntityMappingType().createPrimaryTableReference( sqlAliasBase, creationState @@ -389,7 +388,7 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa final CompositeType compositeType; if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded() && compositeType.getPropertyNames().length == 1 ) { - ToOneAttributeMapping.addPrefixedPropertyNames( + ToOneAttributeMapping.addPrefixedPropertyPaths( targetKeyPropertyNames, compositeType.getPropertyNames()[0], compositeType.getSubtypes()[0], @@ -397,39 +396,27 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa ); ToOneAttributeMapping.addPrefixedPropertyNames( targetKeyPropertyNames, - ForeignKeyDescriptor.PART_NAME, - compositeType.getSubtypes()[0], + EntityIdentifierMapping.ROLE_LOCAL_NAME, + propertyType, creationProcess.getCreationContext().getSessionFactory() ); } else { - ToOneAttributeMapping.addPrefixedPropertyNames( + ToOneAttributeMapping.addPrefixedPropertyPaths( targetKeyPropertyNames, null, propertyType, creationProcess.getCreationContext().getSessionFactory() ); - ToOneAttributeMapping.addPrefixedPropertyNames( - targetKeyPropertyNames, - ForeignKeyDescriptor.PART_NAME, - propertyType, - creationProcess.getCreationContext().getSessionFactory() - ); } } else { - ToOneAttributeMapping.addPrefixedPropertyNames( + ToOneAttributeMapping.addPrefixedPropertyPaths( targetKeyPropertyNames, entityBinding.getIdentifierProperty().getName(), propertyType, creationProcess.getCreationContext().getSessionFactory() ); - ToOneAttributeMapping.addPrefixedPropertyNames( - targetKeyPropertyNames, - ForeignKeyDescriptor.PART_NAME, - propertyType, - creationProcess.getCreationContext().getSessionFactory() - ); } return targetKeyPropertyNames; } @@ -442,18 +429,12 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa // todo (PropertyMapping) : the problem here is timing. this needs to be delayed. final Type propertyType = ( (PropertyMapping) elementTypeDescriptor.getEntityPersister() ) .toType( referencedPropertyName ); - ToOneAttributeMapping.addPrefixedPropertyNames( + ToOneAttributeMapping.addPrefixedPropertyPaths( targetKeyPropertyNames, referencedPropertyName, propertyType, creationProcess.getCreationContext().getSessionFactory() ); - ToOneAttributeMapping.addPrefixedPropertyNames( - targetKeyPropertyNames, - ForeignKeyDescriptor.PART_NAME, - propertyType, - creationProcess.getCreationContext().getSessionFactory() - ); return targetKeyPropertyNames; } else { @@ -462,7 +443,7 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded() && compositeType.getPropertyNames().length == 1 ) { final Set targetKeyPropertyNames = new HashSet<>( 2 ); - ToOneAttributeMapping.addPrefixedPropertyNames( + ToOneAttributeMapping.addPrefixedPropertyPaths( targetKeyPropertyNames, compositeType.getPropertyNames()[0], compositeType.getSubtypes()[0], @@ -470,34 +451,34 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa ); ToOneAttributeMapping.addPrefixedPropertyNames( targetKeyPropertyNames, - ForeignKeyDescriptor.PART_NAME, - compositeType.getSubtypes()[0], + EntityIdentifierMapping.ROLE_LOCAL_NAME, + propertyType, creationProcess.getCreationContext().getSessionFactory() ); return targetKeyPropertyNames; } else { + final Set targetKeyPropertyNames = new HashSet<>( 2 ); + targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME ); + targetKeyPropertyNames.add( referencedPropertyName ); final String mapsIdAttributeName; if ( ( mapsIdAttributeName = ToOneAttributeMapping.findMapsIdPropertyName( elementTypeDescriptor, referencedPropertyName ) ) != null ) { - final Set targetKeyPropertyNames = new HashSet<>( 2 ); - targetKeyPropertyNames.add( referencedPropertyName ); - ToOneAttributeMapping.addPrefixedPropertyNames( + ToOneAttributeMapping.addPrefixedPropertyPaths( targetKeyPropertyNames, mapsIdAttributeName, elementTypeDescriptor.getEntityPersister().getIdentifierType(), creationProcess.getCreationContext().getSessionFactory() ); - ToOneAttributeMapping.addPrefixedPropertyNames( - targetKeyPropertyNames, - ForeignKeyDescriptor.PART_NAME, - elementTypeDescriptor.getEntityPersister().getIdentifierType(), - creationProcess.getCreationContext().getSessionFactory() - ); - return targetKeyPropertyNames; } else { - return Set.of( referencedPropertyName, ForeignKeyDescriptor.PART_NAME ); + ToOneAttributeMapping.addPrefixedPropertyPaths( + targetKeyPropertyNames, + null, + propertyType, + creationProcess.getCreationContext().getSessionFactory() + ); } + return targetKeyPropertyNames; } } } 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 873e164942..40ee2a194d 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 @@ -6,7 +6,6 @@ */ package org.hibernate.metamodel.mapping.internal; -import java.io.Serializable; import java.util.function.BiConsumer; import org.hibernate.engine.FetchStyle; @@ -147,7 +146,15 @@ public class BasicAttributeMapping if ( original instanceof SingleAttributeIdentifierMapping ) { final SingleAttributeIdentifierMapping mapping = (SingleAttributeIdentifierMapping) original; attributeName = mapping.getAttributeName(); - attributeMetadata = null; + attributeMetadata = new SimpleAttributeMetadata( + propertyAccess, + mapping.getExpressibleJavaType().getMutabilityPlan(), + selectableMapping.isNullable(), + insertable, + updateable, + false, + true + ); } else if ( original instanceof SingularAttributeMapping ) { final SingularAttributeMapping mapping = (SingularAttributeMapping) original; @@ -297,7 +304,7 @@ public class BasicAttributeMapping TableGroup tableGroup, String resultVariable, DomainResultCreationState creationState) { - final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); + final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, null, creationState ); //noinspection unchecked return new BasicResult( @@ -311,14 +318,13 @@ public class BasicAttributeMapping private SqlSelection resolveSqlSelection( NavigablePath navigablePath, TableGroup tableGroup, - @SuppressWarnings("SameParameterValue") boolean allowFkOptimization, FetchParent fetchParent, DomainResultCreationState creationState) { final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver(); final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, - getContainingTableExpression(), - allowFkOptimization + this, + getContainingTableExpression() ); return expressionResolver.resolveSqlSelection( @@ -337,7 +343,7 @@ public class BasicAttributeMapping NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { - resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); + resolveSqlSelection( navigablePath, tableGroup, null, creationState ); } @Override @@ -346,7 +352,7 @@ public class BasicAttributeMapping TableGroup tableGroup, DomainResultCreationState creationState, BiConsumer selectionConsumer) { - selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ), getJdbcMapping() ); + selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, null, creationState ), getJdbcMapping() ); } @Override @@ -375,7 +381,12 @@ public class BasicAttributeMapping assert tableGroup != null; - final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, true, fetchParent, creationState ); + final SqlSelection sqlSelection = resolveSqlSelection( + fetchablePath, + tableGroup, + fetchParent, + creationState + ); valuesArrayPosition = sqlSelection.getValuesArrayPosition(); if ( sqlSelection.getExpressionType() != null) { // if the expression type is different that the expected type coerce the value 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 2a48ac877a..87e6d551dc 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 @@ -225,7 +225,7 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa TableGroup tableGroup, String resultVariable, DomainResultCreationState creationState) { - final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); + final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, null, creationState ); return new BasicResult<>( sqlSelection.getValuesArrayPosition(), @@ -240,7 +240,7 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { - resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); + resolveSqlSelection( navigablePath, tableGroup, null, creationState ); } @Override @@ -250,7 +250,7 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa DomainResultCreationState creationState, BiConsumer selectionConsumer) { selectionConsumer.accept( - resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ), + resolveSqlSelection( navigablePath, tableGroup, null, creationState ), getJdbcMapping() ); } @@ -258,14 +258,13 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa private SqlSelection resolveSqlSelection( NavigablePath navigablePath, TableGroup tableGroup, - boolean allowFkOptimization, FetchParent fetchParent, DomainResultCreationState creationState) { final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState() .getSqlExpressionResolver(); final TableReference rootTableReference; try { - rootTableReference = tableGroup.resolveTableReference( navigablePath, rootTable, allowFkOptimization ); + rootTableReference = tableGroup.resolveTableReference( navigablePath, rootTable ); } catch (Exception e) { throw new IllegalStateException( @@ -315,12 +314,12 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa @Override public boolean isInsertable() { - return updateable; + return insertable; } @Override public boolean isUpdateable() { - return insertable; + return updateable; } @Override @@ -414,7 +413,7 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa assert tableGroup != null; - final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, false, fetchParent, creationState ); + final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, fetchParent, creationState ); final JdbcMappingContainer selectionType = sqlSelection.getExpressionType(); return new BasicFetch<>( sqlSelection.getValuesArrayPosition(), 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 8a354b0f2f..8b246bf169 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 @@ -6,7 +6,6 @@ */ package org.hibernate.metamodel.mapping.internal; -import java.io.Serializable; import java.util.Collections; import java.util.List; import java.util.function.BiConsumer; @@ -160,7 +159,7 @@ public class BasicValuedCollectionPart TableGroup tableGroup, String resultVariable, DomainResultCreationState creationState) { - final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); + final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, null, creationState ); return new BasicResult<>( sqlSelection.getValuesArrayPosition(), @@ -173,7 +172,6 @@ public class BasicValuedCollectionPart private SqlSelection resolveSqlSelection( NavigablePath navigablePath, TableGroup tableGroup, - boolean allowFkOptimization, FetchParent fetchParent, DomainResultCreationState creationState) { final SqlExpressionResolver exprResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver(); @@ -189,8 +187,7 @@ public class BasicValuedCollectionPart } final TableReference tableReference = targetTableGroup.resolveTableReference( navigablePath, - getContainingTableExpression(), - allowFkOptimization + getContainingTableExpression() ); return exprResolver.resolveSqlSelection( exprResolver.resolveSqlExpression( @@ -206,7 +203,7 @@ public class BasicValuedCollectionPart @Override public void applySqlSelections( NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { - resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); + resolveSqlSelection( navigablePath, tableGroup, null, creationState ); } @Override @@ -215,7 +212,7 @@ public class BasicValuedCollectionPart TableGroup tableGroup, DomainResultCreationState creationState, BiConsumer selectionConsumer) { - selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ), getJdbcMapping() ); + selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, null, creationState ), getJdbcMapping() ); } @Override @@ -272,7 +269,7 @@ public class BasicValuedCollectionPart final TableGroup tableGroup = creationState.getSqlAstCreationState() .getFromClauseAccess() .findTableGroup( parentNavigablePath ); - final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, true, fetchParent, creationState ); + final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, fetchParent, creationState ); return new BasicFetch<>( sqlSelection.getValuesArrayPosition(), 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 bcfd3649d0..f9ac2ea1d0 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 @@ -7,7 +7,6 @@ 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; @@ -95,7 +94,6 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator (tableName, tableDiscriminatorDetails) -> tableGroup.getTableReference( fetchablePath, tableName, - false, true ) ); @@ -244,7 +242,6 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator final TableReference tableReference = entityTableGroup.getTableReference( entityTableGroup.getNavigablePath(), tableName, - false, false ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddableMappingTypeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddableMappingTypeImpl.java index 1ac1360c4a..bbf7791418 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddableMappingTypeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/EmbeddableMappingTypeImpl.java @@ -266,7 +266,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme selectableMappings, inverseMappingType, creationProcess, - valueMapping.getDeclaringType(), + this, attributeMappings ) ); 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 28509100ec..c5339b650e 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 @@ -298,12 +298,12 @@ public class EmbeddedAttributeMapping } final List columnReferences = CollectionHelper.arrayList( embeddableMappingType.getJdbcTypeCount() ); final NavigablePath navigablePath = tableGroup.getNavigablePath().append( getNavigableRole().getNavigableName() ); - final TableReference defaultTableReference = tableGroup.resolveTableReference( navigablePath, getContainingTableExpression() ); + final TableReference defaultTableReference = tableGroup.resolveTableReference( navigablePath, this, getContainingTableExpression() ); getEmbeddableTypeDescriptor().forEachSelectable( (columnIndex, selection) -> { final TableReference tableReference = getContainingTableExpression().equals( selection.getContainingTableExpression() ) ? defaultTableReference - : tableGroup.resolveTableReference( navigablePath, selection.getContainingTableExpression() ); + : tableGroup.resolveTableReference( navigablePath, this, selection.getContainingTableExpression() ); final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression( tableReference, selection @@ -394,4 +394,17 @@ public class EmbeddedAttributeMapping public boolean isSelectable() { return selectable; } + + @Override + public boolean containsTableReference(String tableExpression) { + final ManagedMappingType declaringType = getDeclaringType(); + final TableGroupProducer producer; + if ( declaringType instanceof TableGroupProducer ) { + producer = (TableGroupProducer) declaringType; + } + else { + producer = ( (EmbeddableMappingType) declaringType ).getEmbeddedValueMapping(); + } + return producer.containsTableReference( tableExpression ); + } } 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 4308bdab97..61ebf724eb 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 @@ -41,6 +41,7 @@ import org.hibernate.sql.ast.tree.from.PluralTableGroup; import org.hibernate.sql.ast.tree.from.StandardVirtualTableGroup; 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; @@ -207,6 +208,7 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF final TableReference tableReference = tableGroup.resolveTableReference( tableGroup.getNavigablePath() .append( getNavigableRole().getNavigableName() ), + this, selection.getContainingTableExpression() ); expressions.add( @@ -322,4 +324,13 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF return FetchTiming.IMMEDIATE; } + @Override + public boolean containsTableReference(String tableExpression) { + if ( collectionDescriptor.isOneToMany() ) { + return ( (EntityCollectionPart) collectionDescriptor.getAttributeMapping().getElementDescriptor() ) + .getPartMappingType().containsTableReference( tableExpression ); + } + return collectionDescriptor.getAttributeMapping().containsTableReference( tableExpression ); + } + } 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 3b1eb6b849..0d64d7a7dd 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 @@ -16,6 +16,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.internal.util.MutableInteger; import org.hibernate.metamodel.mapping.AssociationKey; +import org.hibernate.metamodel.mapping.AttributeMappingsList; import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EntityMappingType; @@ -24,9 +25,11 @@ 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.NonAggregatedIdentifierMapping; import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMappings; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.spi.NavigablePath; @@ -35,10 +38,12 @@ import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.Expression; +import org.hibernate.sql.ast.tree.from.OneToManyTableGroup; 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.from.VirtualTableGroup; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.predicate.Predicate; @@ -171,6 +176,27 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { return targetSide.getModelPart().getEmbeddableTypeDescriptor().getEmbeddedValueMapping(); } + @Override + public boolean isKeyPart(ValuedModelPart modelPart) { + final EmbeddableValuedModelPart keyPart = getKeyPart(); + if ( this == modelPart || keyPart == modelPart ) { + return true; + } + else if ( keyPart instanceof NonAggregatedIdentifierMapping ) { + final AttributeMappingsList attributeMappings = ( (NonAggregatedIdentifierMapping) keyPart ).getVirtualIdEmbeddable() + .getAttributeMappings(); + for ( int i = 0; i < attributeMappings.size(); i++ ) { + if ( modelPart == attributeMappings.get( i ) ) { + return true; + } + } + } + else if ( keyPart.isVirtual() && keyPart.getNumberOfFetchables() == 1 ) { + return keyPart.getFetchable( 0 ) == modelPart; + } + return false; + } + @Override public Side getKeySide() { return keySide; @@ -209,15 +235,35 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { @Override public DomainResult createKeyDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup targetTableGroup, FetchParent fetchParent, DomainResultCreationState creationState) { + assert isTargetTableGroup( targetTableGroup ); return createDomainResult( navigablePath, - tableGroup, + targetTableGroup, null, - keyTable, - keySide.getModelPart(), + Nature.KEY, + fetchParent, + creationState + ); + } + + @Override + public DomainResult createKeyDomainResult( + NavigablePath navigablePath, + TableGroup targetTableGroup, + Nature fromSide, + FetchParent fetchParent, + DomainResultCreationState creationState) { + assert fromSide == Nature.TARGET + ? targetTableGroup.getTableReference( navigablePath, associationKey.getTable(), false ) != null + : isTargetTableGroup( targetTableGroup ); + return createDomainResult( + navigablePath.append( ForeignKeyDescriptor.PART_NAME ), + targetTableGroup, + null, + Nature.KEY, fetchParent, creationState ); @@ -226,70 +272,57 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { @Override public DomainResult createTargetDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup targetTableGroup, FetchParent fetchParent, DomainResultCreationState creationState) { - assert tableGroup.getTableReference( navigablePath, targetTable ) != null; - + assert isTargetTableGroup( targetTableGroup ); return createDomainResult( navigablePath, - tableGroup, + targetTableGroup, null, - targetTable, - targetSide.getModelPart(), + Nature.TARGET, fetchParent, creationState ); } - @Override - public DomainResult createDomainResult( - NavigablePath navigablePath, - TableGroup tableGroup, - Nature side, - FetchParent fetchParent, - DomainResultCreationState creationState) { - if ( side == Nature.KEY ) { - return createDomainResult( - navigablePath, - tableGroup, - null, - keyTable, - keySide.getModelPart(), - fetchParent, - creationState - ); - } - else { - return createDomainResult( - navigablePath, - tableGroup, - null, - targetTable, - targetSide.getModelPart(), - fetchParent, - creationState - ); - } - } - @Override public DomainResult createDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup targetTableGroup, String resultVariable, DomainResultCreationState creationState) { + assert isTargetTableGroup( targetTableGroup ); return createDomainResult( navigablePath, - tableGroup, + targetTableGroup, resultVariable, - keyTable, - keySide.getModelPart(), + Nature.KEY, null, creationState ); } + private boolean isTargetTableGroup(TableGroup tableGroup) { + tableGroup = getUnderlyingTableGroup( tableGroup ); + final TableGroupProducer tableGroupProducer; + if ( tableGroup instanceof OneToManyTableGroup ) { + tableGroupProducer = (TableGroupProducer) ( (OneToManyTableGroup) tableGroup ).getElementTableGroup() + .getModelPart(); + } + else { + tableGroupProducer = (TableGroupProducer) tableGroup.getModelPart(); + } + return tableGroupProducer.containsTableReference( targetSide.getModelPart().getContainingTableExpression() ); + } + + private static TableGroup getUnderlyingTableGroup(TableGroup tableGroup) { + if ( tableGroup instanceof VirtualTableGroup ) { + tableGroup = getUnderlyingTableGroup( ( (VirtualTableGroup) tableGroup ).getUnderlyingTableGroup() ); + } + return tableGroup; + } + @Override public void applySqlSelections( NavigablePath navigablePath, @@ -311,15 +344,17 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { NavigablePath navigablePath, TableGroup tableGroup, String resultVariable, - String columnContainingTable, - EmbeddableValuedModelPart modelPart, + Nature nature, FetchParent fetchParent, DomainResultCreationState creationState) { + final EmbeddableValuedModelPart modelPart; final NavigablePath resultNavigablePath; - if ( modelPart == keySide.getModelPart() ) { + if ( nature == Nature.KEY ) { + modelPart = keySide.getModelPart(); resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.PART_NAME ); } else { + modelPart = targetSide.getModelPart(); resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.TARGET_PART_NAME ); } @@ -343,7 +378,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { final Nature currentForeignKeyResolvingKey = creationState.getCurrentlyResolvingForeignKeyPart(); try { - creationState.setCurrentlyResolvingForeignKeyPart( keySide.getModelPart() == modelPart ? Nature.KEY : Nature.TARGET ); + creationState.setCurrentlyResolvingForeignKeyPart( nature ); return new EmbeddableForeignKeyResultImpl<>( resultNavigablePath, modelPart, @@ -364,13 +399,11 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor { SqlAstCreationState creationState) { final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference( targetSideTableGroup.getNavigablePath(), - targetTable, - false + targetTable ); final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference( null, - keyTable, - false + keyTable ); return generateJoinPredicate( lhsTableReference, rhsTableKeyReference, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java index 9c440831d8..5c79f359eb 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/IdClassEmbeddable.java @@ -139,7 +139,7 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden selectableMappings, inverseMappingType, creationProcess, - valueMapping.getDeclaringType(), + this, this.attributeMappings ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ManyToManyCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ManyToManyCollectionPart.java index a7e318508e..84db4a81d2 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ManyToManyCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ManyToManyCollectionPart.java @@ -47,6 +47,7 @@ import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.tree.from.LazyTableGroup; 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.type.EntityType; @@ -74,7 +75,8 @@ import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelpe * * @author Steve Ebersole */ -public class ManyToManyCollectionPart extends AbstractEntityCollectionPart implements EntityAssociationMapping { +public class ManyToManyCollectionPart extends AbstractEntityCollectionPart implements EntityAssociationMapping, + LazyTableGroup.ParentTableGroupUseChecker { private ForeignKeyDescriptor foreignKey; private ValuedModelPart fkTargetModelPart; @@ -292,24 +294,7 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple sqlAliasBase, creationState ), - (np, tableExpression) -> { - if ( ! foreignKey.getKeyTable().equals( tableExpression ) ) { - return false; - } - - if ( navigablePath.equals( np.getParent() ) ) { - return getTargetKeyPropertyNames().contains( np.getLocalName() ); - } - - final String relativePath = np.relativize( navigablePath ); - if ( relativePath == null ) { - return false; - } - - // Empty relative path means the navigable paths are equal, - // in which case we allow resolving the parent table group - return relativePath.isEmpty() || getTargetKeyPropertyNames().contains( relativePath ); - }, + this, this, explicitSourceAlias, sqlAliasBase, @@ -337,6 +322,11 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple return lazyTableGroup; } + @Override + public boolean canUseParentTableGroup(TableGroupProducer producer, NavigablePath navigablePath, ValuedModelPart valuedModelPart) { + return foreignKey.isKeyPart( valuedModelPart ); + } + @Override public boolean hasPartitionedSelectionMapping() { return foreignKey.hasPartitionedSelectionMapping(); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/OneToManyCollectionPart.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/OneToManyCollectionPart.java index e3efd1ad27..d68672cd95 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/OneToManyCollectionPart.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/OneToManyCollectionPart.java @@ -130,6 +130,11 @@ public class OneToManyCollectionPart extends AbstractEntityCollectionPart implem return false; } + @Override + public boolean containsTableReference(String tableExpression) { + return getAssociatedEntityMappingType().containsTableReference( tableExpression ); + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // TableGroupJoinProducer 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 05cea13674..1736951177 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 @@ -31,6 +31,7 @@ import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.PropertyBasedMapping; import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableMapping; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.proxy.HibernateProxy; @@ -42,10 +43,12 @@ 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.expression.Expression; +import org.hibernate.sql.ast.tree.from.OneToManyTableGroup; 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.UnknownTableReferenceException; +import org.hibernate.sql.ast.tree.from.VirtualTableGroup; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.results.graph.DomainResult; @@ -179,6 +182,11 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa return targetSide.getModelPart(); } + @Override + public boolean isKeyPart(ValuedModelPart modelPart) { + return this == modelPart || keySide.getModelPart() == modelPart; + } + @Override public Side getKeySide() { return keySide; @@ -217,12 +225,32 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa @Override public DomainResult createKeyDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup targetTableGroup, FetchParent fetchParent, DomainResultCreationState creationState) { + assert isTargetTableGroup( targetTableGroup ); return createDomainResult( - navigablePath, - tableGroup, + navigablePath.append( ForeignKeyDescriptor.PART_NAME ), + targetTableGroup, + keySide.getModelPart(), + fetchParent, + creationState + ); + } + + @Override + public DomainResult createKeyDomainResult( + NavigablePath navigablePath, + TableGroup targetTableGroup, + Nature fromSide, + FetchParent fetchParent, + DomainResultCreationState creationState) { + assert fromSide == Nature.TARGET + ? targetTableGroup.getTableReference( navigablePath, associationKey.getTable(), false ) != null + : isTargetTableGroup( targetTableGroup ); + return createDomainResult( + navigablePath.append( ForeignKeyDescriptor.PART_NAME ), + targetTableGroup, keySide.getModelPart(), fetchParent, creationState @@ -232,39 +260,53 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa @Override public DomainResult createTargetDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup targetTableGroup, FetchParent fetchParent, DomainResultCreationState creationState) { + assert isTargetTableGroup( targetTableGroup ); return createDomainResult( - navigablePath, - tableGroup, + navigablePath.append( ForeignKeyDescriptor.TARGET_PART_NAME ), + targetTableGroup, targetSide.getModelPart(), fetchParent, creationState ); } - @Override - public DomainResult createDomainResult( - NavigablePath navigablePath, - TableGroup tableGroup, - Nature side, - FetchParent fetchParent, DomainResultCreationState creationState) { - if ( side == Nature.KEY ) { - return createDomainResult( navigablePath, tableGroup, keySide.getModelPart(), fetchParent, creationState ); - } - else { - return createDomainResult( navigablePath, tableGroup, targetSide.getModelPart(), fetchParent, creationState ); - } - } - @Override public DomainResult createDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup targetTableGroup, String resultVariable, DomainResultCreationState creationState) { - return createDomainResult( navigablePath, tableGroup, keySide.getModelPart(), null, creationState ); + assert isTargetTableGroup( targetTableGroup ); + return createDomainResult( + navigablePath.append( ForeignKeyDescriptor.PART_NAME ), + targetTableGroup, + keySide.getModelPart(), + null, + creationState + ); + } + + private boolean isTargetTableGroup(TableGroup tableGroup) { + tableGroup = getUnderlyingTableGroup( tableGroup ); + final TableGroupProducer tableGroupProducer; + if ( tableGroup instanceof OneToManyTableGroup ) { + tableGroupProducer = (TableGroupProducer) ( (OneToManyTableGroup) tableGroup ).getElementTableGroup() + .getModelPart(); + } + else { + tableGroupProducer = (TableGroupProducer) tableGroup.getModelPart(); + } + return tableGroupProducer.containsTableReference( targetSide.getModelPart().getContainingTableExpression() ); + } + + private static TableGroup getUnderlyingTableGroup(TableGroup tableGroup) { + if ( tableGroup instanceof VirtualTableGroup ) { + tableGroup = getUnderlyingTableGroup( ( (VirtualTableGroup) tableGroup ).getUnderlyingTableGroup() ); + } + return tableGroup; } @Override @@ -287,23 +329,16 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa private DomainResult createDomainResult( NavigablePath navigablePath, TableGroup tableGroup, - SelectableMapping selectableMapping, + BasicValuedModelPart selectableMapping, FetchParent fetchParent, DomainResultCreationState creationState) { final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); - - final NavigablePath resultNavigablePath; - if ( selectableMapping == keySide.getModelPart() ) { - resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.PART_NAME ); - } - else { - resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.TARGET_PART_NAME ); - } final TableReference tableReference; try { tableReference = tableGroup.resolveTableReference( - resultNavigablePath, + navigablePath, + selectableMapping, selectableMapping.getContainingTableExpression() ); } @@ -356,13 +391,11 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa SqlAstCreationState creationState) { final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference( targetSideTableGroup.getNavigablePath(), - targetSide.getModelPart().getContainingTableExpression(), - false + targetSide.getModelPart().getContainingTableExpression() ); final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference( null, - keySide.getModelPart().getContainingTableExpression(), - false + keySide.getModelPart().getContainingTableExpression() ); return generateJoinPredicate( lhsTableReference, rhsTableKeyReference, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingleAttributeIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingleAttributeIdentifierMapping.java index c9b56c7e18..b353f80910 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingleAttributeIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/SingleAttributeIdentifierMapping.java @@ -6,18 +6,85 @@ */ package org.hibernate.metamodel.mapping.internal; +import org.hibernate.generator.Generator; +import org.hibernate.metamodel.mapping.AttributeMapping; +import org.hibernate.metamodel.mapping.AttributeMetadata; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; +import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.PropertyBasedMapping; import org.hibernate.property.access.spi.PropertyAccess; +import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan; +import org.hibernate.type.descriptor.java.MutabilityPlan; /** * @author Steve Ebersole */ -public interface SingleAttributeIdentifierMapping extends EntityIdentifierMapping, PropertyBasedMapping { +public interface SingleAttributeIdentifierMapping extends EntityIdentifierMapping, PropertyBasedMapping, + AttributeMapping, AttributeMetadata { /** * Access to the identifier attribute's PropertyAccess */ PropertyAccess getPropertyAccess(); String getAttributeName(); + + @Override + default String getPartName() { + return ROLE_LOCAL_NAME; + } + + @Override + default Generator getGenerator() { + return null; + } + + @Override + default int getStateArrayPosition() { + return -1; + } + + @Override + default AttributeMetadata getAttributeMetadata() { + return this; + } + + @Override + default ManagedMappingType getDeclaringType() { + return findContainingEntityMapping(); + } + + @Override + default boolean isSelectable() { + return true; + } + + @Override + default boolean isNullable() { + return false; + } + + @Override + default boolean isInsertable() { + return true; + } + + @Override + default boolean isUpdatable() { + return false; + } + + @Override + default boolean isIncludedInDirtyChecking() { + return false; + } + + @Override + default boolean isIncludedInOptimisticLocking() { + return true; + } + + @Override + default MutabilityPlan getMutabilityPlan() { + return ImmutableMutabilityPlan.INSTANCE; + } } 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 edd003208f..7dfe0ed28a 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 @@ -51,6 +51,7 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectablePath; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.VirtualModelPart; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.persister.collection.AbstractCollectionPersister; @@ -106,7 +107,8 @@ import org.hibernate.type.Type; */ public class ToOneAttributeMapping extends AbstractSingularAttributeMapping - implements EntityValuedFetchable, EntityAssociationMapping, TableGroupJoinProducer { + implements EntityValuedFetchable, EntityAssociationMapping, TableGroupJoinProducer, + LazyTableGroup.ParentTableGroupUseChecker { public enum Cardinality { ONE_TO_ONE, @@ -409,18 +411,18 @@ public class ToOneAttributeMapping compositeType.getSubtypes()[0], declaringEntityPersister.getFactory() ); - } - else { - this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME; addPrefixedPropertyNames( targetKeyPropertyNames, - null, + EntityIdentifierMapping.ROLE_LOCAL_NAME, propertyType, declaringEntityPersister.getFactory() ); + } + else { + this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME; addPrefixedPropertyPaths( targetKeyPropertyNames, - targetKeyPropertyName, + null, propertyType, declaringEntityPersister.getFactory() ); @@ -440,7 +442,7 @@ public class ToOneAttributeMapping else if ( bootValue.isReferenceToPrimaryKey() ) { this.targetKeyPropertyName = referencedPropertyName; final Set targetKeyPropertyNames = new HashSet<>( 2 ); - addPrefixedPropertyPaths( + addPrefixedPropertyNames( targetKeyPropertyNames, targetKeyPropertyName, bootValue.getType(), @@ -463,30 +465,41 @@ public class ToOneAttributeMapping compositeType.getSubtypes()[0], declaringEntityPersister.getFactory() ); + addPrefixedPropertyNames( + targetKeyPropertyNames, + EntityIdentifierMapping.ROLE_LOCAL_NAME, + propertyType, + declaringEntityPersister.getFactory() + ); this.targetKeyPropertyNames = targetKeyPropertyNames; } else { + final Set targetKeyPropertyNames = new HashSet<>( 2 ); this.targetKeyPropertyName = referencedPropertyName; final String mapsIdAttributeName; // If there is a "virtual property" for a non-PK join mapping, we try to see if the columns match the // primary key columns and if so, we add the primary key property name as target key property if ( ( mapsIdAttributeName = findMapsIdPropertyName( entityMappingType, referencedPropertyName ) ) != null ) { - final Set targetKeyPropertyNames = new HashSet<>( 2 ); - targetKeyPropertyNames.add( targetKeyPropertyName ); addPrefixedPropertyPaths( targetKeyPropertyNames, mapsIdAttributeName, entityMappingType.getEntityPersister().getIdentifierType(), declaringEntityPersister.getFactory() ); - this.targetKeyPropertyNames = targetKeyPropertyNames; - } - else { - this.targetKeyPropertyNames = Set.of( - targetKeyPropertyName, - ForeignKeyDescriptor.PART_NAME - ); } + addPrefixedPropertyNames( + targetKeyPropertyNames, + targetKeyPropertyName, + propertyType, + declaringEntityPersister.getFactory() + ); + addPrefixedPropertyNames( + targetKeyPropertyNames, + ForeignKeyDescriptor.PART_NAME, + propertyType, + declaringEntityPersister.getFactory() + ); + this.targetKeyPropertyNames = targetKeyPropertyNames; } } } @@ -653,7 +666,7 @@ public class ToOneAttributeMapping return null; } - private static void addPrefixedPropertyPaths( + public static void addPrefixedPropertyPaths( Set targetKeyPropertyNames, String prefix, Type type, @@ -715,21 +728,40 @@ public class ToOneAttributeMapping propertyName = entityType.getRHSUniqueKeyPropertyName(); } final String newPrefix; + final String newPkPrefix; final String newFkPrefix; if ( prefix == null ) { newPrefix = propertyName; + newPkPrefix = propertyName + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME; newFkPrefix = ForeignKeyDescriptor.PART_NAME; } else if ( propertyName == null ) { newPrefix = prefix; + newPkPrefix = prefix + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME; newFkPrefix = prefix + "." + ForeignKeyDescriptor.PART_NAME; } else { newPrefix = prefix + "." + propertyName; + newPkPrefix = prefix + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME; newFkPrefix = prefix + "." + ForeignKeyDescriptor.PART_NAME; } addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, identifierOrUniqueKeyType, factory ); + addPrefixedPropertyNames( targetKeyPropertyNames, newPkPrefix, identifierOrUniqueKeyType, factory ); addPrefixedPropertyNames( targetKeyPropertyNames, newFkPrefix, identifierOrUniqueKeyType, factory ); + if ( identifierOrUniqueKeyType instanceof EmbeddedComponentType ) { + final String newEmbeddedPkPrefix; + final String newEmbeddedFkPrefix; + if ( prefix == null ) { + newEmbeddedPkPrefix = EntityIdentifierMapping.ROLE_LOCAL_NAME; + newEmbeddedFkPrefix = ForeignKeyDescriptor.PART_NAME; + } + else { + newEmbeddedPkPrefix = prefix + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME; + newEmbeddedFkPrefix = prefix + "." + ForeignKeyDescriptor.PART_NAME; + } + addPrefixedPropertyNames( targetKeyPropertyNames, newEmbeddedPkPrefix, identifierOrUniqueKeyType, factory ); + addPrefixedPropertyNames( targetKeyPropertyNames, newEmbeddedFkPrefix, identifierOrUniqueKeyType, factory ); + } } } @@ -843,7 +875,11 @@ public class ToOneAttributeMapping else { fkPart = foreignKeyDescriptor.getTargetPart(); } - if ( fkPart instanceof EmbeddableValuedModelPart && fkPart instanceof VirtualModelPart ) { + if ( fkPart instanceof EmbeddableValuedModelPart && fkPart instanceof VirtualModelPart + && !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( name ) + && !ForeignKeyDescriptor.PART_NAME.equals( name ) + && !ForeignKeyDescriptor.TARGET_PART_NAME.equals( name ) + && !fkPart.getPartName().equals( name ) ) { return ( (ModelPartContainer) fkPart ).findSubPart( name, targetType ); } return fkPart; @@ -940,13 +976,22 @@ public class ToOneAttributeMapping assert !creationState.isResolvingCircularFetch(); try { creationState.setResolvingCircularFetch( true ); - foreignKeyDomainResult = foreignKeyDescriptor.createDomainResult( - fetchablePath, - parentTableGroup, - sideNature, - fetchParent, - creationState - ); + if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) { + foreignKeyDomainResult = foreignKeyDescriptor.createKeyDomainResult( + fetchablePath, + createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ), + fetchParent, + creationState + ); + } + else { + foreignKeyDomainResult = foreignKeyDescriptor.createTargetDomainResult( + fetchablePath, + parentTableGroup, + fetchParent, + creationState + ); + } } finally { creationState.setResolvingCircularFetch( false ); @@ -1155,9 +1200,14 @@ public class ToOneAttributeMapping if ( sideNature == ForeignKeyDescriptor.Nature.KEY && !isKeyTableNullable ) { keyDomainResult = foreignKeyDescriptor.createKeyDomainResult( fetchablePath, - creationState.getSqlAstCreationState() - .getFromClauseAccess() - .findTableGroup( realFetchParent.getNavigablePath() ), + createTableGroupForDelayedFetch( + fetchablePath, + creationState.getSqlAstCreationState() + .getFromClauseAccess() + .findTableGroup( realFetchParent.getNavigablePath() ), + null, + creationState + ), fetchParent, creationState ); @@ -1207,14 +1257,24 @@ public class ToOneAttributeMapping else { realParent = parentNavigablePath; } - final TableGroup tableGroup = fromClauseAccess.getTableGroup( realParent ); - final DomainResult domainResult = foreignKeyDescriptor.createDomainResult( - fetchablePath, - tableGroup, - sideNature, - fetchParent, - creationState - ); + final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( realParent ); + final DomainResult domainResult; + if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) { + domainResult = foreignKeyDescriptor.createKeyDomainResult( + fetchablePath, + createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ), + fetchParent, + creationState + ); + } + else { + domainResult = foreignKeyDescriptor.createTargetDomainResult( + fetchablePath, + parentTableGroup, + fetchParent, + creationState + ); + } if ( fetchTiming == FetchTiming.IMMEDIATE ) { return buildEntityFetchSelect( fetchParent, @@ -1421,7 +1481,7 @@ public class ToOneAttributeMapping if ( notFoundAction != null || !isInternalLoadNullable ) { keyResult = foreignKeyDescriptor.createKeyDomainResult( fetchablePath, - parentTableGroup, + tableGroup, fetchParent, creationState ); @@ -1484,13 +1544,29 @@ public class ToOneAttributeMapping else { side = this.sideNature; } - final DomainResult keyResult = foreignKeyDescriptor.createDomainResult( - fetchablePath, - parentTableGroup, - side, - fetchParent, - creationState - ); + final DomainResult keyResult; + if ( side == ForeignKeyDescriptor.Nature.KEY ) { + final TableGroup tableGroup = sideNature == ForeignKeyDescriptor.Nature.KEY + ? createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ) + : parentTableGroup; + keyResult = foreignKeyDescriptor.createKeyDomainResult( + fetchablePath, + tableGroup, + fetchParent, + creationState + ); + } + else { + final TableGroup tableGroup = sideNature == ForeignKeyDescriptor.Nature.TARGET + ? parentTableGroup + : createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ); + keyResult = foreignKeyDescriptor.createTargetDomainResult( + fetchablePath, + tableGroup, + fetchParent, + creationState + ); + } final boolean selectByUniqueKey = isSelectByUniqueKey( side ); // Consider all associations annotated with @NotFound as EAGER @@ -1577,6 +1653,38 @@ public class ToOneAttributeMapping ); } + private TableGroup createTableGroupForDelayedFetch( + NavigablePath fetchablePath, + TableGroup parentTableGroup, + String resultVariable, + DomainResultCreationState creationState) { + // Check if we can reuse a table group join of the parent + final TableGroup compatibleTableGroup = parentTableGroup.findCompatibleJoinedGroup( + this, + SqlAstJoinType.LEFT + ); + if ( compatibleTableGroup != null ) { + return compatibleTableGroup; + } + // We have to create the table group that points to the target so that table reference resolving works + final TableGroupJoin tableGroupJoin = createTableGroupJoin( + fetchablePath, + parentTableGroup, + resultVariable, + null, + SqlAstJoinType.LEFT, + false, + false, + creationState.getSqlAstCreationState() + ); + parentTableGroup.addTableGroupJoin( tableGroupJoin ); + creationState.getSqlAstCreationState().getFromClauseAccess().registerTableGroup( + fetchablePath, + tableGroupJoin.getJoinedGroup() + ); + return tableGroupJoin.getJoinedGroup(); + } + private boolean isSelectByUniqueKey(ForeignKeyDescriptor.Nature side) { if ( side == ForeignKeyDescriptor.Nature.KEY ) { // case 1.2 @@ -1598,7 +1706,7 @@ public class ToOneAttributeMapping @Override public DomainResult createSnapshotDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup parentTableGroup, String resultVariable, DomainResultCreationState creationState) { // We need a join if either @@ -1615,31 +1723,36 @@ public class ToOneAttributeMapping np -> { final TableGroupJoin tableGroupJoin = createTableGroupJoin( navigablePath, - tableGroup, + parentTableGroup, null, null, - getDefaultSqlAstJoinType( tableGroup ), + getDefaultSqlAstJoinType( parentTableGroup ), true, false, creationState.getSqlAstCreationState() ); - tableGroup.addTableGroupJoin( tableGroupJoin ); + parentTableGroup.addTableGroupJoin( tableGroupJoin ); return tableGroupJoin.getJoinedGroup(); } ); } else { - tableGroupToUse = tableGroup; + tableGroupToUse = createTableGroupForDelayedFetch( + navigablePath, + parentTableGroup, + resultVariable, + creationState + ); } if ( hasNotFoundAction() ) { - assert tableGroupToUse != tableGroup; + assert tableGroupToUse != parentTableGroup; //noinspection unchecked return new NotFoundSnapshotResult( navigablePath, this, + parentTableGroup, tableGroupToUse, - tableGroup, creationState ); } @@ -1727,6 +1840,11 @@ public class ToOneAttributeMapping return predicate == null || foreignKeyDescriptor.isSimpleJoinPredicate( predicate ); } + @Override + public boolean containsTableReference(String tableExpression) { + return getEntityMappingType().containsTableReference( tableExpression ); + } + @Override public int getNumberOfFetchables() { return getEntityMappingType().getNumberOfFetchables(); @@ -1767,8 +1885,18 @@ public class ToOneAttributeMapping embeddablePathSb = new StringBuilder(); } embeddablePathSb.insert( 0, parentContainer.getPartName() + "." ); - parentTableGroup = fromClauseAccess.findTableGroup( parentTableGroup.getNavigablePath().getParent() ); - parentContainer = parentTableGroup.getModelPart(); + final NavigablePath parentNavigablePath = parentTableGroup.getNavigablePath(); + final TableGroup tableGroup = fromClauseAccess.findTableGroup( parentNavigablePath.getParent() ); + if ( tableGroup == null ) { + assert parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.PART_NAME ) + || parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.TARGET_PART_NAME ); + // Might happen that we don't register a table group for the collection role if this is a + // foreign key part and the collection is delayed. We can just break out in this case though, + // since these checks here are only for reusing a map key property, which we won't have + break; + } + parentTableGroup = tableGroup; + parentContainer = tableGroup.getModelPart(); } else { break; @@ -1803,28 +1931,7 @@ public class ToOneAttributeMapping indexTableGroup, fetched, pluralTableGroup, - (np, tableExpression) -> { - if ( !canUseParentTableGroup ) { - return false; - } - - if ( !identifyingColumnsTableExpression.equals( tableExpression ) ) { - return false; - } - - if ( navigablePath.equals( np.getParent() ) ) { - return targetKeyPropertyNames.contains( np.getLocalName() ); - } - - final String relativePath = np.relativize( navigablePath ); - if ( relativePath == null ) { - return false; - } - - // Empty relative path means the navigable paths are equal, - // in which case we allow resolving the parent table group - return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath ); - } + this ), null ); @@ -1850,8 +1957,11 @@ public class ToOneAttributeMapping lazyTableGroup, null ); - - final TableReference lhsTableReference = lhs.resolveTableReference( navigablePath, identifyingColumnsTableExpression ); + final TableReference lhsTableReference = lhs.resolveTableReference( + navigablePath, + this, + identifyingColumnsTableExpression + ); lazyTableGroup.setTableGroupInitializerCallback( tableGroup -> { @@ -1932,7 +2042,19 @@ public class ToOneAttributeMapping TableGroup realParentTableGroup = lhs; final FromClauseAccess fromClauseAccess = creationState.getFromClauseAccess(); while ( realParentTableGroup.getModelPart() instanceof EmbeddableValuedModelPart ) { - realParentTableGroup = fromClauseAccess.findTableGroup( realParentTableGroup.getNavigablePath().getParent() ); + final NavigablePath parentNavigablePath = realParentTableGroup.getNavigablePath(); + final TableGroup tableGroup = fromClauseAccess.findTableGroup( parentNavigablePath.getParent() ); + if ( tableGroup == null ) { + assert parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.PART_NAME ) + || parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.TARGET_PART_NAME ); + // Might happen that we don't register a table group for the collection role if this is a + // foreign key part and the collection is delayed. We can just break out in this case though, + // since the realParentTableGroup is only relevant if this association is actually joined, + // which it is not, because this is part of the target FK + realParentTableGroup = null; + break; + } + realParentTableGroup = tableGroup; } final TableGroupProducer tableGroupProducer; @@ -1958,28 +2080,7 @@ public class ToOneAttributeMapping sqlAliasBase, creationState ), - (np, tableExpression) -> { - if ( !canUseParentTableGroup || tableGroupProducer != ToOneAttributeMapping.this ) { - return false; - } - - if ( !identifyingColumnsTableExpression.equals( tableExpression ) ) { - return false; - } - - if ( navigablePath.pathsMatch( np.getParent() ) ) { - return targetKeyPropertyNames.contains( np.getLocalName() ); - } - - final String relativePath = np.relativize( navigablePath ); - if ( relativePath == null ) { - return false; - } - - // Empty relative path means the navigable paths are equal, - // in which case we allow resolving the parent table group - return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath ); - }, + this, tableGroupProducer, explicitSourceAlias, sqlAliasBase, @@ -2015,6 +2116,16 @@ public class ToOneAttributeMapping return lazyTableGroup; } + @Override + public boolean canUseParentTableGroup( + TableGroupProducer producer, + NavigablePath navigablePath, + ValuedModelPart valuedModelPart) { + return producer == this + && sideNature == ForeignKeyDescriptor.Nature.KEY + && foreignKeyDescriptor.isKeyPart( valuedModelPart ); + } + private void initializeIfNeeded(TableGroup lhs, SqlAstJoinType sqlAstJoinType, TableGroup tableGroup) { if ( sqlAstJoinType == SqlAstJoinType.INNER && ( isNullable || !lhs.canUseInnerJoins() ) ) { // Force initialization of the underlying table group join to retain cardinality diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualIdEmbeddable.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualIdEmbeddable.java index 02769d7895..e4ec1079ea 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualIdEmbeddable.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/VirtualIdEmbeddable.java @@ -89,7 +89,7 @@ public class VirtualIdEmbeddable extends AbstractEmbeddableMapping implements Id selectableMappings, inverseMappingType, creationProcess, - valueMapping.getDeclaringType(), + this, this.attributeMappings ) ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/SingularAttributeImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/SingularAttributeImpl.java index 1ca23c81a1..b085a9ab93 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/SingularAttributeImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/model/domain/internal/SingularAttributeImpl.java @@ -16,6 +16,7 @@ import org.hibernate.metamodel.internal.MetadataContext; import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.model.domain.AnyMappingDomainType; import org.hibernate.metamodel.model.domain.DomainType; +import org.hibernate.metamodel.model.domain.IdentifiableDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; import org.hibernate.metamodel.model.domain.SimpleDomainType; @@ -197,6 +198,13 @@ public class SingularAttributeImpl if ( parent.getReferencedPathSource() instanceof PluralPersistentAttribute ) { navigablePath = navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ); } + if ( getDeclaringType() instanceof IdentifiableDomainType ) { + final IdentifiableDomainType declaringType = (IdentifiableDomainType) getDeclaringType(); + if ( !declaringType.hasSingleIdAttribute() ) { + return new EntityIdentifierNavigablePath( navigablePath, null ) + .append( getName(), SqmCreationHelper.determineAlias( alias ) ); + } + } return new EntityIdentifierNavigablePath( navigablePath, SqmCreationHelper.determineAlias( alias ), getName() ); } } 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 292bfce704..e15cc8ac89 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 @@ -1327,7 +1327,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister { // Table references not appearing in this set can later be pruned away for ( String subclassTableName : subclassTableNames ) { final TableReference tableReference = - tableGroup.getTableReference( null, subclassTableName, false, false ); + tableGroup.getTableReference( null, subclassTableName, false ); if ( tableReference == null ) { throw new UnknownTableReferenceException( getRootTableName(), "Couldn't find table reference" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleBasicEntityIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleBasicEntityIdentifierMapping.java index 40288b078a..d1f65dc0b8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleBasicEntityIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleBasicEntityIdentifierMapping.java @@ -11,6 +11,8 @@ import org.hibernate.engine.spi.IdentifierValue; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.JdbcMapping; +import org.hibernate.metamodel.mapping.ManagedMappingType; +import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.query.sqm.SqmExpressible; @@ -25,11 +27,12 @@ public class AnonymousTupleBasicEntityIdentifierMapping private final BasicEntityIdentifierMapping delegate; public AnonymousTupleBasicEntityIdentifierMapping( + MappingType declaringType, String selectionExpression, SqmExpressible expressible, JdbcMapping jdbcMapping, BasicEntityIdentifierMapping delegate) { - super( delegate.getAttributeName(), selectionExpression, expressible, jdbcMapping, -1 ); + super( declaringType, delegate.getAttributeName(), selectionExpression, expressible, jdbcMapping, -1 ); this.delegate = delegate; } @@ -67,4 +70,9 @@ public class AnonymousTupleBasicEntityIdentifierMapping public String getAttributeName() { return getPartName(); } + + @Override + public ManagedMappingType getDeclaringType() { + return (ManagedMappingType) super.getDeclaringType(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleBasicValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleBasicValuedModelPart.java index a1ff5335db..b749e7f079 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleBasicValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleBasicValuedModelPart.java @@ -18,7 +18,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.MappingType; -import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.metamodel.mapping.OwnedValuedModelPart; import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.query.sqm.SqmExpressible; @@ -41,9 +41,10 @@ import org.hibernate.type.descriptor.java.JavaType; * @author Christian Beikov */ @Incubating -public class AnonymousTupleBasicValuedModelPart implements ModelPart, MappingType, BasicValuedModelPart { +public class AnonymousTupleBasicValuedModelPart implements OwnedValuedModelPart, MappingType, BasicValuedModelPart { private static final FetchOptions FETCH_OPTIONS = FetchOptions.valueOf( FetchTiming.IMMEDIATE, FetchStyle.JOIN ); + private final MappingType declaringType; private final String partName; private final String selectionExpression; private final SqmExpressible expressible; @@ -51,11 +52,13 @@ public class AnonymousTupleBasicValuedModelPart implements ModelPart, MappingTyp private final int fetchableIndex; public AnonymousTupleBasicValuedModelPart( + MappingType declaringType, String partName, String selectionExpression, SqmExpressible expressible, JdbcMapping jdbcMapping, int fetchableIndex) { + this.declaringType = declaringType; this.partName = partName; this.selectionExpression = selectionExpression; this.expressible = expressible; @@ -78,6 +81,11 @@ public class AnonymousTupleBasicValuedModelPart implements ModelPart, MappingTyp return expressible.getExpressibleJavaType(); } + @Override + public MappingType getDeclaringType() { + return declaringType; + } + @Override public String getPartName() { return partName; @@ -217,6 +225,7 @@ public class AnonymousTupleBasicValuedModelPart implements ModelPart, MappingTyp final SqlExpressionResolver expressionResolver = creationState.getSqlExpressionResolver(); final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, + this, getContainingTableExpression() ); final Expression expression = expressionResolver.resolveSqlExpression( tableReference, this ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEmbeddableValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEmbeddableValuedModelPart.java index f99c34cb11..9320d47ffa 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEmbeddableValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEmbeddableValuedModelPart.java @@ -9,6 +9,7 @@ package org.hibernate.query.derived; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -34,7 +35,9 @@ import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.NavigableRole; +import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy; +import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.Clause; @@ -60,6 +63,8 @@ import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.embeddable.internal.EmbeddableResultImpl; import org.hibernate.type.descriptor.java.JavaType; +import jakarta.persistence.metamodel.Attribute; + /** * @author Christian Beikov */ @@ -76,12 +81,25 @@ public class AnonymousTupleEmbeddableValuedModelPart implements EmbeddableValued private final int fetchableIndex; public AnonymousTupleEmbeddableValuedModelPart( - Map modelPartMap, + SqmExpressible sqmExpressible, + List sqlSelections, + int selectionIndex, + String selectionExpression, + Set compatibleTableExpressions, + Set> attributes, DomainType domainType, String componentName, EmbeddableValuedModelPart existingModelPartContainer, int fetchableIndex) { - this.modelPartMap = modelPartMap; + this.modelPartMap = createModelParts( + sqmExpressible, + sqlSelections, + selectionIndex, + selectionExpression, + compatibleTableExpressions, + attributes, + existingModelPartContainer + ); this.modelParts = modelPartMap.values().toArray( new ModelPart[0] ); this.domainType = domainType; this.componentName = componentName; @@ -89,6 +107,38 @@ public class AnonymousTupleEmbeddableValuedModelPart implements EmbeddableValued this.fetchableIndex = fetchableIndex; } + private Map createModelParts( + SqmExpressible sqmExpressible, + List sqlSelections, + int selectionIndex, + String selectionExpression, + Set compatibleTableExpressions, + Set> attributes, + EmbeddableValuedModelPart modelPartContainer) { + final Map modelParts = CollectionHelper.linkedMapOfSize( attributes.size() ); + int index = 0; + for ( Attribute attribute : attributes ) { + if ( !( attribute instanceof SingularPersistentAttribute ) ) { + throw new IllegalArgumentException( "Only embeddables without collections are supported!" ); + } + final DomainType attributeType = ( (SingularPersistentAttribute) attribute ).getType(); + final ModelPart modelPart = AnonymousTupleTableGroupProducer.createModelPart( + this, + sqmExpressible, + attributeType, + sqlSelections, + selectionIndex, + selectionExpression + "_" + attribute.getName(), + attribute.getName(), + modelPartContainer.findSubPart( attribute.getName(), null ), + compatibleTableExpressions, + index++ + ); + modelParts.put( modelPart.getPartName(), modelPart ); + } + return modelParts; + } + @Override public ModelPart findSubPart(String name, EntityMappingType treatTargetType) { return modelPartMap.get( name ); @@ -281,7 +331,7 @@ public class AnonymousTupleEmbeddableValuedModelPart implements EmbeddableValued SqlAstCreationState sqlAstCreationState) { final List columnReferences = CollectionHelper.arrayList( getJdbcTypeCount() ); final NavigablePath navigablePath = tableGroup.getNavigablePath().append( componentName ); - final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, getContainingTableExpression() ); + final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, this, getContainingTableExpression() ); for ( ModelPart modelPart : modelParts ) { modelPart.forEachSelectable( (columnIndex, selection) -> { diff --git a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEmbeddedEntityIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEmbeddedEntityIdentifierMapping.java index 7f24733b69..2a5aada397 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEmbeddedEntityIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEmbeddedEntityIdentifierMapping.java @@ -6,17 +6,26 @@ */ package org.hibernate.query.derived; +import java.util.List; import java.util.Map; +import java.util.Set; import org.hibernate.Incubating; import org.hibernate.engine.spi.IdentifierValue; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping; import org.hibernate.metamodel.model.domain.DomainType; +import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; import org.hibernate.property.access.spi.PropertyAccess; +import org.hibernate.query.sqm.SqmExpressible; +import org.hibernate.sql.ast.spi.SqlSelection; + +import jakarta.persistence.metamodel.Attribute; /** * @author Christian Beikov @@ -28,14 +37,23 @@ public class AnonymousTupleEmbeddedEntityIdentifierMapping extends AnonymousTupl private final CompositeIdentifierMapping delegate; public AnonymousTupleEmbeddedEntityIdentifierMapping( - Map modelParts, + SqmExpressible sqmExpressible, + List sqlSelections, + int selectionIndex, + String selectionExpression, + Set compatibleTableExpressions, + Set> attributes, DomainType domainType, - String componentName, CompositeIdentifierMapping delegate) { super( - modelParts, + sqmExpressible, + sqlSelections, + selectionIndex, + selectionExpression, + compatibleTableExpressions, + attributes, domainType, - componentName, + delegate.getAttributeName(), delegate, -1 ); @@ -72,6 +90,11 @@ public class AnonymousTupleEmbeddedEntityIdentifierMapping extends AnonymousTupl return ((SingleAttributeIdentifierMapping) delegate).getPropertyAccess(); } + @Override + public int compare(Object value1, Object value2) { + return super.compare( value1, value2 ); + } + @Override public String getAttributeName() { return getPartName(); diff --git a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEntityValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEntityValuedModelPart.java index 053651adaa..e9a32df372 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEntityValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleEntityValuedModelPart.java @@ -40,6 +40,7 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.TableDetails; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.internal.OneToManyCollectionPart; import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; @@ -59,6 +60,7 @@ 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.TableGroupJoinProducer; +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.Predicate; @@ -74,7 +76,8 @@ import static org.hibernate.internal.util.collections.CollectionHelper.arrayList */ @Incubating public class AnonymousTupleEntityValuedModelPart - implements EntityValuedModelPart, EntityMappingType, TableGroupJoinProducer { + implements EntityValuedModelPart, EntityMappingType, TableGroupJoinProducer, ValuedModelPart, + LazyTableGroup.ParentTableGroupUseChecker { private final EntityIdentifierMapping identifierMapping; private final DomainType domainType; @@ -115,7 +118,7 @@ public class AnonymousTupleEntityValuedModelPart @Override public ModelPart findSubPart(String name, EntityMappingType treatTargetType) { if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) { - if ( ( (SingleAttributeIdentifierMapping) identifierMapping ).getAttributeName().equals( name ) ) { + if ( identifierMapping.getAttributeName().equals( name ) ) { return identifierMapping; } } @@ -141,6 +144,11 @@ public class AnonymousTupleEntityValuedModelPart return this; } + @Override + public MappingType getMappedType() { + return getPartMappingType(); + } + @Override public JavaType getJavaType() { return domainType.getExpressibleJavaType(); @@ -151,6 +159,11 @@ public class AnonymousTupleEntityValuedModelPart return componentName; } + @Override + public String getContainingTableExpression() { + return ""; + } + @Override public int getJdbcTypeCount() { return delegate.getJdbcTypeCount(); @@ -227,6 +240,11 @@ public class AnonymousTupleEntityValuedModelPart return identifierMapping.forEachSelectable( offset, consumer ); } + @Override + public SelectableMapping getSelectable(int columnIndex) { + return identifierMapping.getSelectable( columnIndex ); + } + @Override public JavaType getMappedJavaType() { return delegate.getJavaType(); @@ -375,8 +393,7 @@ public class AnonymousTupleEntityValuedModelPart final SelectableMapping targetMapping = targetMappings.get( i ); final TableReference targetTableReference = tg.resolveTableReference( null, - targetMapping.getContainingTableExpression(), - false + targetMapping.getContainingTableExpression() ); predicateConsumer.accept( new ComparisonPredicate( @@ -444,7 +461,6 @@ public class AnonymousTupleEntityValuedModelPart creationState.getSqlAliasBaseGenerator() ); final boolean canUseInnerJoin = sqlAstJoinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins(); - final EntityPersister entityPersister = delegate.getEntityMappingType().getEntityPersister(); final LazyTableGroup lazyTableGroup = new LazyTableGroup( canUseInnerJoin, navigablePath, @@ -457,23 +473,7 @@ public class AnonymousTupleEntityValuedModelPart sqlAliasBase, creationState ), - (np, tableExpression) -> { - if ( !tableExpression.isEmpty() && !entityPersister.containsTableReference( tableExpression ) ) { - return false; - } - if ( navigablePath.equals( np.getParent() ) ) { - return targetKeyPropertyNames.contains( np.getLocalName() ); - } - - final String relativePath = np.relativize( navigablePath ); - if ( relativePath == null ) { - return false; - } - - // Empty relative path means the navigable paths are equal, - // in which case we allow resolving the parent table group - return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath ); - }, + this, this, explicitSourceAlias, sqlAliasBase, @@ -490,6 +490,22 @@ public class AnonymousTupleEntityValuedModelPart return lazyTableGroup; } + @Override + public boolean canUseParentTableGroup(TableGroupProducer producer, NavigablePath navigablePath, ValuedModelPart valuedModelPart) { + final ModelPart foreignKeyPart = getForeignKeyPart(); + if ( foreignKeyPart instanceof AnonymousTupleNonAggregatedEntityIdentifierMapping ) { + final AnonymousTupleNonAggregatedEntityIdentifierMapping identifierMapping = (AnonymousTupleNonAggregatedEntityIdentifierMapping) foreignKeyPart; + final int numberOfFetchables = identifierMapping.getNumberOfFetchables(); + for ( int i = 0; i< numberOfFetchables; i++ ) { + if ( valuedModelPart == identifierMapping.getFetchable( i ) ) { + return true; + } + } + return false; + } + return foreignKeyPart == valuedModelPart; + } + @Override public String getSqlAliasStem() { return getPartName(); @@ -707,4 +723,9 @@ public class AnonymousTupleEntityValuedModelPart ? ( (TableGroupJoinProducer) delegate ).isSimpleJoinPredicate( predicate ) : false; } + + @Override + public boolean containsTableReference(String tableExpression) { + return ( (TableGroupProducer) delegate ).containsTableReference( tableExpression ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleNonAggregatedEntityIdentifierMapping.java b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleNonAggregatedEntityIdentifierMapping.java index 951114badf..07e0c0e8a0 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleNonAggregatedEntityIdentifierMapping.java +++ b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleNonAggregatedEntityIdentifierMapping.java @@ -6,7 +6,9 @@ */ package org.hibernate.query.derived; +import java.util.List; import java.util.Map; +import java.util.Set; import org.hibernate.Incubating; import org.hibernate.engine.FetchStyle; @@ -19,6 +21,10 @@ import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping; import org.hibernate.metamodel.mapping.internal.IdClassEmbeddable; import org.hibernate.metamodel.mapping.internal.VirtualIdEmbeddable; import org.hibernate.metamodel.model.domain.DomainType; +import org.hibernate.query.sqm.SqmExpressible; +import org.hibernate.sql.ast.spi.SqlSelection; + +import jakarta.persistence.metamodel.Attribute; /** * @author Christian Beikov @@ -30,12 +36,22 @@ public class AnonymousTupleNonAggregatedEntityIdentifierMapping extends Anonymou private final NonAggregatedIdentifierMapping delegate; public AnonymousTupleNonAggregatedEntityIdentifierMapping( - Map modelParts, + SqmExpressible sqmExpressible, + List sqlSelections, + int selectionIndex, + String selectionExpression, + Set compatibleTableExpressions, + Set> attributes, DomainType domainType, String componentName, NonAggregatedIdentifierMapping delegate) { super( - modelParts, + sqmExpressible, + sqlSelections, + selectionIndex, + selectionExpression, + compatibleTableExpressions, + attributes, domainType, componentName, delegate, diff --git a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleTableGroupProducer.java b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleTableGroupProducer.java index beef17c850..e7ff7d41cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleTableGroupProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/derived/AnonymousTupleTableGroupProducer.java @@ -36,7 +36,6 @@ import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.NavigableRole; -import org.hibernate.metamodel.model.domain.SingularPersistentAttribute; import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.select.SqmSelectableNode; @@ -92,6 +91,7 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map final SqmPath sqmPath = (SqmPath) selectableNode; final TableGroup tableGroup = fromClauseAccess.findTableGroup( sqmPath.getNavigablePath() ); modelPart = createModelPart( + this, selectableNode.getExpressible(), sqmPath.getNodeType().getSqmPathType(), sqlSelections, @@ -105,6 +105,7 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map } else { modelPart = new AnonymousTupleBasicValuedModelPart( + this, partName, partName, selectableNode.getExpressible(), @@ -129,7 +130,8 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map return tableGroup.getModelPart(); } - private ModelPart createModelPart( + public static ModelPart createModelPart( + MappingType mappingType, SqmExpressible sqmExpressible, DomainType domainType, List sqlSelections, @@ -145,41 +147,24 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map .getIdentifierMapping(); final EntityIdentifierMapping newIdentifierMapping; if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) { - final String attributeName = identifierMapping.getAttributeName(); if ( identifierMapping.getPartMappingType() instanceof ManagedMappingType ) { //noinspection unchecked final Set> attributes = (Set>) ( (ManagedDomainType) ( (EntityDomainType) domainType ).getIdentifierDescriptor().getSqmPathType() ).getAttributes(); - final Map modelParts = CollectionHelper.linkedMapOfSize( attributes.size() ); - final EmbeddableValuedModelPart modelPartContainer = (EmbeddableValuedModelPart) identifierMapping; - int index = 0; - for ( Attribute attribute : attributes ) { - if ( !( attribute instanceof SingularPersistentAttribute ) ) { - throw new IllegalArgumentException( "Only embeddables without collections are supported!" ); - } - final DomainType attributeType = ( (SingularPersistentAttribute) attribute ).getType(); - final ModelPart modelPart = createModelPart( - sqmExpressible, - attributeType, - sqlSelections, - selectionIndex, - selectionExpression + "_" + attributeName + "_" + attribute.getName(), - attribute.getName(), - modelPartContainer.findSubPart( attribute.getName(), null ), - compatibleTableExpressions, - index++ - ); - modelParts.put( modelPart.getPartName(), modelPart ); - } newIdentifierMapping = new AnonymousTupleEmbeddedEntityIdentifierMapping( - modelParts, + sqmExpressible, + sqlSelections, + selectionIndex, + selectionExpression + "_" + identifierMapping.getAttributeName(), + compatibleTableExpressions, + attributes, domainType, - attributeName, (CompositeIdentifierMapping) identifierMapping ); } else { newIdentifierMapping = new AnonymousTupleBasicEntityIdentifierMapping( - selectionExpression + "_" + attributeName, + mappingType, + selectionExpression + "_" + identifierMapping.getAttributeName(), sqmExpressible, sqlSelections.get( selectionIndex ).getExpressionType().getSingleJdbcMapping(), (BasicEntityIdentifierMapping) identifierMapping @@ -189,29 +174,13 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map else { //noinspection unchecked final Set> attributes = (Set>) ( (ManagedDomainType) ( (EntityDomainType) domainType ).getIdentifierDescriptor().getSqmPathType() ).getAttributes(); - final Map modelParts = CollectionHelper.linkedMapOfSize( attributes.size() ); - final EmbeddableValuedModelPart modelPartContainer = (EmbeddableValuedModelPart) identifierMapping; - int index = 0; - for ( Attribute attribute : attributes ) { - if ( !( attribute instanceof SingularPersistentAttribute ) ) { - throw new IllegalArgumentException( "Only embeddables without collections are supported!" ); - } - final DomainType attributeType = ( (SingularPersistentAttribute) attribute ).getType(); - final ModelPart modelPart = createModelPart( - sqmExpressible, - attributeType, - sqlSelections, - selectionIndex + index, - selectionExpression + "_" + attribute.getName(), - attribute.getName(), - modelPartContainer.findSubPart( attribute.getName(), null ), - compatibleTableExpressions, - index++ - ); - modelParts.put( modelPart.getPartName(), modelPart ); - } newIdentifierMapping = new AnonymousTupleNonAggregatedEntityIdentifierMapping( - modelParts, + sqmExpressible, + sqlSelections, + selectionIndex, + selectionExpression, + compatibleTableExpressions, + attributes, domainType, selectionExpression, (NonAggregatedIdentifierMapping) identifierMapping @@ -232,31 +201,22 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map else if ( domainType instanceof ManagedDomainType ) { //noinspection unchecked final Set> attributes = (Set>) ( (ManagedDomainType) domainType ).getAttributes(); - final Map modelParts = CollectionHelper.linkedMapOfSize( attributes.size() ); - final EmbeddableValuedModelPart modelPartContainer = (EmbeddableValuedModelPart) existingModelPart; - int index = 0; - for ( Attribute attribute : attributes ) { - if ( !( attribute instanceof SingularPersistentAttribute ) ) { - throw new IllegalArgumentException( "Only embeddables without collections are supported" ); - } - final DomainType attributeType = ( (SingularPersistentAttribute) attribute ).getType(); - final ModelPart modelPart = createModelPart( - sqmExpressible, - attributeType, - sqlSelections, - selectionIndex + index, - selectionExpression + "_" + attribute.getName(), - attribute.getName(), - modelPartContainer.findSubPart( attribute.getName(), null ), - compatibleTableExpressions, - index++ - ); - modelParts.put( modelPart.getPartName(), modelPart ); - } - return new AnonymousTupleEmbeddableValuedModelPart( modelParts, domainType, selectionExpression, modelPartContainer, fetchableIndex ); + return new AnonymousTupleEmbeddableValuedModelPart( + sqmExpressible, + sqlSelections, + selectionIndex, + selectionExpression, + compatibleTableExpressions, + attributes, + domainType, + selectionExpression, + (EmbeddableValuedModelPart) existingModelPart, + fetchableIndex + ); } else { return new AnonymousTupleBasicValuedModelPart( + mappingType, partName, selectionExpression, sqmExpressible, diff --git a/hibernate-core/src/main/java/org/hibernate/query/derived/CteTupleTableGroupProducer.java b/hibernate-core/src/main/java/org/hibernate/query/derived/CteTupleTableGroupProducer.java index cf1360afdc..cf9eef2f38 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/derived/CteTupleTableGroupProducer.java +++ b/hibernate-core/src/main/java/org/hibernate/query/derived/CteTupleTableGroupProducer.java @@ -11,6 +11,7 @@ import java.util.List; import org.hibernate.Incubating; import org.hibernate.metamodel.mapping.EntityMappingType; +import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.cte.SqmCteTable; @@ -43,19 +44,24 @@ public class CteTupleTableGroupProducer extends AnonymousTupleTableGroupProducer final BasicType stringType = cteStatement.nodeBuilder() .getTypeConfiguration() .getBasicTypeForJavaType( String.class ); - this.searchModelPart = createModelPart( cteStatement.getSearchAttributeName(), stringType ); + this.searchModelPart = createModelPart( this, cteStatement.getSearchAttributeName(), stringType ); this.cycleMarkModelPart = createModelPart( + this, cteStatement.getCycleMarkAttributeName(), cteStatement.getCycleLiteral() == null ? null : (BasicType) cteStatement.getCycleLiteral().getNodeType() ); - this.cyclePathModelPart = createModelPart( cteStatement.getCyclePathAttributeName(), stringType ); + this.cyclePathModelPart = createModelPart( this, cteStatement.getCyclePathAttributeName(), stringType ); } - private static AnonymousTupleBasicValuedModelPart createModelPart(String attributeName, BasicType basicType) { + private static AnonymousTupleBasicValuedModelPart createModelPart( + MappingType declaringType, + String attributeName, + BasicType basicType) { if ( attributeName != null ) { return new AnonymousTupleBasicValuedModelPart( + declaringType, attributeName, attributeName, basicType, diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java b/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java index 385457a46f..134dea2bb2 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/DomainResultCreationStateImpl.java @@ -374,8 +374,8 @@ public class DomainResultCreationStateImpl @Override public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) { - final EntityValuedModelPart entityValuedFetchable = fetchParent.getEntityValuedModelPart(); - final EntityIdentifierMapping identifierMapping = entityValuedFetchable.getEntityMappingType().getIdentifierMapping(); + final EntityValuedModelPart parentModelPart = fetchParent.getEntityValuedModelPart(); + final EntityIdentifierMapping identifierMapping = parentModelPart.getEntityMappingType().getIdentifierMapping(); final String identifierAttributeName = attributeName( identifierMapping ); final Map.Entry oldEntry = relativePathStack.getCurrent(); final String fullPath; @@ -388,7 +388,7 @@ public class DomainResultCreationStateImpl oldEntry.getKey() + "." + identifierAttributeName; } - final Fetchable fetchable = (Fetchable) identifierMapping; + final Fetchable identifierFetchable = (Fetchable) identifierMapping; final FetchBuilder explicitFetchBuilder = (FetchBuilder) fetchBuilderResolverStack .getCurrent() .apply( fullPath ); @@ -423,7 +423,7 @@ public class DomainResultCreationStateImpl } else { if ( fetchBuilderLegacy == null ) { - fetchBuilder = Builders.implicitFetchBuilder( fetchPath, fetchable, this ); + fetchBuilder = Builders.implicitFetchBuilder( fetchPath, identifierFetchable, this ); } else { fetchBuilder = fetchBuilderLegacy; 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 8323a16732..11a8d0b1bd 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 @@ -54,15 +54,14 @@ public class TableGroupImpl extends AbstractTableGroup { } @Override - public TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { - if ( primaryTableReference.getTableReference( navigablePath , tableExpression, allowFkOptimization, resolve ) != null ) { + if ( primaryTableReference.getTableReference( navigablePath , tableExpression, resolve ) != null ) { return primaryTableReference; } - return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); + return super.getTableReference( navigablePath, tableExpression, resolve ); } } 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 f9673185f5..0ae7621b6d 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 @@ -82,7 +82,7 @@ public class CompleteFetchBuilderBasicPart implements CompleteFetchBuilder, Basi final String mappedTable = referencedModelPart.getContainingTableExpression(); final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( parent.getNavigablePath() ); - final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, mappedTable ); + final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, referencedModelPart, mappedTable ); final String selectedAlias; final int jdbcPosition; diff --git a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderEmbeddableValuedModelPart.java b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderEmbeddableValuedModelPart.java index ab3de15a98..68ae55e057 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderEmbeddableValuedModelPart.java +++ b/hibernate-core/src/main/java/org/hibernate/query/results/complete/CompleteFetchBuilderEmbeddableValuedModelPart.java @@ -86,7 +86,11 @@ public class CompleteFetchBuilderEmbeddableValuedModelPart final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() ); modelPart.forEachSelectable( (selectionIndex, selectableMapping) -> { - final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, selectableMapping.getContainingTableExpression() ); + final TableReference tableReference = tableGroup.resolveTableReference( + navigablePath, + modelPart, + selectableMapping.getContainingTableExpression() + ); final String columnAlias = columnAliases.get( selectionIndex ); creationStateImpl.resolveSqlSelection( ResultsHelper.resolveSqlExpression( 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 bbe8bbb5df..6e5631ca0c 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 @@ -11,6 +11,7 @@ import java.util.List; import java.util.function.BiFunction; import org.hibernate.engine.FetchTiming; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.query.results.ResultsHelper; import org.hibernate.spi.NavigablePath; import org.hibernate.query.results.DomainResultCreationStateImpl; @@ -86,7 +87,11 @@ public class CompleteFetchBuilderEntityValuedModelPart final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() ); modelPart.forEachSelectable( (selectionIndex, selectableMapping) -> { - final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, selectableMapping.getContainingTableExpression() ); + final TableReference tableReference = tableGroup.resolveTableReference( + navigablePath, + (ValuedModelPart) modelPart, + selectableMapping.getContainingTableExpression() + ); final String columnAlias = columnAliases.get( selectionIndex ); creationStateImpl.resolveSqlSelection( ResultsHelper.resolveSqlExpression( 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 44cfc57aae..db5a419731 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 @@ -75,7 +75,7 @@ public class CompleteResultBuilderBasicModelPart final DomainResultCreationStateImpl creationStateImpl = impl( domainResultCreationState ); final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() ); - final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, modelPart.getContainingTableExpression() ); + final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, modelPart, modelPart.getContainingTableExpression() ); final SqlSelection sqlSelection = creationStateImpl.resolveSqlSelection( ResultsHelper.resolveSqlExpression( 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 5a26d7922c..f6c26b1a41 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 @@ -172,7 +172,11 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue (selectionIndex, selectableMapping) -> { resolveSqlSelection( columnNames.get( selectionIndex ), - tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ), + tableGroup.resolveTableReference( + fetchPath, + keyDescriptor.getKeyPart(), + selectableMapping.getContainingTableExpression() + ), selectableMapping, jdbcResultsMetadata, domainResultCreationState 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 551a8864eb..0a56cafe65 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 @@ -12,8 +12,11 @@ import java.util.function.BiFunction; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.BasicValuedMapping; +import org.hibernate.metamodel.mapping.BasicValuedModelPart; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.SelectableConsumer; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.query.NativeQuery; import org.hibernate.spi.NavigablePath; @@ -80,9 +83,108 @@ public class DynamicFetchBuilderStandard final Fetchable attributeMapping = (Fetchable) parent.getReferencedMappingContainer().findSubPart( fetchableName, null ); final SqlExpressionResolver sqlExpressionResolver = domainResultCreationState.getSqlAstCreationState().getSqlExpressionResolver(); - final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> { + if ( attributeMapping instanceof BasicValuedModelPart ) { + attributeMapping.forEachSelectable( + getSelectableConsumer( + fetchPath, + jdbcResultsMetadata, + domainResultCreationState, + creationStateImpl, + ownerTableGroup, + sqlExpressionResolver, + (BasicValuedModelPart) attributeMapping + ) + ); + return parent.generateFetchableFetch( + attributeMapping, + fetchPath, + FetchTiming.IMMEDIATE, + true, + null, + creationStateImpl + ); + } + else if ( attributeMapping instanceof EmbeddableValuedFetchable ) { + attributeMapping.forEachSelectable( + getSelectableConsumer( + fetchPath, + jdbcResultsMetadata, + domainResultCreationState, + creationStateImpl, + ownerTableGroup, + sqlExpressionResolver, + (EmbeddableValuedFetchable) attributeMapping + ) + ); + return parent.generateFetchableFetch( + attributeMapping, + fetchPath, + FetchTiming.IMMEDIATE, + false, + null, + creationStateImpl + ); + } + else if ( attributeMapping instanceof ToOneAttributeMapping ) { + final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping; + toOneAttributeMapping.getForeignKeyDescriptor().getPart( toOneAttributeMapping.getSideNature() ) + .forEachSelectable( + getSelectableConsumer( + fetchPath, + jdbcResultsMetadata, + domainResultCreationState, + creationStateImpl, + ownerTableGroup, + sqlExpressionResolver, + toOneAttributeMapping.getForeignKeyDescriptor() + ) + ); + return parent.generateFetchableFetch( + attributeMapping, + fetchPath, + FetchTiming.DELAYED, + false, + null, + creationStateImpl + ); + } + else { + assert attributeMapping instanceof PluralAttributeMapping; + final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) attributeMapping; + pluralAttributeMapping.getKeyDescriptor().visitTargetSelectables( + getSelectableConsumer( + fetchPath, + jdbcResultsMetadata, + domainResultCreationState, + creationStateImpl, + ownerTableGroup, + sqlExpressionResolver, + pluralAttributeMapping.getKeyDescriptor() + ) + ); + return parent.generateFetchableFetch( + attributeMapping, + fetchPath, + FetchTiming.DELAYED, + false, + null, + creationStateImpl + ); + } + } + + private SelectableConsumer getSelectableConsumer( + NavigablePath fetchPath, + JdbcValuesMetadata jdbcResultsMetadata, + DomainResultCreationState domainResultCreationState, + DomainResultCreationStateImpl creationStateImpl, + TableGroup ownerTableGroup, + SqlExpressionResolver sqlExpressionResolver, + ValuedModelPart valuedModelPart) { + return (selectionIndex, selectableMapping) -> { final TableReference tableReference = ownerTableGroup.resolveTableReference( fetchPath, + valuedModelPart, selectableMapping.getContainingTableExpression() ); final String columnAlias = columnNames.get( selectionIndex ); @@ -102,54 +204,6 @@ public class DynamicFetchBuilderStandard .getTypeConfiguration() ); }; - if ( attributeMapping instanceof BasicValuedMapping ) { - attributeMapping.forEachSelectable( selectableConsumer ); - return parent.generateFetchableFetch( - attributeMapping, - fetchPath, - FetchTiming.IMMEDIATE, - true, - null, - creationStateImpl - ); - } - else if ( attributeMapping instanceof EmbeddableValuedFetchable ) { - attributeMapping.forEachSelectable( selectableConsumer ); - return parent.generateFetchableFetch( - attributeMapping, - fetchPath, - FetchTiming.IMMEDIATE, - false, - null, - creationStateImpl - ); - } - else if ( attributeMapping instanceof ToOneAttributeMapping ) { - final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping; - toOneAttributeMapping.getForeignKeyDescriptor().getPart( toOneAttributeMapping.getSideNature() ) - .forEachSelectable( selectableConsumer ); - return parent.generateFetchableFetch( - attributeMapping, - fetchPath, - FetchTiming.DELAYED, - false, - null, - creationStateImpl - ); - } - else { - assert attributeMapping instanceof PluralAttributeMapping; - final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) attributeMapping; - pluralAttributeMapping.getKeyDescriptor().visitTargetSelectables( selectableConsumer ); - return parent.generateFetchableFetch( - attributeMapping, - fetchPath, - FetchTiming.DELAYED, - false, - null, - creationStateImpl - ); - } } @Override 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 787779d3e8..3860ec5ee2 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 @@ -97,7 +97,7 @@ public class ImplicitFetchBuilderBasic implements ImplicitFetchBuilder, BasicVal final Expression expression = ResultsHelper.resolveSqlExpression( creationStateImpl, jdbcResultsMetadata, - parentTableGroup.resolveTableReference( fetchPath, table ), + parentTableGroup.resolveTableReference( fetchPath, fetchable, table ), fetchable, column ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java index 26f8436077..4534368576 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteDeleteHandler.java @@ -13,7 +13,6 @@ import java.util.Map; import org.hibernate.boot.model.naming.Identifier; import org.hibernate.dialect.Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; -import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.query.sqm.internal.DomainParameterXref; @@ -155,7 +154,6 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele final TableReference updatingTableReference = updatingTableGroup.getTableReference( updatingTableGroup.getNavigablePath(), tableExpression, - true, true ); final NamedTableReference dmlTableReference = resolveUnionTableReference( diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java index 7183acddb4..f381f2f18e 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteInsertHandler.java @@ -758,7 +758,6 @@ public class CteInsertHandler implements InsertHandler { final TableReference rootTableReference = updatingTableGroup.getTableReference( updatingTableGroup.getNavigablePath(), rootTableName, - true, true ); @@ -777,7 +776,6 @@ public class CteInsertHandler implements InsertHandler { final TableReference updatingTableReference = updatingTableGroup.getTableReference( updatingTableGroup.getNavigablePath(), tableExpression, - true, true ); final List, Assignment>> assignmentList = assignmentsByTable.get( updatingTableReference ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java index 7477f3e8f3..5201cc9788 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/cte/CteUpdateHandler.java @@ -159,7 +159,6 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda final TableReference updatingTableReference = updatingTableGroup.getTableReference( updatingTableGroup.getNavigablePath(), tableExpression, - true, true ); final List assignmentList = assignmentsByTable.get( updatingTableReference ); @@ -272,7 +271,6 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda final TableReference updatingTableReference = updatingTableGroup.getTableReference( updatingTableGroup.getNavigablePath(), tableExpression, - true, true ); final List assignmentList = assignmentsByTable.get( updatingTableReference ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java index 8c47a2f6ca..6106c32563 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/inline/InlineUpdateHandler.java @@ -332,7 +332,6 @@ public class InlineUpdateHandler implements UpdateHandler { final TableReference updatingTableReference = updatingTableGroup.getTableReference( updatingTableGroup.getNavigablePath(), tableExpression, - true, true ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java index 7dc59d9fd2..271a8af668 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/InsertExecutionDelegate.java @@ -301,7 +301,6 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio final TableReference updatingTableReference = updatingTableGroup.getTableReference( updatingTableGroup.getNavigablePath(), tableExpression, - true, true ); @@ -661,7 +660,6 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio final TableReference updatingTableReference = updatingTableGroup.getTableReference( updatingTableGroup.getNavigablePath(), tableExpression, - true, true ); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/RestrictedDeleteExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/RestrictedDeleteExecutionDelegate.java index 3c2ee4e781..245082a0f6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/RestrictedDeleteExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/RestrictedDeleteExecutionDelegate.java @@ -23,7 +23,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.internal.util.MutableBoolean; import org.hibernate.internal.util.MutableInteger; -import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.MappingModelExpressible; @@ -329,7 +328,6 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle final NamedTableReference tableReference = (NamedTableReference) tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression, - true, true ); final QuerySpec idMatchingSubQuerySpec; diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java index b57559f34f..e8f4088bd8 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/mutation/internal/temptable/UpdateExecutionDelegate.java @@ -243,7 +243,6 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio final TableReference updatingTableReference = updatingTableGroup.getTableReference( updatingTableGroup.getNavigablePath(), tableExpression, - true, true ); 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 e197aa863c..4c17533f4e 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 @@ -84,6 +84,7 @@ import org.hibernate.metamodel.mapping.SelectableMappings; import org.hibernate.metamodel.mapping.SqlExpressible; import org.hibernate.metamodel.mapping.SqlTypedMapping; import org.hibernate.metamodel.mapping.ValueMapping; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.internal.EntityCollectionPart; import org.hibernate.metamodel.mapping.internal.ManyToManyCollectionPart; import org.hibernate.metamodel.mapping.internal.OneToManyCollectionPart; @@ -182,6 +183,7 @@ import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference; import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin; import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; +import org.hibernate.query.sqm.tree.domain.SqmSimplePath; import org.hibernate.query.sqm.tree.domain.SqmTreatedPath; import org.hibernate.query.sqm.tree.domain.SqmTreatedRoot; import org.hibernate.query.sqm.tree.expression.Conversion; @@ -2901,7 +2903,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base } final int subclassTableSpan = persister.getSubclassTableSpan(); for ( int i = 0; i < subclassTableSpan; i++ ) { - tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ), false ); + tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ) ); } } @@ -2927,7 +2929,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base } final int subclassTableSpan = persister.getSubclassTableSpan(); for ( int i = 0; i < subclassTableSpan; i++ ) { - tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ), false ); + tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ) ); } } @@ -3468,8 +3470,16 @@ public abstract class BaseSqmToSqlAstConverter extends Base } } } - else if ( path instanceof SqmFrom ) { - registerTreatUsage( (SqmFrom) path, tableGroup ); + else { + if ( path instanceof SqmFrom ) { + registerTreatUsage( (SqmFrom) path, tableGroup ); + } + if ( path instanceof SqmSimplePath && CollectionPart.Nature.fromName( path.getNavigablePath().getLocalName() ) == null ) { + // If a table group for a selection already exists, we must make sure that the join type is INNER + fromClauseIndex.findTableGroup( path.getNavigablePath().getParent() ) + .findTableGroupJoin( tableGroup ) + .setJoinType( SqlAstJoinType.INNER ); + } } } @@ -3507,8 +3517,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base } else { // Check if we can reuse a table group join of the parent - final TableGroup compatibleTableGroup = findCompatibleJoinedGroup( - actualParentTableGroup, + final TableGroup compatibleTableGroup = actualParentTableGroup.findCompatibleJoinedGroup( joinProducer, SqlAstJoinType.INNER ); @@ -3898,7 +3907,6 @@ public abstract class BaseSqmToSqlAstConverter extends Base navigablePath, tableGroupToUse == null ? tableGroup : tableGroupToUse, expandToAllColumns ? null : resultModelPart, - true, interpretationModelPart, treatedMapping, this @@ -3908,7 +3916,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base final EmbeddableValuedModelPart mapping = (EmbeddableValuedModelPart) actualModelPart; result = new EmbeddableValuedPathInterpretation<>( mapping.toSqlExpression( - tableGroup, + getFromClauseAccess().findTableGroup( path.getLhs().getNavigablePath() ), currentClauseStack.getCurrent(), this, getSqlAstCreationState() @@ -3923,6 +3931,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base final BasicValuedModelPart mapping = (BasicValuedModelPart) actualModelPart; final TableReference tableReference = tableGroup.resolveTableReference( navigablePath.append( actualModelPart.getPartName() ), + mapping, mapping.getContainingTableExpression() ); @@ -4439,6 +4448,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base new ColumnReference( tableGroup.resolveTableReference( navigablePath, + (ValuedModelPart) modelPart, selectionMapping.getContainingTableExpression() ), selectionMapping @@ -4564,6 +4574,7 @@ public abstract class BaseSqmToSqlAstConverter extends Base final ColumnReference columnReference = new ColumnReference( tableGroup.resolveTableReference( navigablePath, + (ValuedModelPart) modelPart, selectionMapping.getContainingTableExpression() ), selectionMapping @@ -7139,11 +7150,9 @@ public abstract class BaseSqmToSqlAstConverter extends Base @Override public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) { - final EntityIdentifierMapping identifierMapping = fetchParent.getEntityValuedModelPart() - .getEntityMappingType() + final EntityIdentifierMapping identifierMapping = fetchParent.getReferencedMappingContainer() .getIdentifierMapping(); - final Fetchable fetchableIdentifierMapping = (Fetchable) identifierMapping; - return createFetch( fetchParent, fetchableIdentifierMapping, false ); + return createFetch( fetchParent, (Fetchable) identifierMapping, false ); } private Fetch createFetch(FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) { diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/BasicValuedPathInterpretation.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/BasicValuedPathInterpretation.java index 02f6fd8930..79f2635505 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/BasicValuedPathInterpretation.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/BasicValuedPathInterpretation.java @@ -108,6 +108,7 @@ public class BasicValuedPathInterpretation extends AbstractSqmPathInterpretat final TableReference tableReference = tableGroup.resolveTableReference( sqmPath.getNavigablePath(), + mapping, mapping.getContainingTableExpression() ); 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 966731568c..475f28526b 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 @@ -21,6 +21,7 @@ import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.SelectableConsumer; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.internal.EntityCollectionPart; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.query.derived.AnonymousTupleEntityValuedModelPart; @@ -44,8 +45,8 @@ import org.hibernate.sql.ast.tree.update.Assignable; import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.Fetchable; -public class EntityValuedPathInterpretation extends AbstractSqmPathInterpretation implements SqlTupleContainer, - Assignable { +public class EntityValuedPathInterpretation extends AbstractSqmPathInterpretation + implements SqlTupleContainer, Assignable { public static EntityValuedPathInterpretation from( SqmEntityValuedSimplePath sqmPath, @@ -99,7 +100,6 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta sqmPath.getNavigablePath(), tableGroup, pathMapping.getEntityMappingType().getIdentifierMapping(), - false, pathMapping, pathMapping, sqlAstCreationState @@ -149,7 +149,6 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta EntityValuedModelPart mapping, MappingModelExpressible inferredMapping, SqmToSqlAstConverter sqlAstCreationState) { - final boolean allowFkOptimization; final ModelPart resultModelPart; final TableGroup resultTableGroup; // For association mappings where the FK optimization i.e. use of the parent table group is allowed, @@ -220,7 +219,6 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta } resultTableGroup = tableGroup; } - allowFkOptimization = true; } else if ( inferredMapping == null && hasNotFound( mapping ) ) { // This is necessary to allow expression like `where root.notFoundAssociation is null` @@ -229,31 +227,26 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta resultModelPart = keyTargetMatchPart; resultTableGroup = sqlAstCreationState.getFromClauseAccess() .findTableGroup( tableGroup.getNavigablePath().getParent() ); - allowFkOptimization = false; } else { // If the mapping is an inverse association, use the PK and disallow FK optimizations resultModelPart = ( (EntityAssociationMapping) mapping ).getAssociatedEntityMappingType().getIdentifierMapping(); resultTableGroup = tableGroup; - allowFkOptimization = false; } } else if ( mapping instanceof AnonymousTupleEntityValuedModelPart ) { resultModelPart = ( (AnonymousTupleEntityValuedModelPart) mapping ).getForeignKeyPart(); resultTableGroup = tableGroup; - allowFkOptimization = true; } else { // If the mapping is not an association, use the PK and disallow FK optimizations resultModelPart = mapping.getEntityMappingType().getIdentifierMapping(); resultTableGroup = tableGroup; - allowFkOptimization = false; } return from( navigablePath, resultTableGroup, resultModelPart, - allowFkOptimization, mapping, mapping, sqlAstCreationState @@ -288,7 +281,6 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta NavigablePath navigablePath, TableGroup tableGroup, ModelPart resultModelPart, - boolean allowFkOptimization, EntityValuedModelPart mapping, EntityValuedModelPart treatedMapping, SqmToSqlAstConverter sqlAstCreationState) { @@ -309,8 +301,7 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> { final TableReference tableReference = parentTableGroup.resolveTableReference( navigablePath, - selectableMapping.getContainingTableExpression(), - false + selectableMapping.getContainingTableExpression() ); expressions.add( sqlExprResolver.resolveSqlExpression( tableReference, selectableMapping ) @@ -333,8 +324,8 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta final BasicValuedModelPart basicValuedModelPart = (BasicValuedModelPart) resultModelPart; final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, - basicValuedModelPart.getContainingTableExpression(), - allowFkOptimization + basicValuedModelPart, + basicValuedModelPart.getContainingTableExpression() ); sqlExpression = sqlExprResolver.resolveSqlExpression( tableReference, basicValuedModelPart ); } @@ -344,8 +335,8 @@ public class EntityValuedPathInterpretation extends AbstractSqmPathInterpreta (selectionIndex, selectableMapping) -> { final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, - selectableMapping.getContainingTableExpression(), - allowFkOptimization + (ValuedModelPart) resultModelPart, + selectableMapping.getContainingTableExpression() ); expressions.add( sqlExprResolver.resolveSqlExpression( tableReference, selectableMapping ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/spi/NavigablePath.java b/hibernate-core/src/main/java/org/hibernate/spi/NavigablePath.java index fe5db18524..1d6d058f31 100644 --- a/hibernate-core/src/main/java/org/hibernate/spi/NavigablePath.java +++ b/hibernate-core/src/main/java/org/hibernate/spi/NavigablePath.java @@ -199,7 +199,7 @@ public class NavigablePath implements DotIdentifierSequence, Serializable { if ( dotIdentifierSequence == null ) { return true; } - if ( !getLocalName().equals( dotIdentifierSequence.getLocalName() ) ) { + if ( !localNamesMatch( dotIdentifierSequence ) ) { return false; } return getParent() != null && getParent().isSuffix( dotIdentifierSequence.getParent() ); 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 8bfcec4306..dce5b2c8f8 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 @@ -67,10 +67,9 @@ public class CteTableGroup extends AbstractTableGroup { } @Override - protected TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { if ( compatibleTableExpressions.contains( tableExpression ) ) { return getPrimaryTableReference(); @@ -78,7 +77,7 @@ public class CteTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, tableExpression, resolve ); if ( groupTableReference != null ) { return groupTableReference; } @@ -86,7 +85,7 @@ public class CteTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, tableExpression, resolve ); if ( groupTableReference != null ) { return groupTableReference; } 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 6de34d4465..87fb4cf6f2 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 @@ -7,7 +7,6 @@ package org.hibernate.sql.ast.tree.from; import java.util.List; -import java.util.Locale; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.spi.NavigablePath; @@ -25,53 +24,14 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // TableReference handling - @Override - public TableReference resolveTableReference( - NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { - assert tableExpression != null; - - final TableReference tableReference = getTableReferenceInternal( - navigablePath, - tableExpression, - allowFkOptimization, - true - ); - - if ( tableReference == null ) { - throw new UnknownTableReferenceException( - tableExpression, - String.format( - Locale.ROOT, - "Unable to determine TableReference (`%s`) for `%s`", - tableExpression, - navigablePath - ) - ); - } - - return tableReference; - } - @Override public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, - boolean resolve) { - return getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); - } - - protected TableReference getTableReferenceInternal( - NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization, boolean resolve) { final TableReference primaryTableReference = getPrimaryTableReference().getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( primaryTableReference != null) { @@ -82,7 +42,6 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc final TableReference tableReference = tableJoin.getJoinedTableReference().getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( tableReference != null) { 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 index 9b7784178c..2e28f6dfa6 100644 --- 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 @@ -79,15 +79,13 @@ public class CollectionTableGroup extends StandardTableGroup implements PluralTa } @Override - protected TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { - final TableReference tableReference = super.getTableReferenceInternal( + final TableReference tableReference = super.getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( tableReference != null ) { @@ -97,7 +95,6 @@ public class CollectionTableGroup extends StandardTableGroup implements PluralTa final TableReference indexTableReference = indexTableGroup.getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( indexTableReference != null ) { @@ -108,7 +105,6 @@ public class CollectionTableGroup extends StandardTableGroup implements PluralTa final TableReference elementTableReference = elementTableGroup.getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( elementTableReference != 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 17dad55f93..450f5fe8b5 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 @@ -6,41 +6,89 @@ */ package org.hibernate.sql.ast.tree.from; +import java.util.Locale; + +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.spi.NavigablePath; /** * @author Steve Ebersole */ public interface ColumnReferenceQualifier { - default TableReference resolveTableReference(NavigablePath navigablePath, String tableExpression) { - return resolveTableReference( navigablePath, tableExpression, true ); - } default TableReference resolveTableReference(String tableExpression) { - return resolveTableReference( null, tableExpression, true ); + return resolveTableReference( null, tableExpression ); } /** - * Like {@link #getTableReference(NavigablePath, String, boolean, boolean)}, but will throw an exception if no + * Like {@link #getTableReference(NavigablePath, String, boolean)}, but will throw an exception if no * table reference can be found, even after resolving possible table reference joins. * * @param navigablePath The path for which to look up the table reference, may be null * @param tableExpression The table expression for which to look up the table reference - * @param allowFkOptimization Whether a foreign key optimization is allowed i.e. use the FK column on the key-side * * @throws UnknownTableReferenceException to indicate that the given tableExpression could not be resolved */ - TableReference resolveTableReference( + default TableReference resolveTableReference( NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization); + String tableExpression) { + assert tableExpression != null; + + final TableReference tableReference = getTableReference( + navigablePath, + tableExpression, + true + ); + + if ( tableReference == null ) { + throw new UnknownTableReferenceException( + tableExpression, + String.format( + Locale.ROOT, + "Unable to determine TableReference (`%s`) for `%s`", + tableExpression, + navigablePath + ) + ); + } + + return tableReference; + } + + default TableReference resolveTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression) { + assert modelPart != null; + + final TableReference tableReference = getTableReference( + navigablePath, + modelPart, + tableExpression, + true + ); + + if ( tableReference == null ) { + throw new UnknownTableReferenceException( + tableExpression, + String.format( + Locale.ROOT, + "Unable to determine TableReference (`%s`) for `%s`", + tableExpression, + navigablePath + ) + ); + } + + return tableReference; + } default TableReference getTableReference(NavigablePath navigablePath, String tableExpression) { - return getTableReference( navigablePath, tableExpression, true, false ); + return getTableReference( navigablePath, tableExpression, false ); } default TableReference getTableReference(String tableExpression) { - return getTableReference( null, tableExpression, true, false ); + return getTableReference( null, tableExpression, false ); } /** @@ -48,12 +96,17 @@ public interface ColumnReferenceQualifier { * * @param navigablePath The path for which to look up the table reference, may be null * @param tableExpression The table expression for which to look up the table reference - * @param allowFkOptimization Whether a foreign key optimization is allowed i.e. use the FK column on the key-side * @param resolve Whether to potentially create table reference joins for this table group */ TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve); + default TableReference getTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression, + boolean resolve) { + return getTableReference( navigablePath, tableExpression, resolve ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedPluralTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedPluralTableGroup.java index e99bcd8953..be28425b70 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedPluralTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/CorrelatedPluralTableGroup.java @@ -10,6 +10,7 @@ import java.util.function.Consumer; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.tree.predicate.Predicate; @@ -60,15 +61,53 @@ public class CorrelatedPluralTableGroup extends CorrelatedTableGroup implements } @Override - protected TableReference getTableReferenceInternal( + public TableReference getTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression, + boolean resolve) { + final TableReference tableReference = super.getTableReference( + navigablePath, + modelPart, + tableExpression, + resolve + ); + if ( tableReference != null ) { + return tableReference; + } + if ( indexTableGroup != null && ( navigablePath == null || indexTableGroup.getNavigablePath().isParent( navigablePath ) ) ) { + final TableReference indexTableReference = indexTableGroup.getTableReference( + navigablePath, + modelPart, + tableExpression, + resolve + ); + if ( indexTableReference != null ) { + return indexTableReference; + } + } + if ( elementTableGroup != null && ( navigablePath == null || elementTableGroup.getNavigablePath().isParent( navigablePath ) ) ) { + final TableReference elementTableReference = elementTableGroup.getTableReference( + navigablePath, + modelPart, + tableExpression, + resolve + ); + if ( elementTableReference != null ) { + return elementTableReference; + } + } + return null; + } + + @Override + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { - final TableReference tableReference = super.getTableReferenceInternal( + final TableReference tableReference = super.getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( tableReference != null ) { @@ -78,7 +117,6 @@ public class CorrelatedPluralTableGroup extends CorrelatedTableGroup implements final TableReference indexTableReference = indexTableGroup.getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( indexTableReference != null ) { @@ -89,7 +127,6 @@ public class CorrelatedPluralTableGroup extends CorrelatedTableGroup implements final TableReference elementTableReference = elementTableGroup.getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( elementTableReference != null ) { 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 96d2e1c564..267d28c0c0 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 @@ -11,6 +11,7 @@ import java.util.List; import java.util.function.Consumer; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.spi.SqlAliasBase; @@ -71,15 +72,15 @@ public class CorrelatedTableGroup extends AbstractTableGroup { } @Override - protected TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, + ValuedModelPart modelPart, String tableExpression, - boolean allowFkOptimization, boolean resolve) { final TableReference tableReference = correlatedTableGroup.getTableReference( navigablePath, + modelPart, tableExpression, - allowFkOptimization, resolve ); if ( tableReference != null ) { @@ -88,7 +89,7 @@ public class CorrelatedTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, modelPart, tableExpression, resolve ); if ( groupTableReference != null ) { return groupTableReference; } @@ -96,7 +97,39 @@ public class CorrelatedTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, modelPart, tableExpression, resolve ); + if ( groupTableReference != null ) { + return groupTableReference; + } + } + return null; + } + + @Override + public TableReference getTableReference( + NavigablePath navigablePath, + String tableExpression, + boolean resolve) { + final TableReference tableReference = correlatedTableGroup.getTableReference( + navigablePath, + tableExpression, + resolve + ); + if ( tableReference != null ) { + return tableReference; + } + for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { + final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() + .getPrimaryTableReference() + .getTableReference( navigablePath, tableExpression, resolve ); + if ( groupTableReference != null ) { + return groupTableReference; + } + } + for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { + final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() + .getPrimaryTableReference() + .getTableReference( navigablePath, tableExpression, resolve ); if ( groupTableReference != null ) { return groupTableReference; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java index b9c6a041ac..081d4b717b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DelegatingTableGroup.java @@ -11,6 +11,7 @@ import java.util.function.Consumer; import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPartContainer; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.spi.SqlSelection; @@ -58,40 +59,20 @@ public abstract class DelegatingTableGroup implements TableGroup { } @Override - public TableReference resolveTableReference(NavigablePath navigablePath, String tableExpression) { - return resolveTableReference( navigablePath, tableExpression, true ); - } - - @Override - public TableReference resolveTableReference(String tableExpression) { - return resolveTableReference( null, tableExpression, true ); - } - - @Override - public TableReference resolveTableReference( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { - return getTableGroup().resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); - } - - @Override - public TableReference getTableReference(NavigablePath navigablePath, String tableExpression) { - return getTableReference( navigablePath, tableExpression, true, false ); - } - - @Override - public TableReference getTableReference(String tableExpression) { - return getTableReference( null, tableExpression, true, false ); + boolean resolve) { + return getTableGroup().getTableReference( navigablePath, tableExpression, resolve ); } @Override public TableReference getTableReference( NavigablePath navigablePath, + ValuedModelPart modelPart, String tableExpression, - boolean allowFkOptimization, boolean resolve) { - return getTableGroup().getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + return getTableGroup().getTableReference( navigablePath, modelPart, tableExpression, resolve ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DerivedTableReference.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DerivedTableReference.java index 3cd23f1c2c..92e31e31e9 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DerivedTableReference.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/DerivedTableReference.java @@ -9,6 +9,7 @@ package org.hibernate.sql.ast.tree.from; import java.util.List; import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.spi.NavigablePath; /** @@ -45,8 +46,18 @@ public abstract class DerivedTableReference extends AbstractTableReference { @Override public TableReference resolveTableReference( NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { + String tableExpression) { + throw new UnknownTableReferenceException( + tableExpression, + "TableReferences cannot be resolved relative to DerivedTableReferences - `" + tableExpression + "` : " + navigablePath + ); + } + + @Override + public TableReference resolveTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression) { throw new UnknownTableReferenceException( tableExpression, "TableReferences cannot be resolved relative to DerivedTableReferences - `" + tableExpression + "` : " + navigablePath @@ -57,7 +68,6 @@ public abstract class DerivedTableReference extends AbstractTableReference { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { return null; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FunctionTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FunctionTableGroup.java index 6d5dca0071..9ebb6a0581 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FunctionTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/FunctionTableGroup.java @@ -55,10 +55,9 @@ public class FunctionTableGroup extends AbstractTableGroup { } @Override - protected TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { if ( tableExpression == null ) { return getPrimaryTableReference(); @@ -66,7 +65,7 @@ public class FunctionTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, tableExpression, resolve ); if ( groupTableReference != null ) { return groupTableReference; } @@ -74,7 +73,7 @@ public class FunctionTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, tableExpression, resolve ); if ( groupTableReference != null ) { return groupTableReference; } 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 42994befba..a5d3569936 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 @@ -9,13 +9,12 @@ package org.hibernate.sql.ast.tree.from; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Locale; -import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Supplier; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.metamodel.mapping.ModelPart; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.spi.SqlAliasBase; @@ -37,7 +36,7 @@ public class LazyTableGroup extends DelegatingTableGroup { private final SqlAliasBase sqlAliasBase; private final Supplier tableGroupSupplier; private final TableGroup parentTableGroup; - private final BiPredicate navigablePathChecker; + private final ParentTableGroupUseChecker parentTableGroupUseChecker; private List tableGroupJoins; private List nestedTableGroupJoins; private Consumer tableGroupConsumer; @@ -48,7 +47,7 @@ public class LazyTableGroup extends DelegatingTableGroup { NavigablePath navigablePath, boolean fetched, Supplier tableGroupSupplier, - BiPredicate navigablePathChecker, + ParentTableGroupUseChecker parentTableGroupUseChecker, TableGroupProducer tableGroupProducer, String sourceAlias, SqlAliasBase sqlAliasBase, @@ -61,7 +60,7 @@ public class LazyTableGroup extends DelegatingTableGroup { this.sourceAlias = sourceAlias; this.sqlAliasBase = sqlAliasBase; this.tableGroupSupplier = tableGroupSupplier; - this.navigablePathChecker = navigablePathChecker; + this.parentTableGroupUseChecker = parentTableGroupUseChecker; this.parentTableGroup = parentTableGroup; } @@ -237,60 +236,34 @@ public class LazyTableGroup extends DelegatingTableGroup { } @Override - public TableReference resolveTableReference( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization) { - assert tableExpression != null; - - final TableReference tableReference = getTableReferenceInternal( - navigablePath, - tableExpression, - allowFkOptimization, - true - ); - - if ( tableReference == null ) { - throw new UnknownTableReferenceException( - tableExpression, - String.format( - Locale.ROOT, - "Unable to determine TableReference (`%s`) for `%s`", - tableExpression, - navigablePath - ) - ); - } - - return tableReference; + boolean resolve) { + return getTableGroup().getTableReference( navigablePath, tableExpression, resolve ); } @Override public TableReference getTableReference( NavigablePath navigablePath, + ValuedModelPart modelPart, String tableExpression, - boolean allowFkOptimization, boolean resolve) { - return getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); - } - - protected TableReference getTableReferenceInternal( - NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization, - boolean resolve) { - if ( allowFkOptimization && ( navigablePath == null || navigablePathChecker.test( navigablePath, tableExpression ) ) ) { + if ( parentTableGroupUseChecker.canUseParentTableGroup( producer, navigablePath, modelPart ) ) { final TableReference reference = parentTableGroup.getTableReference( navigablePath, + (ValuedModelPart) producer, tableExpression, - allowFkOptimization, resolve ); if ( reference != null ) { return reference; } } - return getTableGroup().getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + return getTableGroup().getTableReference( navigablePath, modelPart, tableExpression, resolve ); } + public static interface ParentTableGroupUseChecker { + boolean canUseParentTableGroup(TableGroupProducer producer, NavigablePath navigablePath, ValuedModelPart valuedModelPart); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java index b7a1b290fe..18beaed17b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/MappedByTableGroup.java @@ -9,10 +9,10 @@ package org.hibernate.sql.ast.tree.from; import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.function.BiPredicate; import java.util.function.Consumer; import org.hibernate.metamodel.mapping.ModelPartContainer; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.spi.NavigablePath; /** @@ -21,25 +21,25 @@ import org.hibernate.spi.NavigablePath; public class MappedByTableGroup extends DelegatingTableGroup implements VirtualTableGroup { private final NavigablePath navigablePath; - private final ModelPartContainer modelPart; + private final TableGroupProducer producer; private final TableGroup underlyingTableGroup; private final boolean fetched; private final TableGroup parentTableGroup; - private final BiPredicate navigablePathChecker; + private final LazyTableGroup.ParentTableGroupUseChecker parentTableGroupUseChecker; public MappedByTableGroup( NavigablePath navigablePath, - ModelPartContainer modelPart, + TableGroupProducer producer, TableGroup underlyingTableGroup, boolean fetched, TableGroup parentTableGroup, - BiPredicate navigablePathChecker) { + LazyTableGroup.ParentTableGroupUseChecker parentTableGroupUseChecker) { this.navigablePath = navigablePath; - this.modelPart = modelPart; + this.producer = producer; this.underlyingTableGroup = underlyingTableGroup; this.fetched = fetched; this.parentTableGroup = parentTableGroup; - this.navigablePathChecker = navigablePathChecker; + this.parentTableGroupUseChecker = parentTableGroupUseChecker; } @Override @@ -75,7 +75,7 @@ public class MappedByTableGroup extends DelegatingTableGroup implements VirtualT @Override public ModelPartContainer getModelPart() { - return modelPart; + return producer; } // Don't provide access to table group joins as this is table group is just a "named reference" @@ -119,12 +119,39 @@ public class MappedByTableGroup extends DelegatingTableGroup implements VirtualT @Override public TableReference resolveTableReference( NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { + String tableExpression) { final TableReference tableReference = getTableReference( navigablePath, tableExpression, - allowFkOptimization, + true + ); + + if ( tableReference == null ) { + throw new UnknownTableReferenceException( + tableExpression, + String.format( + Locale.ROOT, + "Unable to determine TableReference (`%s`) for `%s`", + tableExpression, + navigablePath + ) + ); + } + + return tableReference; + } + + @Override + public TableReference resolveTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression) { + assert modelPart != null; + + final TableReference tableReference = getTableReference( + navigablePath, + modelPart, + tableExpression, true ); @@ -147,25 +174,31 @@ public class MappedByTableGroup extends DelegatingTableGroup implements VirtualT public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { - if ( allowFkOptimization && ( navigablePath == null || navigablePathChecker.test( navigablePath, tableExpression ) ) ) { + return getTableGroup().getTableReference( + navigablePath, + tableExpression, + resolve + ); + } + + @Override + public TableReference getTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression, + boolean resolve) { + if ( parentTableGroupUseChecker.canUseParentTableGroup( producer, navigablePath, modelPart ) ) { final TableReference reference = parentTableGroup.getTableReference( navigablePath, + (ValuedModelPart) producer, tableExpression, - allowFkOptimization, resolve ); if ( reference != null ) { return reference; } } - - return underlyingTableGroup.getTableReference( - navigablePath, - tableExpression, - allowFkOptimization, - resolve - ); + return getTableGroup().getTableReference( navigablePath, modelPart, tableExpression, 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 fe7b089df5..efbaeeeda9 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,21 +63,12 @@ public class MutatingTableReferenceGroupWrapper implements TableGroup { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { return mutatingTableReference.getTableExpression().equals( tableExpression ) ? mutatingTableReference : null; } - @Override - public TableReference resolveTableReference( - NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { - return getTableReference( navigablePath, tableExpression, allowFkOptimization, true ); - } - @Override public void applyAffectedTableNames(Consumer nameCollector) { nameCollector.accept( mutatingTableReference.getTableExpression() ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java index 87fe6653e8..33e4dc2f62 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/NamedTableReference.java @@ -83,8 +83,7 @@ public class NamedTableReference extends AbstractTableReference { @Override public TableReference resolveTableReference( NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { + String tableExpression) { if ( tableExpression.equals( getTableExpression() ) ) { return this; } @@ -104,12 +103,8 @@ public class NamedTableReference extends AbstractTableReference { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { - if ( this.tableExpression.equals( tableExpression ) ) { - return this; - } - return null; + return this.tableExpression.equals( tableExpression ) ? this : null; } @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 index e116718f6c..45b250520c 100644 --- 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 @@ -165,15 +165,13 @@ public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implem } @Override - protected TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { final TableReference tableReference = elementTableGroup.getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( tableReference != null || indexTableGroup == null @@ -184,7 +182,6 @@ public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implem return indexTableGroup.getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/QueryPartTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/QueryPartTableGroup.java index 6b6f141212..ab60144691 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/QueryPartTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/QueryPartTableGroup.java @@ -8,13 +8,11 @@ package org.hibernate.sql.ast.tree.from; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.Set; import java.util.function.Consumer; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.spi.NavigablePath; -import org.hibernate.sql.ast.tree.select.QueryPart; import org.hibernate.sql.ast.tree.select.SelectStatement; /** @@ -83,10 +81,9 @@ public class QueryPartTableGroup extends AbstractTableGroup { } @Override - protected TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { if ( compatibleTableExpressions.contains( tableExpression ) ) { return getPrimaryTableReference(); @@ -94,7 +91,7 @@ public class QueryPartTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, tableExpression, resolve ); if ( groupTableReference != null ) { return groupTableReference; } @@ -102,7 +99,7 @@ public class QueryPartTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, tableExpression, resolve ); if ( groupTableReference != null ) { return groupTableReference; } 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 795b41b71f..a13cd574e4 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,15 +131,13 @@ public class StandardTableGroup extends AbstractTableGroup { } @Override - protected TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { final TableReference tableReference = primaryTableReference.getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( tableReference != null ) { @@ -152,7 +150,7 @@ public class StandardTableGroup extends AbstractTableGroup { final TableReferenceJoin join = tableJoins.get( i ); assert join != null; final TableReference resolveTableReference = join.getJoinedTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, tableExpression, resolve ); if ( resolveTableReference != null ) { return resolveTableReference; } @@ -164,13 +162,13 @@ public class StandardTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); - if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { + if ( primaryTableReference.getTableReference( navigablePath, tableExpression, resolve ) != null ) { return primaryTableReference; } } for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); - if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { + if ( primaryTableReference.getTableReference( navigablePath, tableExpression, resolve ) != null ) { return primaryTableReference; } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardVirtualTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardVirtualTableGroup.java index a4f75c7ffe..9e5fb00f0b 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardVirtualTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/StandardVirtualTableGroup.java @@ -9,7 +9,11 @@ package org.hibernate.sql.ast.tree.from; import java.util.List; import java.util.function.Consumer; +import org.hibernate.metamodel.mapping.EmbeddableMappingType; +import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.ModelPartContainer; +import org.hibernate.metamodel.mapping.OwnedValuedModelPart; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.spi.NavigablePath; /** @@ -105,21 +109,68 @@ public class StandardVirtualTableGroup extends AbstractTableGroup implements Vir } @Override - public TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { final TableReference tableReference = underlyingTableGroup.getTableReference( navigablePath, tableExpression, - allowFkOptimization, resolve ); if ( tableReference != null ) { return tableReference; } - return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); + + for ( TableReferenceJoin tableJoin : getTableReferenceJoins() ) { + final TableReference joinedTableReference = tableJoin.getJoinedTableReference().getTableReference( + navigablePath, + tableExpression, + resolve + ); + if ( joinedTableReference != null) { + return joinedTableReference; + } + } + return null; } + @Override + public TableReference getTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression, + boolean resolve) { + final ValuedModelPart parentModelPart; + final MappingType declaringType; + if ( modelPart instanceof OwnedValuedModelPart + && ( declaringType = ( (OwnedValuedModelPart) modelPart ).getDeclaringType() ) instanceof EmbeddableMappingType ) { + parentModelPart = ( (EmbeddableMappingType) declaringType ).getEmbeddedValueMapping(); + } + else { + parentModelPart = modelPart; + } + final TableReference tableReference = underlyingTableGroup.getTableReference( + navigablePath, + parentModelPart, + tableExpression, + resolve + ); + if ( tableReference != null ) { + return tableReference; + } + + for ( TableReferenceJoin tableJoin : getTableReferenceJoins() ) { + final TableReference joinedTableReference = tableJoin.getJoinedTableReference().getTableReference( + navigablePath, + modelPart, + tableExpression, + resolve + ); + if ( joinedTableReference != null) { + return joinedTableReference; + } + } + return null; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java index 6e13721aee..cb39a614a2 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroup.java @@ -15,6 +15,7 @@ import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.spi.NavigablePath; import org.hibernate.query.sqm.sql.internal.DomainResultProducer; import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; +import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.results.graph.DomainResult; @@ -159,4 +160,37 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat default boolean isInitialized() { return true; } + + default TableGroup findCompatibleJoinedGroup( + TableGroupJoinProducer joinProducer, + SqlAstJoinType requestedJoinType) { + // We don't look into nested table group joins as that wouldn't be "compatible" + for ( TableGroupJoin join : getTableGroupJoins() ) { + // Compatibility obviously requires the same model part but also join type compatibility + // Note that if the requested join type is left, we can also use an existing inner join + // The other case, when the requested join type is inner and there is an existing left join, + // is not compatible though because the cardinality is different. + // We could reuse the join though if we alter the join type to INNER, but that's an optimization for later + final SqlAstJoinType joinType = join.getJoinType(); + if ( join.getJoinedGroup().getModelPart() == joinProducer + && ( requestedJoinType == joinType || requestedJoinType == SqlAstJoinType.LEFT && joinType == SqlAstJoinType.INNER ) ) { + // If there is an existing inner join, we can always use that as a new join can never produce results + // regardless of the join type or predicate since the LHS is the same table group + // If this is a left join though, we have to check if the predicate is simply the association predicate + if ( joinType == SqlAstJoinType.INNER || joinProducer.isSimpleJoinPredicate( join.getPredicate() ) ) { + return join.getJoinedGroup(); + } + } + } + return null; + } + + default TableGroupJoin findTableGroupJoin(TableGroup tableGroup) { + for ( TableGroupJoin join : getTableGroupJoins() ) { + if ( join.getJoinedGroup() == tableGroup ) { + return join; + } + } + return null; + } } 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 f487880b7b..40b9130e66 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 @@ -65,13 +65,11 @@ public interface TableReference extends SqlAstNode, ColumnReferenceQualifier { @Override TableReference resolveTableReference( NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization); + String tableExpression); @Override TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve); } 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 916583fd0a..8d8cd89920 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 @@ -44,14 +44,13 @@ public class UnionTableGroup extends AbstractTableGroup { } @Override - public TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { - if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { + if ( tableReference.getTableReference( navigablePath, tableExpression, resolve ) != null ) { return tableReference; } - return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); + return super.getTableReference( navigablePath, tableExpression, resolve ); } } 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 bb691d1be5..d38bc05f24 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 @@ -39,8 +39,7 @@ public class UnionTableReference extends NamedTableReference { @Override public TableReference resolveTableReference( NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { + String tableExpression) { if ( hasTableExpression( tableExpression ) ) { return this; } @@ -60,7 +59,6 @@ public class UnionTableReference extends NamedTableReference { public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { if ( hasTableExpression( tableExpression ) ) { return this; diff --git a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ValuesTableGroup.java b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ValuesTableGroup.java index 534e33f912..a681438914 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ValuesTableGroup.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/ValuesTableGroup.java @@ -43,10 +43,9 @@ public class ValuesTableGroup extends AbstractTableGroup { } @Override - protected TableReference getTableReferenceInternal( + public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { if ( ( (TableGroupProducer) getModelPart() ).containsTableReference( tableExpression ) ) { return getPrimaryTableReference(); @@ -54,7 +53,7 @@ public class ValuesTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, tableExpression, resolve ); if ( groupTableReference != null ) { return groupTableReference; } @@ -62,7 +61,7 @@ public class ValuesTableGroup extends AbstractTableGroup { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() .getPrimaryTableReference() - .getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + .getTableReference( navigablePath, tableExpression, resolve ); if ( groupTableReference != null ) { return groupTableReference; } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/MutatingTableReference.java b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/MutatingTableReference.java index cb1ca9c0f3..8eeff60d2e 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/model/ast/MutatingTableReference.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/model/ast/MutatingTableReference.java @@ -10,6 +10,7 @@ import java.util.Locale; import java.util.Objects; import java.util.function.Function; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.tree.from.TableReference; @@ -61,7 +62,9 @@ public class MutatingTableReference implements TableReference { } @Override - public TableReference resolveTableReference(NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization) { + public TableReference resolveTableReference( + NavigablePath navigablePath, + String tableExpression) { if ( getTableName().equals( tableExpression ) ) { return this; } @@ -77,8 +80,36 @@ public class MutatingTableReference implements TableReference { } @Override - public TableReference getTableReference(NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization, boolean resolve) { - return resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); + public TableReference resolveTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression) { + if ( getTableName().equals( tableExpression ) ) { + return this; + } + + throw new IllegalArgumentException( + String.format( + Locale.ROOT, + "Table-expression (%s) did not match mutating table name - %s", + tableExpression, + getTableName() + ) + ); + } + + @Override + public TableReference getTableReference(NavigablePath navigablePath, String tableExpression, boolean resolve) { + return getTableName().equals( tableExpression ) ? this : null; + } + + @Override + public TableReference getTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression, + boolean resolve) { + return getTableName().equals( tableExpression ) ? this : null; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DatabaseSnapshotContributor.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DatabaseSnapshotContributor.java index 0cd457d3f4..17db5edf22 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DatabaseSnapshotContributor.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DatabaseSnapshotContributor.java @@ -24,9 +24,9 @@ public interface DatabaseSnapshotContributor extends Fetchable { */ default DomainResult createSnapshotDomainResult( NavigablePath navigablePath, - TableGroup tableGroup, + TableGroup parentTableGroup, String resultVariable, DomainResultCreationState creationState) { - return createDomainResult( navigablePath, tableGroup, null, creationState ); + return createDomainResult( navigablePath, parentTableGroup, null, creationState ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultCreationState.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultCreationState.java index 5b0691b383..07aa81158d 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultCreationState.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/DomainResultCreationState.java @@ -11,9 +11,12 @@ import java.util.List; import org.hibernate.Incubating; import org.hibernate.engine.FetchTiming; import org.hibernate.metamodel.mapping.AssociationKey; +import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; +import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; 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.spi.EntityIdentifierNavigablePath; @@ -88,8 +91,7 @@ public interface DomainResultCreationState { ModelPart resolveModelPart(NavigablePath navigablePath); default Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) { - final EntityIdentifierMapping identifierMapping = fetchParent.getEntityValuedModelPart() - .getEntityMappingType() + final EntityIdentifierMapping identifierMapping = fetchParent.getReferencedMappingContainer() .getIdentifierMapping(); return fetchParent.generateFetchableFetch( (Fetchable) identifierMapping, 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 6e157d83ab..eda07c1e74 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 @@ -37,7 +37,7 @@ public interface FetchParent extends DomainResultGraphNode { default NavigablePath resolveNavigablePath(Fetchable fetchable) { final String fetchableName = fetchable.getFetchableName(); - if ( NavigablePath.IDENTIFIER_MAPPER_PROPERTY.equals( fetchableName ) || fetchable instanceof EntityIdentifierMapping ) { + if ( fetchable instanceof EntityIdentifierMapping ) { return new EntityIdentifierNavigablePath( getNavigablePath(), fetchableName ); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/Fetchable.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/Fetchable.java index 6597eafa40..5b6d6ec71f 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/Fetchable.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/Fetchable.java @@ -92,7 +92,7 @@ public interface Fetchable extends ModelPart { default boolean isSelectable() { final AttributeMapping attributeMapping = asAttributeMapping(); - if ( attributeMapping != null ) { + if ( attributeMapping != null && attributeMapping.getAttributeMetadata() != null ) { return attributeMapping.getAttributeMetadata().isSelectable(); } else { diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java index bbf1705d3e..ae653015bf 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/CollectionDomainResult.java @@ -12,6 +12,7 @@ import java.util.List; import org.hibernate.LockMode; import org.hibernate.collection.spi.CollectionInitializerProducer; import org.hibernate.collection.spi.CollectionSemantics; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -53,10 +54,11 @@ public class CollectionDomainResult implements DomainResult, CollectionResultGra this.loadingAttribute = loadingAttribute; this.resultVariable = resultVariable; this.tableGroup = tableGroup; - - fkResult = loadingAttribute.getKeyDescriptor().createKeyDomainResult( + // The collection is always the target side + this.fkResult = loadingAttribute.getKeyDescriptor().createKeyDomainResult( loadingPath, tableGroup, + ForeignKeyDescriptor.Nature.TARGET, this, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EagerCollectionFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EagerCollectionFetch.java index 577165e9c5..03a8115c50 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EagerCollectionFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/collection/internal/EagerCollectionFetch.java @@ -99,9 +99,11 @@ public class EagerCollectionFetch extends CollectionFetch implements FetchParent fetchParent, creationState ); + // The collection is always the target side collectionValueKeyResult = keyDescriptor.createKeyDomainResult( fetchedPath, collectionTableGroup, + ForeignKeyDescriptor.Nature.TARGET, fetchParent, creationState ); 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 099c4d3c2e..d8b43c3ca7 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 @@ -234,10 +234,7 @@ public abstract class AbstractEmbeddableInitializer extends AbstractFetchParentA // Virtual model parts use the owning entity as container which the fetch parent access provides. // For an identifier or foreign key this is called during the resolveKey phase of the fetch parent, // so we can't use the fetch parent access in that case. - if ( fetchParentAccess != null && embedded instanceof VirtualModelPart - && !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() ) - && !ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() ) - && !ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getLocalName() ) ) { + if ( fetchParentAccess != null && embedded instanceof VirtualModelPart && !isPartOfKey ) { fetchParentAccess.resolveInstance( processingState ); compositeInstance = fetchParentAccess.getInitializedInstance(); EntityInitializer entityInitializer = fetchParentAccess.asEntityInitializer(); 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 1a7045d77d..6f6e79b03b 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 @@ -6,15 +6,10 @@ */ package org.hibernate.sql.results.graph.entity; -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.ManagedMappingType; -import org.hibernate.metamodel.mapping.MappingType; -import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.AbstractEntityPersister; -import org.hibernate.spi.EntityIdentifierNavigablePath; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.results.graph.AbstractFetchParent; @@ -25,47 +20,37 @@ import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.basic.BasicFetch; import org.hibernate.type.descriptor.java.JavaType; -import static org.hibernate.query.results.ResultsHelper.attributeName; - /** * AbstractFetchParent sub-class for entity-valued graph nodes * * @author Steve Ebersole */ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent implements EntityResultGraphNode { - private final EntityValuedModelPart referencedModelPart; private Fetch identifierFetch; private BasicFetch discriminatorFetch; private DomainResult rowIdResult; public AbstractEntityResultGraphNode(EntityValuedModelPart referencedModelPart, NavigablePath navigablePath) { - super( referencedModelPart.getEntityMappingType(), navigablePath ); - this.referencedModelPart = referencedModelPart; + super( referencedModelPart, navigablePath ); } @Override public void afterInitialize(FetchParent fetchParent, DomainResultCreationState creationState) { - final EntityMappingType entityDescriptor = referencedModelPart.getEntityMappingType(); - final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping(); final NavigablePath navigablePath = getNavigablePath(); final TableGroup entityTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess() .getTableGroup( navigablePath ); + final EntityResultGraphNode entityResultGraphNode = (EntityResultGraphNode) fetchParent; if ( navigablePath.getParent() == null && !creationState.forceIdentifierSelection() ) { identifierFetch = null; - visitIdentifierMapping( - new EntityIdentifierNavigablePath( navigablePath, attributeName( identifierMapping ) ), - creationState, - identifierMapping, - entityTableGroup - ); + creationState.visitIdentifierFetch( entityResultGraphNode ); } else { - identifierFetch = creationState.visitIdentifierFetch( this ); + identifierFetch = creationState.visitIdentifierFetch( entityResultGraphNode ); } - discriminatorFetch = creationState.visitDiscriminatorFetch( this ); + discriminatorFetch = creationState.visitDiscriminatorFetch( entityResultGraphNode ); - final EntityRowIdMapping rowIdMapping = entityDescriptor.getRowIdMapping(); + final EntityRowIdMapping rowIdMapping = getEntityValuedModelPart().getEntityMappingType().getRowIdMapping(); if ( rowIdMapping == null ) { rowIdResult = null; } @@ -80,45 +65,6 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent super.afterInitialize( fetchParent, creationState ); } - private void visitIdentifierMapping( - EntityIdentifierNavigablePath navigablePath, - DomainResultCreationState creationState, - EntityIdentifierMapping identifierMapping, - TableGroup entityTableGroup) { - final MappingType mappingType = identifierMapping.getPartMappingType(); - if ( mappingType instanceof ManagedMappingType ) { - ( (ManagedMappingType) mappingType ).forEachAttributeMapping( - attributeMapping -> { - if ( attributeMapping instanceof ToOneAttributeMapping ) { - ( (ToOneAttributeMapping) attributeMapping ).getForeignKeyDescriptor() - .createKeyDomainResult( - navigablePath.getParent(), - entityTableGroup, - this, - creationState - ); - } - else { - attributeMapping.createDomainResult( - navigablePath, - entityTableGroup, - null, - creationState - ); - } - } - ); - } - else { - identifierMapping.createDomainResult( - navigablePath, - entityTableGroup, - null, - creationState - ); - } - } - @Override public EntityMappingType getReferencedMappingContainer() { return getEntityValuedModelPart().getEntityMappingType(); @@ -126,7 +72,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent @Override public EntityValuedModelPart getEntityValuedModelPart() { - return referencedModelPart; + return (EntityValuedModelPart) getFetchContainer(); } @Override @@ -145,4 +91,5 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent public DomainResult getRowIdResult() { return rowIdResult; } + } diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractNonLazyEntityFetch.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractNonLazyEntityFetch.java index d8ed2c061a..3f40708279 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractNonLazyEntityFetch.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/AbstractNonLazyEntityFetch.java @@ -22,20 +22,18 @@ import org.hibernate.sql.results.graph.entity.internal.EntityAssembler; */ public abstract class AbstractNonLazyEntityFetch extends AbstractFetchParent implements EntityFetch { private final FetchParent fetchParent; - private final EntityValuedFetchable referencedModelPart; public AbstractNonLazyEntityFetch( FetchParent fetchParent, EntityValuedFetchable fetchedPart, NavigablePath navigablePath) { - super( fetchedPart.getEntityMappingType(), navigablePath ); - this.referencedModelPart = fetchedPart; + super( fetchedPart, navigablePath ); this.fetchParent = fetchParent; } @Override public EntityValuedFetchable getEntityValuedModelPart() { - return referencedModelPart; + return (EntityValuedFetchable) getFetchContainer(); } @Override 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 a8d7eb3266..c195862f52 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 @@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph.entity.internal; import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping; +import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.spi.NavigablePath; import org.hibernate.sql.ast.tree.from.TableGroup; @@ -33,13 +34,14 @@ public class EntityDelayedResultImpl implements DomainResult { public EntityDelayedResultImpl( NavigablePath navigablePath, EntityAssociationMapping entityValuedModelPart, - TableGroup rootTableGroup, + TableGroup targetTableGroup, DomainResultCreationState creationState) { this.navigablePath = navigablePath; this.entityValuedModelPart = entityValuedModelPart; this.identifierResult = entityValuedModelPart.getForeignKeyDescriptor().createKeyDomainResult( navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), - rootTableGroup, + targetTableGroup, + entityValuedModelPart.getSideNature(), null, creationState ); diff --git a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/NotFoundSnapshotResult.java b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/NotFoundSnapshotResult.java index 970792ae4d..8491cf0ef8 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/NotFoundSnapshotResult.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/results/graph/entity/internal/NotFoundSnapshotResult.java @@ -44,7 +44,12 @@ public class NotFoundSnapshotResult implements DomainResult { // however, that would mean a 1-1 with a join-table which // is pretty odd mapping final ForeignKeyDescriptor fkDescriptor = toOneMapping.getForeignKeyDescriptor(); - this.keyResult = fkDescriptor.createKeyDomainResult( navigablePath, keyTableGroup, null, creationState ); + this.keyResult = fkDescriptor.createKeyDomainResult( + navigablePath, + targetTableGroup, + null, + creationState + ); this.targetResult = fkDescriptor.createTargetDomainResult( navigablePath, targetTableGroup, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/CardField.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/CardField.java index 10e5774d82..2063b24e0e 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/CardField.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/CardField.java @@ -20,6 +20,7 @@ public class CardField implements Serializable { @EmbeddedId private PrimaryKey primaryKey; + private String name; CardField(Card card, Key key) { this.primaryKey = new PrimaryKey( card, key); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/Key.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/Key.java index 3a6016e2d0..78d3391e8f 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/Key.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/cid/keymanytoone/association/Key.java @@ -21,6 +21,7 @@ import jakarta.persistence.Table; public class Key implements Serializable { @Id private String id; + private String name; public Key(String id) { this.id = id; diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/embedded/EmbeddableWithManyToOneSelfReferenceTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/embedded/EmbeddableWithManyToOneSelfReferenceTest.java new file mode 100644 index 0000000000..e56c66f701 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/embedded/EmbeddableWithManyToOneSelfReferenceTest.java @@ -0,0 +1,223 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.annotations.embedded; + +import org.hibernate.testing.jdbc.SQLStatementInspector; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Embedded; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinColumns; +import jakarta.persistence.ManyToOne; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +@DomainModel( + annotatedClasses = { + EmbeddableWithManyToOneSelfReferenceTest.EntityTest.class, + EmbeddableWithManyToOneSelfReferenceTest.IntIdEntity.class + } +) +@SessionFactory(useCollectingStatementInspector = true) +public class EmbeddableWithManyToOneSelfReferenceTest { + + @BeforeEach + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + IntIdEntity intIdEntity = new IntIdEntity( 1 ); + + EntityTest entity1 = new EntityTest( "1", intIdEntity ); + EmbeddableTest embeddable1 = new EmbeddableTest(); + embeddable1.setName( "E1" ); + + entity1.setEmbeddedAttribute( embeddable1 ); + + EntityTest entity2 = new EntityTest( "2", intIdEntity ); + + EmbeddableTest embeddable2 = new EmbeddableTest(); + embeddable2.setAssociation( entity1 ); + embeddable2.setName( "E2" ); + + entity2.setEmbeddedAttribute( embeddable2 ); + + session.persist( intIdEntity ); + session.persist( entity1 ); + session.persist( entity2 ); + } + ); + } + + @AfterEach + public void tearDown(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + session.createMutationQuery( "delete from EntityTest e where e.embeddedAttribute.association is not null" ).executeUpdate(); + session.createMutationQuery( "delete from EntityTest e" ).executeUpdate(); + session.createMutationQuery( "delete from IntIdEntity e" ).executeUpdate(); + } + ); + } + + @Test + public void testGet(SessionFactoryScope scope) { + SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); + statementInspector.clear(); + scope.inTransaction( + session -> { + EntityTest entity1 = session.find( EntityTest.class, new EntityTestId( "1", session.getReference( IntIdEntity.class, 1 ) ) ); + assertNotNull( entity1.getEmbeddedAttribute() ); + assertNull( entity1.getEmbeddedAttribute().getAssociation() ); + assertEquals( "E1", entity1.getEmbeddedAttribute().getName() ); + + EntityTest entity2 = session.find( EntityTest.class, new EntityTestId( "2", session.getReference( IntIdEntity.class, 1 ) ) ); + assertNotNull( entity2.getEmbeddedAttribute() ); + assertNotNull( entity2.getEmbeddedAttribute().getAssociation() ); + assertEquals( entity1, entity2.getEmbeddedAttribute().getAssociation() ); + assertEquals( "E2", entity2.getEmbeddedAttribute().getName() ); + } + ); + } + + @Entity(name = "EntityTest") + public static class EntityTest { + @EmbeddedId + private EntityTestId id; + + @Embedded + private EmbeddableTest embeddedAttribute; + + public EntityTest() { + } + + public EntityTest(String string, IntIdEntity intIdEntity) { + this.id = new EntityTestId(string, intIdEntity); + } + + public EntityTestId getId() { + return id; + } + + public void setId(EntityTestId id) { + this.id = id; + } + + public EmbeddableTest getEmbeddedAttribute() { + return embeddedAttribute; + } + + public void setEmbeddedAttribute(EmbeddableTest embeddedAttribute) { + this.embeddedAttribute = embeddedAttribute; + } + } + + @Embeddable + public static class EntityTestId { + @Column(name = "string_key", length = 10) + private String stringKey; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "intIdEntity_id") + private IntIdEntity intIdEntity; + + public EntityTestId() { + } + + public EntityTestId(String stringKey, IntIdEntity intIdEntity) { + this.stringKey = stringKey; + this.intIdEntity = intIdEntity; + } + + public String getStringKey() { + return stringKey; + } + + public void setStringKey(String stringField) { + this.stringKey = stringField; + } + + public IntIdEntity getIntIdEntity() { + return intIdEntity; + } + + public void setIntIdEntity(IntIdEntity entity) { + this.intIdEntity = entity; + } + } + + @Embeddable + public static class EmbeddableTest { + private String name; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumns({ + @JoinColumn(name = "assoc_string_key", referencedColumnName = "string_key"), + @JoinColumn(name = "assoc_intIdEntity_id", referencedColumnName = "intIdEntity_id") + }) + private EntityTest association; + + public String getName() { + return name; + } + + public void setName(String stringField) { + this.name = stringField; + } + + public EntityTest getAssociation() { + return association; + } + + public void setAssociation(EntityTest entity) { + this.association = entity; + } + } + + @Entity(name = "IntIdEntity") + public static class IntIdEntity { + @Id + private Integer id; + + private String name; + + public IntIdEntity() { + } + + public IntIdEntity(int id) { + this.id = id; + } + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/LazyManyToOneEmbeddedIdWithToOneFKTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/LazyManyToOneEmbeddedIdWithToOneFKTest.java index e20e114450..bb1412d904 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/LazyManyToOneEmbeddedIdWithToOneFKTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/LazyManyToOneEmbeddedIdWithToOneFKTest.java @@ -108,7 +108,7 @@ public class LazyManyToOneEmbeddedIdWithToOneFKTest { assertTrue( Hibernate.isInitialized( system.getUser() ) ); statementInspector.assertExecutedCount( 1 ); - statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); } ); @@ -147,7 +147,7 @@ public class LazyManyToOneEmbeddedIdWithToOneFKTest { assertTrue( Hibernate.isInitialized( system.getUser() ) ); statementInspector.assertExecutedCount( 1 ); - statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); + statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); } ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithToOneFKTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithToOneFKTest.java index 5db575a114..bf0fc87957 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithToOneFKTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/compositefk/ManyToOneEmbeddedIdWithToOneFKTest.java @@ -89,7 +89,7 @@ public class ManyToOneEmbeddedIdWithToOneFKTest { statementInspector.assertExecutedCount( 3 ); statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); - statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 0 ); + statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 1 ); assertTrue( Hibernate.isInitialized( system.getDataCenterUser() ) ); @@ -119,7 +119,7 @@ public class ManyToOneEmbeddedIdWithToOneFKTest { statementInspector.assertExecutedCount( 3 ); statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); - statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 0 ); + statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 1 ); assertThat( system, is( notNullValue() ) ); DataCenterUser user = system.getDataCenterUser(); assertThat( user, is( notNullValue() ) ); diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/embeddable/EmbeddableQuerySelectTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/embeddable/EmbeddableQuerySelectTest.java new file mode 100644 index 0000000000..5acaf8000e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/embeddable/EmbeddableQuerySelectTest.java @@ -0,0 +1,273 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.embeddable; + +import java.util.List; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DomainModel; +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 jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { + EmbeddableQuerySelectTest.Organisation.class, + EmbeddableQuerySelectTest.User.class, + EmbeddableQuerySelectTest.OrganisationUser.class, + } +) +@SessionFactory +@TestForIssue(jiraKey = "HHH-16366") +public class EmbeddableQuerySelectTest { + + private static final Integer ORGANISATION_ID = 1; + private static final Integer USER_ID = 2; + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Organisation organisation = new Organisation( ORGANISATION_ID, "Red Hat" ); + session.persist( organisation ); + + User user = new User( USER_ID, AccountType.FOO ); + session.persist( user ); + + OrganisationUserEmbeddable embeddable = new OrganisationUserEmbeddable( organisation, user, "1" ); + + OrganisationUser organisationUser = new OrganisationUser( 3, embeddable ); + session.persist( organisationUser ); + } + ); + } + + @Test + public void testSelectUsingEmbeddableInWhereClause(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.embeddable.user u where o.embeddable.organisation.id = ?1 and u.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.embeddable.user u join fetch o.embeddable.organisation or where or.id = ?1 and u.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.embeddable.organisation or where or.id = ?1 and o.embeddable.user.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o where o.embeddable.organisation.id = ?1 and o.embeddable.user.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + } + + @Test + public void testSelect(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o", + OrganisationUser.class + ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + } + + @Test + public void testSelectJoiningPartOfEmbeddable(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.embeddable.user u ", + OrganisationUser.class + ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + } + + @Entity(name = "OrganisationUser") + @Table(name = "ORGANISATION_USER") + public static class OrganisationUser { + + @Id + private Integer id; + + private OrganisationUserEmbeddable embeddable; + + public OrganisationUser() { + } + + public OrganisationUser(Integer id, OrganisationUserEmbeddable organisationUserEmbeddable) { + this.id = id; + this.embeddable = organisationUserEmbeddable; + } + + public Integer getId() { + return id; + } + + public OrganisationUserEmbeddable getEmbeddable() { + return embeddable; + } + } + + @Entity(name = "User") + @Table(name = "F_USER") + public static class User { + + @Id + private Integer id; + + @Enumerated(EnumType.STRING) + @Column(name = "ACCOUNT_TYPE") + private AccountType accountType; + + public User() { + } + + public User(Integer id, AccountType accountType) { + this.id = id; + this.accountType = accountType; + } + + public Integer getId() { + return id; + } + + public AccountType getAccountType() { + return accountType; + } + + } + + @Entity(name = "Organisation") + @Table(name = "ORGANISATION") + public static class Organisation { + + @Id + private Integer id; + + private String name; + + public Organisation() { + } + + public Organisation(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + } + + public enum AccountType { + FOO, + BAR, + } + + @Embeddable + public static class OrganisationUserEmbeddable { + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "ORGANISATION_ID", nullable = false) + private Organisation organisation; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "USER_ID", nullable = false) + private User user; + + private String code; + + public OrganisationUserEmbeddable() { + } + + public OrganisationUserEmbeddable(Organisation organisation, User user, String code) { + this.organisation = organisation; + this.user = user; + this.code = code; + } + + public Organisation getOrganisation() { + return organisation; + } + + public User getUser() { + return user; + } + + public String getCode() { + return code; + } + } + +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/CriteriaEntityGraphTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/CriteriaEntityGraphTest.java index 85cc950b26..b8e3bb3502 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/CriteriaEntityGraphTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/CriteriaEntityGraphTest.java @@ -22,6 +22,7 @@ import org.hibernate.metamodel.mapping.AttributeMappingsList; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; +import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.spi.QueryImplementor; @@ -70,10 +71,10 @@ import jakarta.persistence.criteria.CriteriaQuery; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hibernate.testing.hamcrest.AssignableMatcher.assignableTo; import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize; import static org.hibernate.testing.hamcrest.CollectionMatchers.isEmpty; -import static org.junit.Assert.assertThat; /** * @author Nathan Xu @@ -286,8 +287,11 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware { .next() .getJoinedGroup(); assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) ); - assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() ); assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() ); + assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) ); + + final TableGroup joinedGroup = compositeTableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( joinedGroup.isInitialized(), is( false ) ); } else { assertThat( tableGroup.getTableGroupJoins(), isEmpty() ); @@ -295,8 +299,11 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware { final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup(); assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) ); - assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() ); assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() ); + assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) ); + + final TableGroup joinedGroup = compositeTableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( joinedGroup.isInitialized(), is( false ) ); } } ); @@ -311,7 +318,10 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware { assertThat( fromClause.getRoots(), hasSize( 1 ) ); final TableGroup rootTableGroup = fromClause.getRoots().get( 0 ); - assertThat( rootTableGroup.getTableGroupJoins(), isEmpty() ); + assertThat( rootTableGroup.getTableGroupJoins(), hasSize( 1 ) ); + + final TableGroup tableGroup = rootTableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( tableGroup.isInitialized(), is( false ) ); } private void assertEntityValuedJoinedGroup(SelectStatement sqlAst, String expectedAttributeName, Class expectedEntityJpaClass, Consumer tableGroupConsumer) { @@ -343,12 +353,18 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware { } private void assertPersonHomeAddressJoinedGroup(TableGroup tableGroup) { - assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) ); + assertThat( tableGroup.getTableGroupJoins(), hasSize( 2 ) ); - final TableGroup joinedGroup = tableGroup.getTableGroupJoins().iterator().next().getJoinedGroup(); - assertThat( joinedGroup.getModelPart().getPartName(), is( "homeAddress" ) ); - assertThat( joinedGroup.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) ); - assertThat( joinedGroup, instanceOf( StandardVirtualTableGroup.class ) ); + final TableGroup company = tableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( company.getModelPart().getPartName(), is( "company" ) ); + assertThat( company.getModelPart(), instanceOf( ToOneAttributeMapping.class ) ); + assertThat( company, instanceOf( LazyTableGroup.class ) ); + assertThat( company.isInitialized(), is( false ) ); + + final TableGroup homeAddress = tableGroup.getTableGroupJoins().get( 1 ).getJoinedGroup(); + assertThat( homeAddress.getModelPart().getPartName(), is( "homeAddress" ) ); + assertThat( homeAddress.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) ); + assertThat( homeAddress, instanceOf( StandardVirtualTableGroup.class ) ); } // util methods for verifying 'domain-result' graph ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/EntityGraphLoadPlanBuilderTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/EntityGraphLoadPlanBuilderTest.java index 8270dd2f99..d952117bb5 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/EntityGraphLoadPlanBuilderTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/EntityGraphLoadPlanBuilderTest.java @@ -24,6 +24,7 @@ import org.hibernate.metamodel.mapping.AttributeMappingsList; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; +import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.sql.ast.tree.from.FromClause; import org.hibernate.sql.ast.tree.from.LazyTableGroup; @@ -63,10 +64,10 @@ import jakarta.persistence.OneToMany; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hibernate.testing.hamcrest.AssignableMatcher.assignableTo; import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize; import static org.hibernate.testing.hamcrest.CollectionMatchers.isEmpty; -import static org.junit.Assert.assertThat; /** * @author Strong Liu @@ -268,8 +269,11 @@ public class EntityGraphLoadPlanBuilderTest implements SessionFactoryScopeAware final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup(); assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) ); - assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() ); assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() ); + assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) ); + + final TableGroup joinedGroup = compositeTableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( joinedGroup.isInitialized(), is( false ) ); } } ); @@ -284,7 +288,10 @@ public class EntityGraphLoadPlanBuilderTest implements SessionFactoryScopeAware assertThat( fromClause.getRoots(), hasSize( 1 ) ); final TableGroup rootTableGroup = fromClause.getRoots().get( 0 ); - assertThat( rootTableGroup.getTableGroupJoins(), isEmpty() ); + assertThat( rootTableGroup.getTableGroupJoins(), hasSize( 1 ) ); + + final TableGroup tableGroup = rootTableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( tableGroup.isInitialized(), is( false ) ); } private void assertEntityValuedJoinedGroup(SelectStatement sqlAst, String expectedAttributeName, Class expectedEntityJpaClass, Consumer tableGroupConsumer) { @@ -316,12 +323,18 @@ public class EntityGraphLoadPlanBuilderTest implements SessionFactoryScopeAware } private void assertPersonHomeAddressJoinedGroup(TableGroup tableGroup) { - assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) ); + assertThat( tableGroup.getTableGroupJoins(), hasSize( 2 ) ); - final TableGroup joinedGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() ).getJoinedGroup(); - assertThat( joinedGroup.getModelPart().getPartName(), is( "homeAddress" ) ); - assertThat( joinedGroup.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) ); - assertThat( joinedGroup, instanceOf( StandardVirtualTableGroup.class ) ); + final TableGroup company = tableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( company.getModelPart().getPartName(), is( "company" ) ); + assertThat( company.getModelPart(), instanceOf( ToOneAttributeMapping.class ) ); + assertThat( company, instanceOf( LazyTableGroup.class ) ); + assertThat( company.isInitialized(), is( false ) ); + + final TableGroup homeAddress = tableGroup.getTableGroupJoins().get( 1 ).getJoinedGroup(); + assertThat( homeAddress.getModelPart().getPartName(), is( "homeAddress" ) ); + assertThat( homeAddress.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) ); + assertThat( homeAddress, instanceOf( StandardVirtualTableGroup.class ) ); } // util methods for verifying 'domain-result' graph ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/HqlEntityGraphTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/HqlEntityGraphTest.java index 2be33ed2a5..67ad994c64 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/HqlEntityGraphTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/entitygraph/ast/HqlEntityGraphTest.java @@ -22,6 +22,7 @@ import org.hibernate.metamodel.mapping.AttributeMappingsList; import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; +import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.spi.QueryImplementor; @@ -68,10 +69,10 @@ import jakarta.persistence.OneToMany; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hibernate.testing.hamcrest.AssignableMatcher.assignableTo; import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize; import static org.hibernate.testing.hamcrest.CollectionMatchers.isEmpty; -import static org.junit.Assert.assertThat; /** * @author Nathan Xu @@ -284,8 +285,11 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware { .next() .getJoinedGroup(); assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) ); - assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty( ) ); assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() ); + assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) ); + + final TableGroup joinedGroup = compositeTableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( joinedGroup.isInitialized(), is( false ) ); } else { assertThat( tableGroup.getTableGroupJoins(), isEmpty() ); @@ -293,8 +297,11 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware { final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup(); assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) ); - assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() ); assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() ); + assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) ); + + final TableGroup joinedGroup = compositeTableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( joinedGroup.isInitialized(), is( false ) ); } } ); } @@ -308,7 +315,10 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware { assertThat( fromClause.getRoots(), hasSize( 1 ) ); final TableGroup rootTableGroup = fromClause.getRoots().get( 0 ); - assertThat( rootTableGroup.getTableGroupJoins(), isEmpty() ); + assertThat( rootTableGroup.getTableGroupJoins(), hasSize( 1 ) ); + + final TableGroup tableGroup = rootTableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( tableGroup.isInitialized(), is( false ) ); } private void assertEntityValuedJoinedGroup(SelectStatement sqlAst, String expectedAttributeName, Class expectedEntityJpaClass, Consumer tableGroupConsumer) { @@ -333,19 +343,26 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware { final TableGroup root = fromClause.getRoots().get( 0 ); assertThat( root.getTableGroupJoins(), hasSize( 1 ) ); - final TableGroup joinedGroup = root.getTableGroupJoins().iterator().next().getJoinedGroup(); + final TableGroup joinedGroup = root.getTableGroupJoins().get( 0 ).getJoinedGroup(); assertThat( joinedGroup.getModelPart().getPartName(), is( expectedPluralAttributeName ) ); assertThat( joinedGroup.getModelPart(), instanceOf( PluralAttributeMapping.class ) ); tableGroupConsumer.accept( joinedGroup ); } private void assertPersonHomeAddressJoinedGroup(TableGroup tableGroup) { - assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) ); - final TableGroup joinedGroup = tableGroup.getTableGroupJoins().iterator().next().getJoinedGroup(); - assertThat( joinedGroup.getModelPart().getPartName(), is( "homeAddress" ) ); - assertThat( joinedGroup.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) ); - assertThat( joinedGroup, instanceOf( StandardVirtualTableGroup.class ) ); + assertThat( tableGroup.getTableGroupJoins(), hasSize( 2 ) ); + + final TableGroup company = tableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup(); + assertThat( company.getModelPart().getPartName(), is( "company" ) ); + assertThat( company.getModelPart(), instanceOf( ToOneAttributeMapping.class ) ); + assertThat( company, instanceOf( LazyTableGroup.class ) ); + assertThat( company.isInitialized(), is( false ) ); + + final TableGroup homeAddress = tableGroup.getTableGroupJoins().get( 1 ).getJoinedGroup(); + assertThat( homeAddress.getModelPart().getPartName(), is( "homeAddress" ) ); + assertThat( homeAddress.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) ); + assertThat( homeAddress, instanceOf( StandardVirtualTableGroup.class ) ); } // util methods for verifying 'domain-result' graph ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/graph/HHH15065Test.java b/hibernate-core/src/test/java/org/hibernate/orm/test/graph/HHH15065Test.java index 036fbdf244..f016143efd 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/graph/HHH15065Test.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/graph/HHH15065Test.java @@ -46,7 +46,7 @@ class HHH15065Test { SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); List sqlQueries = statementInspector.getSqlQueries(); assertEquals( 1, sqlQueries.size() ); - assertEquals( "select b1_0.id,a1_0.id,c1_0.id,c2_0.id,e1_0.id" + + assertEquals( "select b1_0.id,a1_0.id,a1_0.name,c1_0.id,c1_0.name,c2_0.id,c2_0.name,e1_0.id,e1_0.name" + " from Book b1_0" + " left join Person a1_0 on a1_0.id=b1_0.author_id" + " left join Person c1_0 on c1_0.id=b1_0.coAuthor_id" + @@ -73,9 +73,10 @@ class HHH15065Test { } @Entity(name = "Person") - public class Person { + public static class Person { @Id Long id; + String name; } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ManyToOneJoinReuseTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ManyToOneJoinReuseTest.java index bb29c87150..5a23147f2d 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ManyToOneJoinReuseTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/hql/ManyToOneJoinReuseTest.java @@ -25,6 +25,8 @@ import jakarta.persistence.Table; import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.JoinType; +import static org.junit.jupiter.api.Assertions.assertEquals; + /** * @author Christian Beikov */ @@ -41,9 +43,9 @@ public class ManyToOneJoinReuseTest { @TestForIssue(jiraKey = "HHH-15648") public void fetchAndImplicitPath(SessionFactoryScope scope) { SQLStatementInspector sqlStatementInterceptor = scope.getCollectingStatementInspector(); - sqlStatementInterceptor.clear(); scope.inTransaction( session -> { + sqlStatementInterceptor.clear(); HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); JpaCriteriaQuery query = cb.createQuery( BookList.class ); @@ -52,7 +54,11 @@ public class ManyToOneJoinReuseTest { query.where( root.get( "book" ).isNotNull() ); session.createQuery( query ).getResultList(); - sqlStatementInterceptor.assertExecuted( "select b1_0.id,b2_0.isbn,b2_0.title from BookList b1_0 join book b2_0 on b2_0.isbn=b1_0.book_isbn where b2_0.isbn is not null" ); + assertEquals( 1, sqlStatementInterceptor.getSqlQueries().size() ); + assertEquals( + "select b1_0.id,b2_0.isbn,b2_0.title from BookList b1_0 join book b2_0 on b2_0.isbn=b1_0.book_isbn where b2_0.isbn is not null", + sqlStatementInterceptor.getSqlQueries().get( 0 ) + ); } ); } @@ -61,9 +67,9 @@ public class ManyToOneJoinReuseTest { @TestForIssue(jiraKey = "HHH-15645") public void joinAndImplicitPath(SessionFactoryScope scope) { SQLStatementInspector sqlStatementInterceptor = scope.getCollectingStatementInspector(); - sqlStatementInterceptor.clear(); scope.inTransaction( session -> { + sqlStatementInterceptor.clear(); HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); JpaCriteriaQuery query = cb.createQuery( BookList.class ); @@ -77,7 +83,11 @@ public class ManyToOneJoinReuseTest { ); session.createQuery( query ).getResultList(); - sqlStatementInterceptor.assertExecuted( "select b1_0.id,b1_0.book_isbn from BookList b1_0 join book b2_0 on b2_0.isbn=b1_0.book_isbn where b2_0.isbn is not null and b1_0.book_isbn is not null" ); + assertEquals( 1, sqlStatementInterceptor.getSqlQueries().size() ); + assertEquals( + "select b1_0.id,b1_0.book_isbn from BookList b1_0 join book b2_0 on b2_0.isbn=b1_0.book_isbn where b2_0.isbn is not null and b1_0.book_isbn is not null", + sqlStatementInterceptor.getSqlQueries().get( 0 ) + ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/IdClassEagerQuerySelectTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/IdClassEagerQuerySelectTest.java new file mode 100644 index 0000000000..33aac7f57b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/IdClassEagerQuerySelectTest.java @@ -0,0 +1,295 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.idclass; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DomainModel; +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 jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { + IdClassEagerQuerySelectTest.Organisation.class, + IdClassEagerQuerySelectTest.User.class, + IdClassEagerQuerySelectTest.OrganisationUser.class, + } +) +@SessionFactory +@TestForIssue(jiraKey = "HHH-16366") +public class IdClassEagerQuerySelectTest { + + private static final Integer ORGANISATION_ID = 1; + private static final Integer USER_ID = 2; + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Organisation organisation = new Organisation( ORGANISATION_ID, "Red Hat" ); + session.persist( organisation ); + + User user = new User( USER_ID, AccountType.FOO ); + session.persist( user ); + + OrganisationUser organisationUser = new OrganisationUser( organisation, user, "1" ); + session.persist( organisationUser ); + } + ); + } + + @Test + public void testSelectUsingIdClassInWhereClause(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.user u where o.organisation.id = ?1 and u.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.user u join fetch o.organisation or where or.id = ?1 and u.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.organisation or where or.id = ?1 and o.user.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o where o.organisation.id = ?1 and o.user.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + } + + @Test + public void testSelect(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o", + OrganisationUser.class + ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + } + + @Test + public void testSelectJoiningPartOfIdClass(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.user u ", + OrganisationUser.class + ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + } + + @Entity(name = "OrganisationUser") + @Table(name = "ORGANISATION_USER") + @IdClass(OrganisationUserId.class) + public static class OrganisationUser { + + @Id + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "ORGANISATION_ID", nullable = false) + private Organisation organisation; + + @Id + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "USER_ID", nullable = false) + private User user; + + public OrganisationUser() { + } + + public OrganisationUser(Organisation organisation, User user, String code) { + this.organisation = organisation; + this.user = user; + this.code = code; + } + + private String code; + + public Organisation getOrganisation() { + return organisation; + } + + public User getUser() { + return user; + } + } + + @Entity(name = "User") + @Table(name = "F_USER") + public static class User { + + @Id + private Integer id; + + @Enumerated(EnumType.STRING) + @Column(name = "ACCOUNT_TYPE") + private AccountType accountType; + + public User() { + } + + public User(Integer id, AccountType accountType) { + this.id = id; + this.accountType = accountType; + } + + public Integer getId() { + return id; + } + + public AccountType getAccountType() { + return accountType; + } + + } + + @Entity(name = "Organisation") + @Table(name = "ORGANISATION") + public static class Organisation { + + @Id + @Column(name = "ID", unique = true) + private Integer id; + + private String name; + + public Organisation() { + } + + public Organisation(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + } + + public enum AccountType { + FOO, + BAR, + } + + public static class OrganisationUserId implements Serializable { + + private Integer organisation; + private Integer user; + + public OrganisationUserId() { + } + + public OrganisationUserId(Integer organisation, Integer user) { + this.organisation = organisation; + this.user = user; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + OrganisationUserId that = (OrganisationUserId) o; + return Objects.equals( organisation, that.organisation ) && Objects.equals( user, that.user ); + } + + @Override + public int hashCode() { + return Objects.hash( organisation, user ); + } + + public Integer getOrganisation() { + return organisation; + } + + public void setOrganisation(Integer organisation) { + this.organisation = organisation; + } + + public Integer getUser() { + return user; + } + + public void setUser(Integer user) { + this.user = user; + } + } + +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/IdClassQuerySelectTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/IdClassQuerySelectTest.java new file mode 100644 index 0000000000..bd92d95ff1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/idclass/IdClassQuerySelectTest.java @@ -0,0 +1,295 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.orm.test.idclass; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.DomainModel; +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 jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +@DomainModel( + annotatedClasses = { + IdClassQuerySelectTest.Organisation.class, + IdClassQuerySelectTest.User.class, + IdClassQuerySelectTest.OrganisationUser.class, + } +) +@SessionFactory +@TestForIssue(jiraKey = "HHH-16366") +public class IdClassQuerySelectTest { + + private static final Integer ORGANISATION_ID = 1; + private static final Integer USER_ID = 2; + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + Organisation organisation = new Organisation( ORGANISATION_ID, "Red Hat" ); + session.persist( organisation ); + + User user = new User( USER_ID, AccountType.FOO ); + session.persist( user ); + + OrganisationUser organisationUser = new OrganisationUser( organisation, user, "1" ); + session.persist( organisationUser ); + } + ); + } + + @Test + public void testSelectUsingIdClassInWhereClause(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.user u where o.organisation.id = ?1 and u.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.user u join fetch o.organisation or where or.id = ?1 and u.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.organisation or where or.id = ?1 and o.user.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o where o.organisation.id = ?1 and o.user.accountType = ?2 ", + OrganisationUser.class + ) + .setParameter( 1, ORGANISATION_ID ) + .setParameter( 2, AccountType.FOO ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + } + + @Test + public void testSelect(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o", + OrganisationUser.class + ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + } + + @Test + public void testSelectJoiningPartOfIdClass(SessionFactoryScope scope) { + scope.inTransaction( + session -> { + List resultList = session.createQuery( + "select distinct o from OrganisationUser o join fetch o.user u ", + OrganisationUser.class + ) + .getResultList(); + + assertThat( resultList.size() ).isEqualTo( 1 ); + } + ); + } + + @Entity(name = "OrganisationUser") + @Table(name = "ORGANISATION_USER") + @IdClass(OrganisationUserId.class) + public static class OrganisationUser { + + @Id + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "ORGANISATION_ID", nullable = false) + private Organisation organisation; + + @Id + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "USER_ID", nullable = false) + private User user; + + public OrganisationUser() { + } + + public OrganisationUser(Organisation organisation, User user, String code) { + this.organisation = organisation; + this.user = user; + this.code = code; + } + + private String code; + + public Organisation getOrganisation() { + return organisation; + } + + public User getUser() { + return user; + } + } + + @Entity(name = "User") + @Table(name = "F_USER") + public static class User { + + @Id + private Integer id; + + @Enumerated(EnumType.STRING) + @Column(name = "ACCOUNT_TYPE") + private AccountType accountType; + + public User() { + } + + public User(Integer id, AccountType accountType) { + this.id = id; + this.accountType = accountType; + } + + public Integer getId() { + return id; + } + + public AccountType getAccountType() { + return accountType; + } + + } + + @Entity(name = "Organisation") + @Table(name = "ORGANISATION") + public static class Organisation { + + @Id + @Column(name = "ID", unique = true) + private Integer id; + + private String name; + + public Organisation() { + } + + public Organisation(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + } + + public enum AccountType { + FOO, + BAR, + } + + public static class OrganisationUserId implements Serializable { + + private Integer organisation; + private Integer user; + + public OrganisationUserId() { + } + + public OrganisationUserId(Integer organisation, Integer user) { + this.organisation = organisation; + this.user = user; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + OrganisationUserId that = (OrganisationUserId) o; + return Objects.equals( organisation, that.organisation ) && Objects.equals( user, that.user ); + } + + @Override + public int hashCode() { + return Objects.hash( organisation, user ); + } + + public Integer getOrganisation() { + return organisation; + } + + public void setOrganisation(Integer organisation) { + this.organisation = organisation; + } + + public Integer getUser() { + return user; + } + + public void setUser(Integer user) { + this.user = user; + } + } + +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/GeneratedAnnotationBatchTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/GeneratedAnnotationBatchTest.java index 374eb3b3d8..37e0517d02 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/GeneratedAnnotationBatchTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/generated/GeneratedAnnotationBatchTest.java @@ -9,6 +9,7 @@ package org.hibernate.orm.test.mapping.generated; import java.time.Instant; import java.util.List; +import org.hibernate.HibernateError; import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.CurrentTimestamp; import org.hibernate.annotations.Generated; @@ -81,7 +82,11 @@ public class GeneratedAnnotationBatchTest { GeneratedEntity.class ).getResultList(); entities.forEach( ge -> ge.setName( "updated" ) ); + + //We need to wait a little to make sure the timestamps produced are different + waitALittle(); session.flush(); // force update and retrieval of generated values + entities.forEach( ge -> assertThat( ge.getName() ).isEqualTo( "updated" ) ); entities.forEach( ge -> assertThat( ge.getUpdateTimestamp() ).isAfter( originalInstant ) ); } ); @@ -125,4 +130,13 @@ public class GeneratedAnnotationBatchTest { return updateTimestamp; } } + + private static void waitALittle() { + try { + Thread.sleep( 10 ); + } + catch (InterruptedException e) { + throw new HibernateError( "Unexpected wakeup from test sleep" ); + } + } } 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 9122f90a0c..08df908d6b 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 @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.List; import org.hibernate.metamodel.mapping.PluralAttributeMapping; +import org.hibernate.metamodel.mapping.ValuedModelPart; import org.hibernate.metamodel.mapping.ordering.OrderByFragment; import org.hibernate.query.ReturnableType; import org.hibernate.persister.collection.QueryableCollection; @@ -87,24 +88,45 @@ public class OrderByFragmentFunction extends AbstractSqmFunctionDescriptor { @Override public TableReference resolveTableReference( NavigablePath navigablePath, - String tableExpression, - boolean allowFkOptimization) { + String tableExpression) { if ( tableExpression.equals( normalTableExpression ) ) { tableExpression = auditTableExpression; } - return super.resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); + return super.resolveTableReference( navigablePath, tableExpression ); + } + + @Override + public TableReference resolveTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression) { + if ( tableExpression.equals( normalTableExpression ) ) { + return resolveTableReference( navigablePath, modelPart, auditTableExpression ); + } + return super.resolveTableReference( navigablePath, modelPart, tableExpression ); } @Override public TableReference getTableReference( NavigablePath navigablePath, String tableExpression, - boolean allowFkOptimization, boolean resolve) { if ( tableExpression.equals( normalTableExpression ) ) { tableExpression = auditTableExpression; } - return super.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); + return super.getTableReference( navigablePath, tableExpression, resolve ); + } + + @Override + public TableReference getTableReference( + NavigablePath navigablePath, + ValuedModelPart modelPart, + String tableExpression, + boolean resolve) { + if ( tableExpression.equals( normalTableExpression ) ) { + return getTableReference( navigablePath, modelPart, auditTableExpression, resolve ); + } + return super.getTableReference( navigablePath, modelPart, tableExpression, resolve ); } }