HHH-16382 Fix resolving table references especially for self-referential associations

This commit is contained in:
Christian Beikov 2023-04-17 15:47:19 +02:00
parent b754325cab
commit e96402a005
103 changed files with 2602 additions and 851 deletions

View File

@ -26,7 +26,7 @@ public class TableGroupFilterAliasGenerator implements FilterAliasGenerator {
if ( table == null ) { if ( table == null ) {
table = defaultTable; 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(); return tableReference == null ? null : tableReference.getIdentificationVariable();
} }

View File

@ -291,4 +291,8 @@ public abstract class AbstractCompositeIdentifierMapping
return false; return false;
} }
@Override
public boolean containsTableReference(String tableExpression) {
return entityMapping.containsTableReference( tableExpression );
}
} }

View File

@ -21,7 +21,7 @@ import org.hibernate.type.descriptor.java.MutabilityPlanExposer;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface AttributeMapping public interface AttributeMapping
extends ValuedModelPart, Fetchable, DatabaseSnapshotContributor, PropertyBasedMapping, MutabilityPlanExposer { extends OwnedValuedModelPart, Fetchable, DatabaseSnapshotContributor, PropertyBasedMapping, MutabilityPlanExposer {
/** /**
* The name of the mapped attribute * The name of the mapped attribute
*/ */

View File

@ -18,4 +18,11 @@ public interface BasicEntityIdentifierMapping extends SingleAttributeIdentifierM
default int getFetchableKey() { default int getFetchableKey() {
return -1; return -1;
} }
@Override
boolean isNullable();
@Override
boolean isInsertable();
} }

View File

@ -10,6 +10,7 @@ import java.util.function.IntFunction;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.mapping.internal.VirtualIdEmbeddable;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
@ -42,7 +43,9 @@ public interface ForeignKeyDescriptor extends VirtualModelPart, ValuedModelPart
ValuedModelPart getTargetPart(); ValuedModelPart getTargetPart();
default ModelPart getPart(Nature nature) { boolean isKeyPart(ValuedModelPart modelPart);
default ValuedModelPart getPart(Nature nature) {
if ( nature == Nature.KEY ) { if ( nature == Nature.KEY ) {
return getKeyPart(); return getKeyPart();
} }
@ -76,27 +79,45 @@ public interface ForeignKeyDescriptor extends VirtualModelPart, ValuedModelPart
/** /**
* Create a DomainResult for the referring-side of the fk * Create a DomainResult for the referring-side of the fk
* The table group must be the one containing the target.
*/ */
DomainResult<?> createKeyDomainResult( DomainResult<?> createKeyDomainResult(
NavigablePath navigablePath, 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, FetchParent fetchParent,
DomainResultCreationState creationState); DomainResultCreationState creationState);
/** /**
* Create a DomainResult for the target-side of the fk * Create a DomainResult for the target-side of the fk
* The table group must be the one containing the target
*/ */
DomainResult<?> createTargetDomainResult( DomainResult<?> createTargetDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup targetTableGroup,
FetchParent fetchParent, FetchParent fetchParent,
DomainResultCreationState creationState); 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
<T> DomainResult<T> createDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup targetTableGroup,
Nature side, String resultVariable,
FetchParent fetchParent,
DomainResultCreationState creationState); DomainResultCreationState creationState);
Predicate generateJoinPredicate( Predicate generateJoinPredicate(

View File

@ -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();
}

View File

@ -121,7 +121,7 @@ public interface PluralAttributeMapping
@Override @Override
default <T> DomainResult<T> createSnapshotDomainResult( default <T> DomainResult<T> createSnapshotDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup parentTableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return new BasicResult( 0, null, getJavaType() ); return new BasicResult( 0, null, getJavaType() );

View File

@ -63,7 +63,11 @@ public abstract class AbstractDomainPath implements DomainPath {
SqlAstCreationState creationState) { SqlAstCreationState creationState) {
if ( referenceModelPart instanceof BasicValuedModelPart ) { if ( referenceModelPart instanceof BasicValuedModelPart ) {
final BasicValuedModelPart selection = (BasicValuedModelPart) referenceModelPart; 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( return creationState.getSqlExpressionResolver().resolveSqlExpression(
createColumnReferenceKey( tableReference, selection.getSelectionExpression() ), createColumnReferenceKey( tableReference, selection.getSelectionExpression() ),
processingState -> new ColumnReference( processingState -> new ColumnReference(

View File

@ -308,7 +308,7 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa
@Override @Override
public boolean containsTableReference(String tableExpression) { public boolean containsTableReference(String tableExpression) {
return getCollectionDescriptor().getAttributeMapping().containsTableReference( tableExpression ); return getAssociatedEntityMappingType().containsTableReference( tableExpression );
} }
public TableGroup createTableGroupInternal( public TableGroup createTableGroupInternal(
@ -319,7 +319,6 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa
final SqlAliasBase sqlAliasBase, final SqlAliasBase sqlAliasBase,
SqlAstCreationState creationState) { SqlAstCreationState creationState) {
final SqlAstCreationContext creationContext = creationState.getCreationContext(); final SqlAstCreationContext creationContext = creationState.getCreationContext();
final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver();
final TableReference primaryTableReference = getEntityMappingType().createPrimaryTableReference( final TableReference primaryTableReference = getEntityMappingType().createPrimaryTableReference(
sqlAliasBase, sqlAliasBase,
creationState creationState
@ -389,7 +388,7 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa
final CompositeType compositeType; final CompositeType compositeType;
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded() if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
&& compositeType.getPropertyNames().length == 1 ) { && compositeType.getPropertyNames().length == 1 ) {
ToOneAttributeMapping.addPrefixedPropertyNames( ToOneAttributeMapping.addPrefixedPropertyPaths(
targetKeyPropertyNames, targetKeyPropertyNames,
compositeType.getPropertyNames()[0], compositeType.getPropertyNames()[0],
compositeType.getSubtypes()[0], compositeType.getSubtypes()[0],
@ -397,39 +396,27 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa
); );
ToOneAttributeMapping.addPrefixedPropertyNames( ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames, targetKeyPropertyNames,
ForeignKeyDescriptor.PART_NAME, EntityIdentifierMapping.ROLE_LOCAL_NAME,
compositeType.getSubtypes()[0], propertyType,
creationProcess.getCreationContext().getSessionFactory() creationProcess.getCreationContext().getSessionFactory()
); );
} }
else { else {
ToOneAttributeMapping.addPrefixedPropertyNames( ToOneAttributeMapping.addPrefixedPropertyPaths(
targetKeyPropertyNames, targetKeyPropertyNames,
null, null,
propertyType, propertyType,
creationProcess.getCreationContext().getSessionFactory() creationProcess.getCreationContext().getSessionFactory()
); );
ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames,
ForeignKeyDescriptor.PART_NAME,
propertyType,
creationProcess.getCreationContext().getSessionFactory()
);
} }
} }
else { else {
ToOneAttributeMapping.addPrefixedPropertyNames( ToOneAttributeMapping.addPrefixedPropertyPaths(
targetKeyPropertyNames, targetKeyPropertyNames,
entityBinding.getIdentifierProperty().getName(), entityBinding.getIdentifierProperty().getName(),
propertyType, propertyType,
creationProcess.getCreationContext().getSessionFactory() creationProcess.getCreationContext().getSessionFactory()
); );
ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames,
ForeignKeyDescriptor.PART_NAME,
propertyType,
creationProcess.getCreationContext().getSessionFactory()
);
} }
return targetKeyPropertyNames; return targetKeyPropertyNames;
} }
@ -442,18 +429,12 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa
// todo (PropertyMapping) : the problem here is timing. this needs to be delayed. // todo (PropertyMapping) : the problem here is timing. this needs to be delayed.
final Type propertyType = ( (PropertyMapping) elementTypeDescriptor.getEntityPersister() ) final Type propertyType = ( (PropertyMapping) elementTypeDescriptor.getEntityPersister() )
.toType( referencedPropertyName ); .toType( referencedPropertyName );
ToOneAttributeMapping.addPrefixedPropertyNames( ToOneAttributeMapping.addPrefixedPropertyPaths(
targetKeyPropertyNames, targetKeyPropertyNames,
referencedPropertyName, referencedPropertyName,
propertyType, propertyType,
creationProcess.getCreationContext().getSessionFactory() creationProcess.getCreationContext().getSessionFactory()
); );
ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames,
ForeignKeyDescriptor.PART_NAME,
propertyType,
creationProcess.getCreationContext().getSessionFactory()
);
return targetKeyPropertyNames; return targetKeyPropertyNames;
} }
else { else {
@ -462,7 +443,7 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded() if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
&& compositeType.getPropertyNames().length == 1 ) { && compositeType.getPropertyNames().length == 1 ) {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 ); final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
ToOneAttributeMapping.addPrefixedPropertyNames( ToOneAttributeMapping.addPrefixedPropertyPaths(
targetKeyPropertyNames, targetKeyPropertyNames,
compositeType.getPropertyNames()[0], compositeType.getPropertyNames()[0],
compositeType.getSubtypes()[0], compositeType.getSubtypes()[0],
@ -470,34 +451,34 @@ public abstract class AbstractEntityCollectionPart implements EntityCollectionPa
); );
ToOneAttributeMapping.addPrefixedPropertyNames( ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames, targetKeyPropertyNames,
ForeignKeyDescriptor.PART_NAME, EntityIdentifierMapping.ROLE_LOCAL_NAME,
compositeType.getSubtypes()[0], propertyType,
creationProcess.getCreationContext().getSessionFactory() creationProcess.getCreationContext().getSessionFactory()
); );
return targetKeyPropertyNames; return targetKeyPropertyNames;
} }
else { else {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
targetKeyPropertyNames.add( referencedPropertyName );
final String mapsIdAttributeName; final String mapsIdAttributeName;
if ( ( mapsIdAttributeName = ToOneAttributeMapping.findMapsIdPropertyName( elementTypeDescriptor, referencedPropertyName ) ) != null ) { if ( ( mapsIdAttributeName = ToOneAttributeMapping.findMapsIdPropertyName( elementTypeDescriptor, referencedPropertyName ) ) != null ) {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 ); ToOneAttributeMapping.addPrefixedPropertyPaths(
targetKeyPropertyNames.add( referencedPropertyName );
ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames, targetKeyPropertyNames,
mapsIdAttributeName, mapsIdAttributeName,
elementTypeDescriptor.getEntityPersister().getIdentifierType(), elementTypeDescriptor.getEntityPersister().getIdentifierType(),
creationProcess.getCreationContext().getSessionFactory() creationProcess.getCreationContext().getSessionFactory()
); );
ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames,
ForeignKeyDescriptor.PART_NAME,
elementTypeDescriptor.getEntityPersister().getIdentifierType(),
creationProcess.getCreationContext().getSessionFactory()
);
return targetKeyPropertyNames;
} }
else { else {
return Set.of( referencedPropertyName, ForeignKeyDescriptor.PART_NAME ); ToOneAttributeMapping.addPrefixedPropertyPaths(
} targetKeyPropertyNames,
null,
propertyType,
creationProcess.getCreationContext().getSessionFactory()
);
}
return targetKeyPropertyNames;
} }
} }
} }

View File

@ -6,7 +6,6 @@
*/ */
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import java.io.Serializable;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
@ -147,7 +146,15 @@ public class BasicAttributeMapping
if ( original instanceof SingleAttributeIdentifierMapping ) { if ( original instanceof SingleAttributeIdentifierMapping ) {
final SingleAttributeIdentifierMapping mapping = (SingleAttributeIdentifierMapping) original; final SingleAttributeIdentifierMapping mapping = (SingleAttributeIdentifierMapping) original;
attributeName = mapping.getAttributeName(); attributeName = mapping.getAttributeName();
attributeMetadata = null; attributeMetadata = new SimpleAttributeMetadata(
propertyAccess,
mapping.getExpressibleJavaType().getMutabilityPlan(),
selectableMapping.isNullable(),
insertable,
updateable,
false,
true
);
} }
else if ( original instanceof SingularAttributeMapping ) { else if ( original instanceof SingularAttributeMapping ) {
final SingularAttributeMapping mapping = (SingularAttributeMapping) original; final SingularAttributeMapping mapping = (SingularAttributeMapping) original;
@ -297,7 +304,7 @@ public class BasicAttributeMapping
TableGroup tableGroup, TableGroup tableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, null, creationState );
//noinspection unchecked //noinspection unchecked
return new BasicResult( return new BasicResult(
@ -311,14 +318,13 @@ public class BasicAttributeMapping
private SqlSelection resolveSqlSelection( private SqlSelection resolveSqlSelection(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
@SuppressWarnings("SameParameterValue") boolean allowFkOptimization,
FetchParent fetchParent, FetchParent fetchParent,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver(); final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver();
final TableReference tableReference = tableGroup.resolveTableReference( final TableReference tableReference = tableGroup.resolveTableReference(
navigablePath, navigablePath,
getContainingTableExpression(), this,
allowFkOptimization getContainingTableExpression()
); );
return expressionResolver.resolveSqlSelection( return expressionResolver.resolveSqlSelection(
@ -337,7 +343,7 @@ public class BasicAttributeMapping
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); resolveSqlSelection( navigablePath, tableGroup, null, creationState );
} }
@Override @Override
@ -346,7 +352,7 @@ public class BasicAttributeMapping
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState, DomainResultCreationState creationState,
BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) { BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) {
selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ), getJdbcMapping() ); selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, null, creationState ), getJdbcMapping() );
} }
@Override @Override
@ -375,7 +381,12 @@ public class BasicAttributeMapping
assert tableGroup != null; assert tableGroup != null;
final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, true, fetchParent, creationState ); final SqlSelection sqlSelection = resolveSqlSelection(
fetchablePath,
tableGroup,
fetchParent,
creationState
);
valuesArrayPosition = sqlSelection.getValuesArrayPosition(); valuesArrayPosition = sqlSelection.getValuesArrayPosition();
if ( sqlSelection.getExpressionType() != null) { if ( sqlSelection.getExpressionType() != null) {
// if the expression type is different that the expected type coerce the value // if the expression type is different that the expected type coerce the value

View File

@ -225,7 +225,7 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
TableGroup tableGroup, TableGroup tableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, null, creationState );
return new BasicResult<>( return new BasicResult<>(
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
@ -240,7 +240,7 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); resolveSqlSelection( navigablePath, tableGroup, null, creationState );
} }
@Override @Override
@ -250,7 +250,7 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
DomainResultCreationState creationState, DomainResultCreationState creationState,
BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) { BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) {
selectionConsumer.accept( selectionConsumer.accept(
resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ), resolveSqlSelection( navigablePath, tableGroup, null, creationState ),
getJdbcMapping() getJdbcMapping()
); );
} }
@ -258,14 +258,13 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
private SqlSelection resolveSqlSelection( private SqlSelection resolveSqlSelection(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
boolean allowFkOptimization,
FetchParent fetchParent, FetchParent fetchParent,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState() final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState()
.getSqlExpressionResolver(); .getSqlExpressionResolver();
final TableReference rootTableReference; final TableReference rootTableReference;
try { try {
rootTableReference = tableGroup.resolveTableReference( navigablePath, rootTable, allowFkOptimization ); rootTableReference = tableGroup.resolveTableReference( navigablePath, rootTable );
} }
catch (Exception e) { catch (Exception e) {
throw new IllegalStateException( throw new IllegalStateException(
@ -315,12 +314,12 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
@Override @Override
public boolean isInsertable() { public boolean isInsertable() {
return updateable; return insertable;
} }
@Override @Override
public boolean isUpdateable() { public boolean isUpdateable() {
return insertable; return updateable;
} }
@Override @Override
@ -414,7 +413,7 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
assert tableGroup != null; 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(); final JdbcMappingContainer selectionType = sqlSelection.getExpressionType();
return new BasicFetch<>( return new BasicFetch<>(
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),

View File

@ -6,7 +6,6 @@
*/ */
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import java.io.Serializable;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@ -160,7 +159,7 @@ public class BasicValuedCollectionPart
TableGroup tableGroup, TableGroup tableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, null, creationState );
return new BasicResult<>( return new BasicResult<>(
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),
@ -173,7 +172,6 @@ public class BasicValuedCollectionPart
private SqlSelection resolveSqlSelection( private SqlSelection resolveSqlSelection(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
boolean allowFkOptimization,
FetchParent fetchParent, FetchParent fetchParent,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlExpressionResolver exprResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver(); final SqlExpressionResolver exprResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver();
@ -189,8 +187,7 @@ public class BasicValuedCollectionPart
} }
final TableReference tableReference = targetTableGroup.resolveTableReference( final TableReference tableReference = targetTableGroup.resolveTableReference(
navigablePath, navigablePath,
getContainingTableExpression(), getContainingTableExpression()
allowFkOptimization
); );
return exprResolver.resolveSqlSelection( return exprResolver.resolveSqlSelection(
exprResolver.resolveSqlExpression( exprResolver.resolveSqlExpression(
@ -206,7 +203,7 @@ public class BasicValuedCollectionPart
@Override @Override
public void applySqlSelections( public void applySqlSelections(
NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) {
resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ); resolveSqlSelection( navigablePath, tableGroup, null, creationState );
} }
@Override @Override
@ -215,7 +212,7 @@ public class BasicValuedCollectionPart
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState, DomainResultCreationState creationState,
BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) { BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) {
selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, true, null, creationState ), getJdbcMapping() ); selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, null, creationState ), getJdbcMapping() );
} }
@Override @Override
@ -272,7 +269,7 @@ public class BasicValuedCollectionPart
final TableGroup tableGroup = creationState.getSqlAstCreationState() final TableGroup tableGroup = creationState.getSqlAstCreationState()
.getFromClauseAccess() .getFromClauseAccess()
.findTableGroup( parentNavigablePath ); .findTableGroup( parentNavigablePath );
final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, true, fetchParent, creationState ); final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, fetchParent, creationState );
return new BasicFetch<>( return new BasicFetch<>(
sqlSelection.getValuesArrayPosition(), sqlSelection.getValuesArrayPosition(),

View File

@ -7,7 +7,6 @@
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -95,7 +94,6 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator
(tableName, tableDiscriminatorDetails) -> tableGroup.getTableReference( (tableName, tableDiscriminatorDetails) -> tableGroup.getTableReference(
fetchablePath, fetchablePath,
tableName, tableName,
false,
true true
) )
); );
@ -244,7 +242,6 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator
final TableReference tableReference = entityTableGroup.getTableReference( final TableReference tableReference = entityTableGroup.getTableReference(
entityTableGroup.getNavigablePath(), entityTableGroup.getNavigablePath(),
tableName, tableName,
false,
false false
); );

View File

@ -266,7 +266,7 @@ public class EmbeddableMappingTypeImpl extends AbstractEmbeddableMapping impleme
selectableMappings, selectableMappings,
inverseMappingType, inverseMappingType,
creationProcess, creationProcess,
valueMapping.getDeclaringType(), this,
attributeMappings attributeMappings
) )
); );

View File

@ -298,12 +298,12 @@ public class EmbeddedAttributeMapping
} }
final List<ColumnReference> columnReferences = CollectionHelper.arrayList( embeddableMappingType.getJdbcTypeCount() ); final List<ColumnReference> columnReferences = CollectionHelper.arrayList( embeddableMappingType.getJdbcTypeCount() );
final NavigablePath navigablePath = tableGroup.getNavigablePath().append( getNavigableRole().getNavigableName() ); 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( getEmbeddableTypeDescriptor().forEachSelectable(
(columnIndex, selection) -> { (columnIndex, selection) -> {
final TableReference tableReference = getContainingTableExpression().equals( selection.getContainingTableExpression() ) final TableReference tableReference = getContainingTableExpression().equals( selection.getContainingTableExpression() )
? defaultTableReference ? defaultTableReference
: tableGroup.resolveTableReference( navigablePath, selection.getContainingTableExpression() ); : tableGroup.resolveTableReference( navigablePath, this, selection.getContainingTableExpression() );
final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression( final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression(
tableReference, tableReference,
selection selection
@ -394,4 +394,17 @@ public class EmbeddedAttributeMapping
public boolean isSelectable() { public boolean isSelectable() {
return selectable; 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 );
}
} }

View File

@ -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.StandardVirtualTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; 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.TableReference;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -207,6 +208,7 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
final TableReference tableReference = tableGroup.resolveTableReference( final TableReference tableReference = tableGroup.resolveTableReference(
tableGroup.getNavigablePath() tableGroup.getNavigablePath()
.append( getNavigableRole().getNavigableName() ), .append( getNavigableRole().getNavigableName() ),
this,
selection.getContainingTableExpression() selection.getContainingTableExpression()
); );
expressions.add( expressions.add(
@ -322,4 +324,13 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
return FetchTiming.IMMEDIATE; 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 );
}
} }

View File

@ -16,6 +16,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.internal.util.MutableInteger; import org.hibernate.internal.util.MutableInteger;
import org.hibernate.metamodel.mapping.AssociationKey; import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart; import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; 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.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings; import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.query.sqm.ComparisonOperator; import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.spi.NavigablePath; 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.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; 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.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference; 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.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.predicate.Junction;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -171,6 +176,27 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
return targetSide.getModelPart().getEmbeddableTypeDescriptor().getEmbeddedValueMapping(); 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 @Override
public Side getKeySide() { public Side getKeySide() {
return keySide; return keySide;
@ -209,15 +235,35 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override @Override
public DomainResult<?> createKeyDomainResult( public DomainResult<?> createKeyDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup targetTableGroup,
FetchParent fetchParent, FetchParent fetchParent,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
assert isTargetTableGroup( targetTableGroup );
return createDomainResult( return createDomainResult(
navigablePath, navigablePath,
tableGroup, targetTableGroup,
null, null,
keyTable, Nature.KEY,
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,
null,
Nature.KEY,
fetchParent, fetchParent,
creationState creationState
); );
@ -226,70 +272,57 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override @Override
public DomainResult<?> createTargetDomainResult( public DomainResult<?> createTargetDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup targetTableGroup,
FetchParent fetchParent, FetchParent fetchParent,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
assert tableGroup.getTableReference( navigablePath, targetTable ) != null; assert isTargetTableGroup( targetTableGroup );
return createDomainResult( return createDomainResult(
navigablePath, navigablePath,
tableGroup, targetTableGroup,
null, null,
targetTable, Nature.TARGET,
targetSide.getModelPart(),
fetchParent, fetchParent,
creationState 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 @Override
public <T> DomainResult<T> createDomainResult( public <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup targetTableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
assert isTargetTableGroup( targetTableGroup );
return createDomainResult( return createDomainResult(
navigablePath, navigablePath,
tableGroup, targetTableGroup,
resultVariable, resultVariable,
keyTable, Nature.KEY,
keySide.getModelPart(),
null, null,
creationState 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 @Override
public void applySqlSelections( public void applySqlSelections(
NavigablePath navigablePath, NavigablePath navigablePath,
@ -311,15 +344,17 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
String resultVariable, String resultVariable,
String columnContainingTable, Nature nature,
EmbeddableValuedModelPart modelPart,
FetchParent fetchParent, FetchParent fetchParent,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final EmbeddableValuedModelPart modelPart;
final NavigablePath resultNavigablePath; final NavigablePath resultNavigablePath;
if ( modelPart == keySide.getModelPart() ) { if ( nature == Nature.KEY ) {
modelPart = keySide.getModelPart();
resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.PART_NAME ); resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.PART_NAME );
} }
else { else {
modelPart = targetSide.getModelPart();
resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.TARGET_PART_NAME ); resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.TARGET_PART_NAME );
} }
@ -343,7 +378,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
final Nature currentForeignKeyResolvingKey = creationState.getCurrentlyResolvingForeignKeyPart(); final Nature currentForeignKeyResolvingKey = creationState.getCurrentlyResolvingForeignKeyPart();
try { try {
creationState.setCurrentlyResolvingForeignKeyPart( keySide.getModelPart() == modelPart ? Nature.KEY : Nature.TARGET ); creationState.setCurrentlyResolvingForeignKeyPart( nature );
return new EmbeddableForeignKeyResultImpl<>( return new EmbeddableForeignKeyResultImpl<>(
resultNavigablePath, resultNavigablePath,
modelPart, modelPart,
@ -364,13 +399,11 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
SqlAstCreationState creationState) { SqlAstCreationState creationState) {
final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference( final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference(
targetSideTableGroup.getNavigablePath(), targetSideTableGroup.getNavigablePath(),
targetTable, targetTable
false
); );
final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference( final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference(
null, null,
keyTable, keyTable
false
); );
return generateJoinPredicate( lhsTableReference, rhsTableKeyReference, creationState ); return generateJoinPredicate( lhsTableReference, rhsTableKeyReference, creationState );

View File

@ -139,7 +139,7 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
selectableMappings, selectableMappings,
inverseMappingType, inverseMappingType,
creationProcess, creationProcess,
valueMapping.getDeclaringType(), this,
this.attributeMappings this.attributeMappings
) )
); );

View File

@ -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.LazyTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; 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.TableReference;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.type.EntityType; import org.hibernate.type.EntityType;
@ -74,7 +75,8 @@ import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelpe
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class ManyToManyCollectionPart extends AbstractEntityCollectionPart implements EntityAssociationMapping { public class ManyToManyCollectionPart extends AbstractEntityCollectionPart implements EntityAssociationMapping,
LazyTableGroup.ParentTableGroupUseChecker {
private ForeignKeyDescriptor foreignKey; private ForeignKeyDescriptor foreignKey;
private ValuedModelPart fkTargetModelPart; private ValuedModelPart fkTargetModelPart;
@ -292,24 +294,7 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
sqlAliasBase, sqlAliasBase,
creationState creationState
), ),
(np, tableExpression) -> { this,
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, explicitSourceAlias,
sqlAliasBase, sqlAliasBase,
@ -337,6 +322,11 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
return lazyTableGroup; return lazyTableGroup;
} }
@Override
public boolean canUseParentTableGroup(TableGroupProducer producer, NavigablePath navigablePath, ValuedModelPart valuedModelPart) {
return foreignKey.isKeyPart( valuedModelPart );
}
@Override @Override
public boolean hasPartitionedSelectionMapping() { public boolean hasPartitionedSelectionMapping() {
return foreignKey.hasPartitionedSelectionMapping(); return foreignKey.hasPartitionedSelectionMapping();

View File

@ -130,6 +130,11 @@ public class OneToManyCollectionPart extends AbstractEntityCollectionPart implem
return false; return false;
} }
@Override
public boolean containsTableReference(String tableExpression) {
return getAssociatedEntityMappingType().containsTableReference( tableExpression );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// TableGroupJoinProducer // TableGroupJoinProducer

View File

@ -31,6 +31,7 @@ import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PropertyBasedMapping; import org.hibernate.metamodel.mapping.PropertyBasedMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.proxy.HibernateProxy; 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.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; 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.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupProducer; import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.UnknownTableReferenceException; 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.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -179,6 +182,11 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
return targetSide.getModelPart(); return targetSide.getModelPart();
} }
@Override
public boolean isKeyPart(ValuedModelPart modelPart) {
return this == modelPart || keySide.getModelPart() == modelPart;
}
@Override @Override
public Side getKeySide() { public Side getKeySide() {
return keySide; return keySide;
@ -217,12 +225,32 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override @Override
public DomainResult<?> createKeyDomainResult( public DomainResult<?> createKeyDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup targetTableGroup,
FetchParent fetchParent, FetchParent fetchParent,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
assert isTargetTableGroup( targetTableGroup );
return createDomainResult( return createDomainResult(
navigablePath, navigablePath.append( ForeignKeyDescriptor.PART_NAME ),
tableGroup, 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(), keySide.getModelPart(),
fetchParent, fetchParent,
creationState creationState
@ -232,39 +260,53 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override @Override
public DomainResult<?> createTargetDomainResult( public DomainResult<?> createTargetDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup targetTableGroup,
FetchParent fetchParent, FetchParent fetchParent,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
assert isTargetTableGroup( targetTableGroup );
return createDomainResult( return createDomainResult(
navigablePath, navigablePath.append( ForeignKeyDescriptor.TARGET_PART_NAME ),
tableGroup, targetTableGroup,
targetSide.getModelPart(), targetSide.getModelPart(),
fetchParent, fetchParent,
creationState 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 @Override
public <T> DomainResult<T> createDomainResult( public <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup targetTableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { 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 @Override
@ -287,23 +329,16 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
private <T> DomainResult<T> createDomainResult( private <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
SelectableMapping selectableMapping, BasicValuedModelPart selectableMapping,
FetchParent fetchParent, FetchParent fetchParent,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState(); final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); 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; final TableReference tableReference;
try { try {
tableReference = tableGroup.resolveTableReference( tableReference = tableGroup.resolveTableReference(
resultNavigablePath, navigablePath,
selectableMapping,
selectableMapping.getContainingTableExpression() selectableMapping.getContainingTableExpression()
); );
} }
@ -356,13 +391,11 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
SqlAstCreationState creationState) { SqlAstCreationState creationState) {
final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference( final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference(
targetSideTableGroup.getNavigablePath(), targetSideTableGroup.getNavigablePath(),
targetSide.getModelPart().getContainingTableExpression(), targetSide.getModelPart().getContainingTableExpression()
false
); );
final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference( final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference(
null, null,
keySide.getModelPart().getContainingTableExpression(), keySide.getModelPart().getContainingTableExpression()
false
); );
return generateJoinPredicate( lhsTableReference, rhsTableKeyReference, creationState ); return generateJoinPredicate( lhsTableReference, rhsTableKeyReference, creationState );

View File

@ -6,18 +6,85 @@
*/ */
package org.hibernate.metamodel.mapping.internal; 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.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.PropertyBasedMapping; import org.hibernate.metamodel.mapping.PropertyBasedMapping;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.MutabilityPlan;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface SingleAttributeIdentifierMapping extends EntityIdentifierMapping, PropertyBasedMapping { public interface SingleAttributeIdentifierMapping extends EntityIdentifierMapping, PropertyBasedMapping,
AttributeMapping, AttributeMetadata {
/** /**
* Access to the identifier attribute's PropertyAccess * Access to the identifier attribute's PropertyAccess
*/ */
PropertyAccess getPropertyAccess(); PropertyAccess getPropertyAccess();
String getAttributeName(); 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;
}
} }

View File

@ -51,6 +51,7 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectablePath; import org.hibernate.metamodel.mapping.SelectablePath;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.VirtualModelPart; import org.hibernate.metamodel.mapping.VirtualModelPart;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.collection.AbstractCollectionPersister; import org.hibernate.persister.collection.AbstractCollectionPersister;
@ -106,7 +107,8 @@ import org.hibernate.type.Type;
*/ */
public class ToOneAttributeMapping public class ToOneAttributeMapping
extends AbstractSingularAttributeMapping extends AbstractSingularAttributeMapping
implements EntityValuedFetchable, EntityAssociationMapping, TableGroupJoinProducer { implements EntityValuedFetchable, EntityAssociationMapping, TableGroupJoinProducer,
LazyTableGroup.ParentTableGroupUseChecker {
public enum Cardinality { public enum Cardinality {
ONE_TO_ONE, ONE_TO_ONE,
@ -409,18 +411,18 @@ public class ToOneAttributeMapping
compositeType.getSubtypes()[0], compositeType.getSubtypes()[0],
declaringEntityPersister.getFactory() declaringEntityPersister.getFactory()
); );
}
else {
this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME;
addPrefixedPropertyNames( addPrefixedPropertyNames(
targetKeyPropertyNames, targetKeyPropertyNames,
null, EntityIdentifierMapping.ROLE_LOCAL_NAME,
propertyType, propertyType,
declaringEntityPersister.getFactory() declaringEntityPersister.getFactory()
); );
}
else {
this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME;
addPrefixedPropertyPaths( addPrefixedPropertyPaths(
targetKeyPropertyNames, targetKeyPropertyNames,
targetKeyPropertyName, null,
propertyType, propertyType,
declaringEntityPersister.getFactory() declaringEntityPersister.getFactory()
); );
@ -440,7 +442,7 @@ public class ToOneAttributeMapping
else if ( bootValue.isReferenceToPrimaryKey() ) { else if ( bootValue.isReferenceToPrimaryKey() ) {
this.targetKeyPropertyName = referencedPropertyName; this.targetKeyPropertyName = referencedPropertyName;
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 ); final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
addPrefixedPropertyPaths( addPrefixedPropertyNames(
targetKeyPropertyNames, targetKeyPropertyNames,
targetKeyPropertyName, targetKeyPropertyName,
bootValue.getType(), bootValue.getType(),
@ -463,30 +465,41 @@ public class ToOneAttributeMapping
compositeType.getSubtypes()[0], compositeType.getSubtypes()[0],
declaringEntityPersister.getFactory() declaringEntityPersister.getFactory()
); );
addPrefixedPropertyNames(
targetKeyPropertyNames,
EntityIdentifierMapping.ROLE_LOCAL_NAME,
propertyType,
declaringEntityPersister.getFactory()
);
this.targetKeyPropertyNames = targetKeyPropertyNames; this.targetKeyPropertyNames = targetKeyPropertyNames;
} }
else { else {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
this.targetKeyPropertyName = referencedPropertyName; this.targetKeyPropertyName = referencedPropertyName;
final String mapsIdAttributeName; final String mapsIdAttributeName;
// If there is a "virtual property" for a non-PK join mapping, we try to see if the columns match the // 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 // primary key columns and if so, we add the primary key property name as target key property
if ( ( mapsIdAttributeName = findMapsIdPropertyName( entityMappingType, referencedPropertyName ) ) != null ) { if ( ( mapsIdAttributeName = findMapsIdPropertyName( entityMappingType, referencedPropertyName ) ) != null ) {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
targetKeyPropertyNames.add( targetKeyPropertyName );
addPrefixedPropertyPaths( addPrefixedPropertyPaths(
targetKeyPropertyNames, targetKeyPropertyNames,
mapsIdAttributeName, mapsIdAttributeName,
entityMappingType.getEntityPersister().getIdentifierType(), entityMappingType.getEntityPersister().getIdentifierType(),
declaringEntityPersister.getFactory() declaringEntityPersister.getFactory()
); );
this.targetKeyPropertyNames = targetKeyPropertyNames;
} }
else { addPrefixedPropertyNames(
this.targetKeyPropertyNames = Set.of( targetKeyPropertyNames,
targetKeyPropertyName, targetKeyPropertyName,
ForeignKeyDescriptor.PART_NAME propertyType,
declaringEntityPersister.getFactory()
); );
} addPrefixedPropertyNames(
targetKeyPropertyNames,
ForeignKeyDescriptor.PART_NAME,
propertyType,
declaringEntityPersister.getFactory()
);
this.targetKeyPropertyNames = targetKeyPropertyNames;
} }
} }
} }
@ -653,7 +666,7 @@ public class ToOneAttributeMapping
return null; return null;
} }
private static void addPrefixedPropertyPaths( public static void addPrefixedPropertyPaths(
Set<String> targetKeyPropertyNames, Set<String> targetKeyPropertyNames,
String prefix, String prefix,
Type type, Type type,
@ -715,21 +728,40 @@ public class ToOneAttributeMapping
propertyName = entityType.getRHSUniqueKeyPropertyName(); propertyName = entityType.getRHSUniqueKeyPropertyName();
} }
final String newPrefix; final String newPrefix;
final String newPkPrefix;
final String newFkPrefix; final String newFkPrefix;
if ( prefix == null ) { if ( prefix == null ) {
newPrefix = propertyName; newPrefix = propertyName;
newPkPrefix = propertyName + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME;
newFkPrefix = ForeignKeyDescriptor.PART_NAME; newFkPrefix = ForeignKeyDescriptor.PART_NAME;
} }
else if ( propertyName == null ) { else if ( propertyName == null ) {
newPrefix = prefix; newPrefix = prefix;
newPkPrefix = prefix + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME;
newFkPrefix = prefix + "." + ForeignKeyDescriptor.PART_NAME; newFkPrefix = prefix + "." + ForeignKeyDescriptor.PART_NAME;
} }
else { else {
newPrefix = prefix + "." + propertyName; newPrefix = prefix + "." + propertyName;
newPkPrefix = prefix + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME;
newFkPrefix = prefix + "." + ForeignKeyDescriptor.PART_NAME; newFkPrefix = prefix + "." + ForeignKeyDescriptor.PART_NAME;
} }
addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, identifierOrUniqueKeyType, factory ); addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, identifierOrUniqueKeyType, factory );
addPrefixedPropertyNames( targetKeyPropertyNames, newPkPrefix, identifierOrUniqueKeyType, factory );
addPrefixedPropertyNames( targetKeyPropertyNames, newFkPrefix, 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 { else {
fkPart = foreignKeyDescriptor.getTargetPart(); 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 ( (ModelPartContainer) fkPart ).findSubPart( name, targetType );
} }
return fkPart; return fkPart;
@ -940,14 +976,23 @@ public class ToOneAttributeMapping
assert !creationState.isResolvingCircularFetch(); assert !creationState.isResolvingCircularFetch();
try { try {
creationState.setResolvingCircularFetch( true ); creationState.setResolvingCircularFetch( true );
foreignKeyDomainResult = foreignKeyDescriptor.createDomainResult( if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) {
foreignKeyDomainResult = foreignKeyDescriptor.createKeyDomainResult(
fetchablePath, fetchablePath,
parentTableGroup, createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ),
sideNature,
fetchParent, fetchParent,
creationState creationState
); );
} }
else {
foreignKeyDomainResult = foreignKeyDescriptor.createTargetDomainResult(
fetchablePath,
parentTableGroup,
fetchParent,
creationState
);
}
}
finally { finally {
creationState.setResolvingCircularFetch( false ); creationState.setResolvingCircularFetch( false );
} }
@ -1154,10 +1199,15 @@ public class ToOneAttributeMapping
// For now, we don't do this if the key table is nullable to avoid an additional join // For now, we don't do this if the key table is nullable to avoid an additional join
if ( sideNature == ForeignKeyDescriptor.Nature.KEY && !isKeyTableNullable ) { if ( sideNature == ForeignKeyDescriptor.Nature.KEY && !isKeyTableNullable ) {
keyDomainResult = foreignKeyDescriptor.createKeyDomainResult( keyDomainResult = foreignKeyDescriptor.createKeyDomainResult(
fetchablePath,
createTableGroupForDelayedFetch(
fetchablePath, fetchablePath,
creationState.getSqlAstCreationState() creationState.getSqlAstCreationState()
.getFromClauseAccess() .getFromClauseAccess()
.findTableGroup( realFetchParent.getNavigablePath() ), .findTableGroup( realFetchParent.getNavigablePath() ),
null,
creationState
),
fetchParent, fetchParent,
creationState creationState
); );
@ -1207,14 +1257,24 @@ public class ToOneAttributeMapping
else { else {
realParent = parentNavigablePath; realParent = parentNavigablePath;
} }
final TableGroup tableGroup = fromClauseAccess.getTableGroup( realParent ); final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( realParent );
final DomainResult<?> domainResult = foreignKeyDescriptor.createDomainResult( final DomainResult<?> domainResult;
if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) {
domainResult = foreignKeyDescriptor.createKeyDomainResult(
fetchablePath, fetchablePath,
tableGroup, createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ),
sideNature,
fetchParent, fetchParent,
creationState creationState
); );
}
else {
domainResult = foreignKeyDescriptor.createTargetDomainResult(
fetchablePath,
parentTableGroup,
fetchParent,
creationState
);
}
if ( fetchTiming == FetchTiming.IMMEDIATE ) { if ( fetchTiming == FetchTiming.IMMEDIATE ) {
return buildEntityFetchSelect( return buildEntityFetchSelect(
fetchParent, fetchParent,
@ -1421,7 +1481,7 @@ public class ToOneAttributeMapping
if ( notFoundAction != null || !isInternalLoadNullable ) { if ( notFoundAction != null || !isInternalLoadNullable ) {
keyResult = foreignKeyDescriptor.createKeyDomainResult( keyResult = foreignKeyDescriptor.createKeyDomainResult(
fetchablePath, fetchablePath,
parentTableGroup, tableGroup,
fetchParent, fetchParent,
creationState creationState
); );
@ -1484,13 +1544,29 @@ public class ToOneAttributeMapping
else { else {
side = this.sideNature; side = this.sideNature;
} }
final DomainResult<?> keyResult = foreignKeyDescriptor.createDomainResult( 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, fetchablePath,
parentTableGroup, tableGroup,
side,
fetchParent, fetchParent,
creationState 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 ); final boolean selectByUniqueKey = isSelectByUniqueKey( side );
// Consider all associations annotated with @NotFound as EAGER // 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) { private boolean isSelectByUniqueKey(ForeignKeyDescriptor.Nature side) {
if ( side == ForeignKeyDescriptor.Nature.KEY ) { if ( side == ForeignKeyDescriptor.Nature.KEY ) {
// case 1.2 // case 1.2
@ -1598,7 +1706,7 @@ public class ToOneAttributeMapping
@Override @Override
public <T> DomainResult<T> createSnapshotDomainResult( public <T> DomainResult<T> createSnapshotDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup parentTableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
// We need a join if either // We need a join if either
@ -1615,31 +1723,36 @@ public class ToOneAttributeMapping
np -> { np -> {
final TableGroupJoin tableGroupJoin = createTableGroupJoin( final TableGroupJoin tableGroupJoin = createTableGroupJoin(
navigablePath, navigablePath,
tableGroup, parentTableGroup,
null, null,
null, null,
getDefaultSqlAstJoinType( tableGroup ), getDefaultSqlAstJoinType( parentTableGroup ),
true, true,
false, false,
creationState.getSqlAstCreationState() creationState.getSqlAstCreationState()
); );
tableGroup.addTableGroupJoin( tableGroupJoin ); parentTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }
); );
} }
else { else {
tableGroupToUse = tableGroup; tableGroupToUse = createTableGroupForDelayedFetch(
navigablePath,
parentTableGroup,
resultVariable,
creationState
);
} }
if ( hasNotFoundAction() ) { if ( hasNotFoundAction() ) {
assert tableGroupToUse != tableGroup; assert tableGroupToUse != parentTableGroup;
//noinspection unchecked //noinspection unchecked
return new NotFoundSnapshotResult( return new NotFoundSnapshotResult(
navigablePath, navigablePath,
this, this,
parentTableGroup,
tableGroupToUse, tableGroupToUse,
tableGroup,
creationState creationState
); );
} }
@ -1727,6 +1840,11 @@ public class ToOneAttributeMapping
return predicate == null || foreignKeyDescriptor.isSimpleJoinPredicate( predicate ); return predicate == null || foreignKeyDescriptor.isSimpleJoinPredicate( predicate );
} }
@Override
public boolean containsTableReference(String tableExpression) {
return getEntityMappingType().containsTableReference( tableExpression );
}
@Override @Override
public int getNumberOfFetchables() { public int getNumberOfFetchables() {
return getEntityMappingType().getNumberOfFetchables(); return getEntityMappingType().getNumberOfFetchables();
@ -1767,8 +1885,18 @@ public class ToOneAttributeMapping
embeddablePathSb = new StringBuilder(); embeddablePathSb = new StringBuilder();
} }
embeddablePathSb.insert( 0, parentContainer.getPartName() + "." ); embeddablePathSb.insert( 0, parentContainer.getPartName() + "." );
parentTableGroup = fromClauseAccess.findTableGroup( parentTableGroup.getNavigablePath().getParent() ); final NavigablePath parentNavigablePath = parentTableGroup.getNavigablePath();
parentContainer = parentTableGroup.getModelPart(); 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 { else {
break; break;
@ -1803,28 +1931,7 @@ public class ToOneAttributeMapping
indexTableGroup, indexTableGroup,
fetched, fetched,
pluralTableGroup, pluralTableGroup,
(np, tableExpression) -> { this
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 );
}
), ),
null null
); );
@ -1850,8 +1957,11 @@ public class ToOneAttributeMapping
lazyTableGroup, lazyTableGroup,
null null
); );
final TableReference lhsTableReference = lhs.resolveTableReference(
final TableReference lhsTableReference = lhs.resolveTableReference( navigablePath, identifyingColumnsTableExpression ); navigablePath,
this,
identifyingColumnsTableExpression
);
lazyTableGroup.setTableGroupInitializerCallback( lazyTableGroup.setTableGroupInitializerCallback(
tableGroup -> { tableGroup -> {
@ -1932,7 +2042,19 @@ public class ToOneAttributeMapping
TableGroup realParentTableGroup = lhs; TableGroup realParentTableGroup = lhs;
final FromClauseAccess fromClauseAccess = creationState.getFromClauseAccess(); final FromClauseAccess fromClauseAccess = creationState.getFromClauseAccess();
while ( realParentTableGroup.getModelPart() instanceof EmbeddableValuedModelPart ) { 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; final TableGroupProducer tableGroupProducer;
@ -1958,28 +2080,7 @@ public class ToOneAttributeMapping
sqlAliasBase, sqlAliasBase,
creationState creationState
), ),
(np, tableExpression) -> { this,
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 );
},
tableGroupProducer, tableGroupProducer,
explicitSourceAlias, explicitSourceAlias,
sqlAliasBase, sqlAliasBase,
@ -2015,6 +2116,16 @@ public class ToOneAttributeMapping
return lazyTableGroup; 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) { private void initializeIfNeeded(TableGroup lhs, SqlAstJoinType sqlAstJoinType, TableGroup tableGroup) {
if ( sqlAstJoinType == SqlAstJoinType.INNER && ( isNullable || !lhs.canUseInnerJoins() ) ) { if ( sqlAstJoinType == SqlAstJoinType.INNER && ( isNullable || !lhs.canUseInnerJoins() ) ) {
// Force initialization of the underlying table group join to retain cardinality // Force initialization of the underlying table group join to retain cardinality

View File

@ -89,7 +89,7 @@ public class VirtualIdEmbeddable extends AbstractEmbeddableMapping implements Id
selectableMappings, selectableMappings,
inverseMappingType, inverseMappingType,
creationProcess, creationProcess,
valueMapping.getDeclaringType(), this,
this.attributeMappings this.attributeMappings
) )
); );

View File

@ -16,6 +16,7 @@ import org.hibernate.metamodel.internal.MetadataContext;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.model.domain.AnyMappingDomainType; import org.hibernate.metamodel.model.domain.AnyMappingDomainType;
import org.hibernate.metamodel.model.domain.DomainType; 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.ManagedDomainType;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.metamodel.model.domain.SimpleDomainType; import org.hibernate.metamodel.model.domain.SimpleDomainType;
@ -197,6 +198,13 @@ public class SingularAttributeImpl<D,J>
if ( parent.getReferencedPathSource() instanceof PluralPersistentAttribute<?, ?, ?> ) { if ( parent.getReferencedPathSource() instanceof PluralPersistentAttribute<?, ?, ?> ) {
navigablePath = navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ); 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() ); return new EntityIdentifierNavigablePath( navigablePath, SqmCreationHelper.determineAlias( alias ), getName() );
} }
} }

View File

@ -1327,7 +1327,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
// Table references not appearing in this set can later be pruned away // Table references not appearing in this set can later be pruned away
for ( String subclassTableName : subclassTableNames ) { for ( String subclassTableName : subclassTableNames ) {
final TableReference tableReference = final TableReference tableReference =
tableGroup.getTableReference( null, subclassTableName, false, false ); tableGroup.getTableReference( null, subclassTableName, false );
if ( tableReference == null ) { if ( tableReference == null ) {
throw new UnknownTableReferenceException( getRootTableName(), "Couldn't find table reference" ); throw new UnknownTableReferenceException( getRootTableName(), "Couldn't find table reference" );
} }

View File

@ -11,6 +11,8 @@ import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.metamodel.mapping.JdbcMapping; 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.property.access.spi.PropertyAccess;
import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.SqmExpressible;
@ -25,11 +27,12 @@ public class AnonymousTupleBasicEntityIdentifierMapping
private final BasicEntityIdentifierMapping delegate; private final BasicEntityIdentifierMapping delegate;
public AnonymousTupleBasicEntityIdentifierMapping( public AnonymousTupleBasicEntityIdentifierMapping(
MappingType declaringType,
String selectionExpression, String selectionExpression,
SqmExpressible<?> expressible, SqmExpressible<?> expressible,
JdbcMapping jdbcMapping, JdbcMapping jdbcMapping,
BasicEntityIdentifierMapping delegate) { BasicEntityIdentifierMapping delegate) {
super( delegate.getAttributeName(), selectionExpression, expressible, jdbcMapping, -1 ); super( declaringType, delegate.getAttributeName(), selectionExpression, expressible, jdbcMapping, -1 );
this.delegate = delegate; this.delegate = delegate;
} }
@ -67,4 +70,9 @@ public class AnonymousTupleBasicEntityIdentifierMapping
public String getAttributeName() { public String getAttributeName() {
return getPartName(); return getPartName();
} }
@Override
public ManagedMappingType getDeclaringType() {
return (ManagedMappingType) super.getDeclaringType();
}
} }

View File

@ -18,7 +18,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType; 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.mapping.SelectableConsumer;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.query.sqm.SqmExpressible; import org.hibernate.query.sqm.SqmExpressible;
@ -41,9 +41,10 @@ import org.hibernate.type.descriptor.java.JavaType;
* @author Christian Beikov * @author Christian Beikov
*/ */
@Incubating @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 static final FetchOptions FETCH_OPTIONS = FetchOptions.valueOf( FetchTiming.IMMEDIATE, FetchStyle.JOIN );
private final MappingType declaringType;
private final String partName; private final String partName;
private final String selectionExpression; private final String selectionExpression;
private final SqmExpressible<?> expressible; private final SqmExpressible<?> expressible;
@ -51,11 +52,13 @@ public class AnonymousTupleBasicValuedModelPart implements ModelPart, MappingTyp
private final int fetchableIndex; private final int fetchableIndex;
public AnonymousTupleBasicValuedModelPart( public AnonymousTupleBasicValuedModelPart(
MappingType declaringType,
String partName, String partName,
String selectionExpression, String selectionExpression,
SqmExpressible<?> expressible, SqmExpressible<?> expressible,
JdbcMapping jdbcMapping, JdbcMapping jdbcMapping,
int fetchableIndex) { int fetchableIndex) {
this.declaringType = declaringType;
this.partName = partName; this.partName = partName;
this.selectionExpression = selectionExpression; this.selectionExpression = selectionExpression;
this.expressible = expressible; this.expressible = expressible;
@ -78,6 +81,11 @@ public class AnonymousTupleBasicValuedModelPart implements ModelPart, MappingTyp
return expressible.getExpressibleJavaType(); return expressible.getExpressibleJavaType();
} }
@Override
public MappingType getDeclaringType() {
return declaringType;
}
@Override @Override
public String getPartName() { public String getPartName() {
return partName; return partName;
@ -217,6 +225,7 @@ public class AnonymousTupleBasicValuedModelPart implements ModelPart, MappingTyp
final SqlExpressionResolver expressionResolver = creationState.getSqlExpressionResolver(); final SqlExpressionResolver expressionResolver = creationState.getSqlExpressionResolver();
final TableReference tableReference = tableGroup.resolveTableReference( final TableReference tableReference = tableGroup.resolveTableReference(
navigablePath, navigablePath,
this,
getContainingTableExpression() getContainingTableExpression()
); );
final Expression expression = expressionResolver.resolveSqlExpression( tableReference, this ); final Expression expression = expressionResolver.resolveSqlExpression( tableReference, this );

View File

@ -9,6 +9,7 @@ package org.hibernate.query.derived;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; 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.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy; import org.hibernate.metamodel.spi.EmbeddableRepresentationStrategy;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.Clause; 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.sql.results.graph.embeddable.internal.EmbeddableResultImpl;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
import jakarta.persistence.metamodel.Attribute;
/** /**
* @author Christian Beikov * @author Christian Beikov
*/ */
@ -76,12 +81,25 @@ public class AnonymousTupleEmbeddableValuedModelPart implements EmbeddableValued
private final int fetchableIndex; private final int fetchableIndex;
public AnonymousTupleEmbeddableValuedModelPart( public AnonymousTupleEmbeddableValuedModelPart(
Map<String, ModelPart> modelPartMap, SqmExpressible<?> sqmExpressible,
List<SqlSelection> sqlSelections,
int selectionIndex,
String selectionExpression,
Set<String> compatibleTableExpressions,
Set<Attribute<?, ?>> attributes,
DomainType<?> domainType, DomainType<?> domainType,
String componentName, String componentName,
EmbeddableValuedModelPart existingModelPartContainer, EmbeddableValuedModelPart existingModelPartContainer,
int fetchableIndex) { int fetchableIndex) {
this.modelPartMap = modelPartMap; this.modelPartMap = createModelParts(
sqmExpressible,
sqlSelections,
selectionIndex,
selectionExpression,
compatibleTableExpressions,
attributes,
existingModelPartContainer
);
this.modelParts = modelPartMap.values().toArray( new ModelPart[0] ); this.modelParts = modelPartMap.values().toArray( new ModelPart[0] );
this.domainType = domainType; this.domainType = domainType;
this.componentName = componentName; this.componentName = componentName;
@ -89,6 +107,38 @@ public class AnonymousTupleEmbeddableValuedModelPart implements EmbeddableValued
this.fetchableIndex = fetchableIndex; this.fetchableIndex = fetchableIndex;
} }
private Map<String, ModelPart> createModelParts(
SqmExpressible<?> sqmExpressible,
List<SqlSelection> sqlSelections,
int selectionIndex,
String selectionExpression,
Set<String> compatibleTableExpressions,
Set<Attribute<?, ?>> attributes,
EmbeddableValuedModelPart modelPartContainer) {
final Map<String, ModelPart> 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 @Override
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) { public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
return modelPartMap.get( name ); return modelPartMap.get( name );
@ -281,7 +331,7 @@ public class AnonymousTupleEmbeddableValuedModelPart implements EmbeddableValued
SqlAstCreationState sqlAstCreationState) { SqlAstCreationState sqlAstCreationState) {
final List<ColumnReference> columnReferences = CollectionHelper.arrayList( getJdbcTypeCount() ); final List<ColumnReference> columnReferences = CollectionHelper.arrayList( getJdbcTypeCount() );
final NavigablePath navigablePath = tableGroup.getNavigablePath().append( componentName ); 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 ) { for ( ModelPart modelPart : modelParts ) {
modelPart.forEachSelectable( modelPart.forEachSelectable(
(columnIndex, selection) -> { (columnIndex, selection) -> {

View File

@ -6,17 +6,26 @@
*/ */
package org.hibernate.query.derived; package org.hibernate.query.derived;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.engine.spi.IdentifierValue; import org.hibernate.engine.spi.IdentifierValue;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping; import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping; import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.property.access.spi.PropertyAccess; 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 * @author Christian Beikov
@ -28,14 +37,23 @@ public class AnonymousTupleEmbeddedEntityIdentifierMapping extends AnonymousTupl
private final CompositeIdentifierMapping delegate; private final CompositeIdentifierMapping delegate;
public AnonymousTupleEmbeddedEntityIdentifierMapping( public AnonymousTupleEmbeddedEntityIdentifierMapping(
Map<String, ModelPart> modelParts, SqmExpressible<?> sqmExpressible,
List<SqlSelection> sqlSelections,
int selectionIndex,
String selectionExpression,
Set<String> compatibleTableExpressions,
Set<Attribute<?, ?>> attributes,
DomainType<?> domainType, DomainType<?> domainType,
String componentName,
CompositeIdentifierMapping delegate) { CompositeIdentifierMapping delegate) {
super( super(
modelParts, sqmExpressible,
sqlSelections,
selectionIndex,
selectionExpression,
compatibleTableExpressions,
attributes,
domainType, domainType,
componentName, delegate.getAttributeName(),
delegate, delegate,
-1 -1
); );
@ -72,6 +90,11 @@ public class AnonymousTupleEmbeddedEntityIdentifierMapping extends AnonymousTupl
return ((SingleAttributeIdentifierMapping) delegate).getPropertyAccess(); return ((SingleAttributeIdentifierMapping) delegate).getPropertyAccess();
} }
@Override
public int compare(Object value1, Object value2) {
return super.compare( value1, value2 );
}
@Override @Override
public String getAttributeName() { public String getAttributeName() {
return getPartName(); return getPartName();

View File

@ -40,6 +40,7 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.TableDetails; 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.OneToManyCollectionPart;
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping; import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; 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.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer; 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.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -74,7 +76,8 @@ import static org.hibernate.internal.util.collections.CollectionHelper.arrayList
*/ */
@Incubating @Incubating
public class AnonymousTupleEntityValuedModelPart public class AnonymousTupleEntityValuedModelPart
implements EntityValuedModelPart, EntityMappingType, TableGroupJoinProducer { implements EntityValuedModelPart, EntityMappingType, TableGroupJoinProducer, ValuedModelPart,
LazyTableGroup.ParentTableGroupUseChecker {
private final EntityIdentifierMapping identifierMapping; private final EntityIdentifierMapping identifierMapping;
private final DomainType<?> domainType; private final DomainType<?> domainType;
@ -115,7 +118,7 @@ public class AnonymousTupleEntityValuedModelPart
@Override @Override
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) { public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) { if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) {
if ( ( (SingleAttributeIdentifierMapping) identifierMapping ).getAttributeName().equals( name ) ) { if ( identifierMapping.getAttributeName().equals( name ) ) {
return identifierMapping; return identifierMapping;
} }
} }
@ -141,6 +144,11 @@ public class AnonymousTupleEntityValuedModelPart
return this; return this;
} }
@Override
public MappingType getMappedType() {
return getPartMappingType();
}
@Override @Override
public JavaType<?> getJavaType() { public JavaType<?> getJavaType() {
return domainType.getExpressibleJavaType(); return domainType.getExpressibleJavaType();
@ -151,6 +159,11 @@ public class AnonymousTupleEntityValuedModelPart
return componentName; return componentName;
} }
@Override
public String getContainingTableExpression() {
return "";
}
@Override @Override
public int getJdbcTypeCount() { public int getJdbcTypeCount() {
return delegate.getJdbcTypeCount(); return delegate.getJdbcTypeCount();
@ -227,6 +240,11 @@ public class AnonymousTupleEntityValuedModelPart
return identifierMapping.forEachSelectable( offset, consumer ); return identifierMapping.forEachSelectable( offset, consumer );
} }
@Override
public SelectableMapping getSelectable(int columnIndex) {
return identifierMapping.getSelectable( columnIndex );
}
@Override @Override
public JavaType<?> getMappedJavaType() { public JavaType<?> getMappedJavaType() {
return delegate.getJavaType(); return delegate.getJavaType();
@ -375,8 +393,7 @@ public class AnonymousTupleEntityValuedModelPart
final SelectableMapping targetMapping = targetMappings.get( i ); final SelectableMapping targetMapping = targetMappings.get( i );
final TableReference targetTableReference = tg.resolveTableReference( final TableReference targetTableReference = tg.resolveTableReference(
null, null,
targetMapping.getContainingTableExpression(), targetMapping.getContainingTableExpression()
false
); );
predicateConsumer.accept( predicateConsumer.accept(
new ComparisonPredicate( new ComparisonPredicate(
@ -444,7 +461,6 @@ public class AnonymousTupleEntityValuedModelPart
creationState.getSqlAliasBaseGenerator() creationState.getSqlAliasBaseGenerator()
); );
final boolean canUseInnerJoin = sqlAstJoinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins(); final boolean canUseInnerJoin = sqlAstJoinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins();
final EntityPersister entityPersister = delegate.getEntityMappingType().getEntityPersister();
final LazyTableGroup lazyTableGroup = new LazyTableGroup( final LazyTableGroup lazyTableGroup = new LazyTableGroup(
canUseInnerJoin, canUseInnerJoin,
navigablePath, navigablePath,
@ -457,23 +473,7 @@ public class AnonymousTupleEntityValuedModelPart
sqlAliasBase, sqlAliasBase,
creationState creationState
), ),
(np, tableExpression) -> { this,
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, explicitSourceAlias,
sqlAliasBase, sqlAliasBase,
@ -490,6 +490,22 @@ public class AnonymousTupleEntityValuedModelPart
return lazyTableGroup; 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 @Override
public String getSqlAliasStem() { public String getSqlAliasStem() {
return getPartName(); return getPartName();
@ -707,4 +723,9 @@ public class AnonymousTupleEntityValuedModelPart
? ( (TableGroupJoinProducer) delegate ).isSimpleJoinPredicate( predicate ) ? ( (TableGroupJoinProducer) delegate ).isSimpleJoinPredicate( predicate )
: false; : false;
} }
@Override
public boolean containsTableReference(String tableExpression) {
return ( (TableGroupProducer) delegate ).containsTableReference( tableExpression );
}
} }

View File

@ -6,7 +6,9 @@
*/ */
package org.hibernate.query.derived; package org.hibernate.query.derived;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.engine.FetchStyle; 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.IdClassEmbeddable;
import org.hibernate.metamodel.mapping.internal.VirtualIdEmbeddable; import org.hibernate.metamodel.mapping.internal.VirtualIdEmbeddable;
import org.hibernate.metamodel.model.domain.DomainType; 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 * @author Christian Beikov
@ -30,12 +36,22 @@ public class AnonymousTupleNonAggregatedEntityIdentifierMapping extends Anonymou
private final NonAggregatedIdentifierMapping delegate; private final NonAggregatedIdentifierMapping delegate;
public AnonymousTupleNonAggregatedEntityIdentifierMapping( public AnonymousTupleNonAggregatedEntityIdentifierMapping(
Map<String, ModelPart> modelParts, SqmExpressible<?> sqmExpressible,
List<SqlSelection> sqlSelections,
int selectionIndex,
String selectionExpression,
Set<String> compatibleTableExpressions,
Set<Attribute<?, ?>> attributes,
DomainType<?> domainType, DomainType<?> domainType,
String componentName, String componentName,
NonAggregatedIdentifierMapping delegate) { NonAggregatedIdentifierMapping delegate) {
super( super(
modelParts, sqmExpressible,
sqlSelections,
selectionIndex,
selectionExpression,
compatibleTableExpressions,
attributes,
domainType, domainType,
componentName, componentName,
delegate, delegate,

View File

@ -36,7 +36,6 @@ import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ManagedDomainType; import org.hibernate.metamodel.model.domain.ManagedDomainType;
import org.hibernate.metamodel.model.domain.NavigableRole; 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.SqmExpressible;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode; import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
@ -92,6 +91,7 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
final SqmPath<?> sqmPath = (SqmPath<?>) selectableNode; final SqmPath<?> sqmPath = (SqmPath<?>) selectableNode;
final TableGroup tableGroup = fromClauseAccess.findTableGroup( sqmPath.getNavigablePath() ); final TableGroup tableGroup = fromClauseAccess.findTableGroup( sqmPath.getNavigablePath() );
modelPart = createModelPart( modelPart = createModelPart(
this,
selectableNode.getExpressible(), selectableNode.getExpressible(),
sqmPath.getNodeType().getSqmPathType(), sqmPath.getNodeType().getSqmPathType(),
sqlSelections, sqlSelections,
@ -105,6 +105,7 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
} }
else { else {
modelPart = new AnonymousTupleBasicValuedModelPart( modelPart = new AnonymousTupleBasicValuedModelPart(
this,
partName, partName,
partName, partName,
selectableNode.getExpressible(), selectableNode.getExpressible(),
@ -129,7 +130,8 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
return tableGroup.getModelPart(); return tableGroup.getModelPart();
} }
private ModelPart createModelPart( public static ModelPart createModelPart(
MappingType mappingType,
SqmExpressible<?> sqmExpressible, SqmExpressible<?> sqmExpressible,
DomainType<?> domainType, DomainType<?> domainType,
List<SqlSelection> sqlSelections, List<SqlSelection> sqlSelections,
@ -145,41 +147,24 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
.getIdentifierMapping(); .getIdentifierMapping();
final EntityIdentifierMapping newIdentifierMapping; final EntityIdentifierMapping newIdentifierMapping;
if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) { if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) {
final String attributeName = identifierMapping.getAttributeName();
if ( identifierMapping.getPartMappingType() instanceof ManagedMappingType ) { if ( identifierMapping.getPartMappingType() instanceof ManagedMappingType ) {
//noinspection unchecked //noinspection unchecked
final Set<Attribute<?, ?>> attributes = (Set<Attribute<?, ?>>) ( (ManagedDomainType<?>) ( (EntityDomainType<?>) domainType ).getIdentifierDescriptor().getSqmPathType() ).getAttributes(); final Set<Attribute<?, ?>> attributes = (Set<Attribute<?, ?>>) ( (ManagedDomainType<?>) ( (EntityDomainType<?>) domainType ).getIdentifierDescriptor().getSqmPathType() ).getAttributes();
final Map<String, ModelPart> modelParts = CollectionHelper.linkedMapOfSize( attributes.size() ); newIdentifierMapping = new AnonymousTupleEmbeddedEntityIdentifierMapping(
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, sqmExpressible,
attributeType,
sqlSelections, sqlSelections,
selectionIndex, selectionIndex,
selectionExpression + "_" + attributeName + "_" + attribute.getName(), selectionExpression + "_" + identifierMapping.getAttributeName(),
attribute.getName(),
modelPartContainer.findSubPart( attribute.getName(), null ),
compatibleTableExpressions, compatibleTableExpressions,
index++ attributes,
);
modelParts.put( modelPart.getPartName(), modelPart );
}
newIdentifierMapping = new AnonymousTupleEmbeddedEntityIdentifierMapping(
modelParts,
domainType, domainType,
attributeName,
(CompositeIdentifierMapping) identifierMapping (CompositeIdentifierMapping) identifierMapping
); );
} }
else { else {
newIdentifierMapping = new AnonymousTupleBasicEntityIdentifierMapping( newIdentifierMapping = new AnonymousTupleBasicEntityIdentifierMapping(
selectionExpression + "_" + attributeName, mappingType,
selectionExpression + "_" + identifierMapping.getAttributeName(),
sqmExpressible, sqmExpressible,
sqlSelections.get( selectionIndex ).getExpressionType().getSingleJdbcMapping(), sqlSelections.get( selectionIndex ).getExpressionType().getSingleJdbcMapping(),
(BasicEntityIdentifierMapping) identifierMapping (BasicEntityIdentifierMapping) identifierMapping
@ -189,29 +174,13 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
else { else {
//noinspection unchecked //noinspection unchecked
final Set<Attribute<?, ?>> attributes = (Set<Attribute<?, ?>>) ( (ManagedDomainType<?>) ( (EntityDomainType<?>) domainType ).getIdentifierDescriptor().getSqmPathType() ).getAttributes(); final Set<Attribute<?, ?>> attributes = (Set<Attribute<?, ?>>) ( (ManagedDomainType<?>) ( (EntityDomainType<?>) domainType ).getIdentifierDescriptor().getSqmPathType() ).getAttributes();
final Map<String, ModelPart> 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( newIdentifierMapping = new AnonymousTupleNonAggregatedEntityIdentifierMapping(
modelParts, sqmExpressible,
sqlSelections,
selectionIndex,
selectionExpression,
compatibleTableExpressions,
attributes,
domainType, domainType,
selectionExpression, selectionExpression,
(NonAggregatedIdentifierMapping) identifierMapping (NonAggregatedIdentifierMapping) identifierMapping
@ -232,31 +201,22 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
else if ( domainType instanceof ManagedDomainType<?> ) { else if ( domainType instanceof ManagedDomainType<?> ) {
//noinspection unchecked //noinspection unchecked
final Set<Attribute<?, ?>> attributes = (Set<Attribute<?, ?>>) ( (ManagedDomainType<?>) domainType ).getAttributes(); final Set<Attribute<?, ?>> attributes = (Set<Attribute<?, ?>>) ( (ManagedDomainType<?>) domainType ).getAttributes();
final Map<String, ModelPart> modelParts = CollectionHelper.linkedMapOfSize( attributes.size() ); return new AnonymousTupleEmbeddableValuedModelPart(
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, sqmExpressible,
attributeType,
sqlSelections, sqlSelections,
selectionIndex + index, selectionIndex,
selectionExpression + "_" + attribute.getName(), selectionExpression,
attribute.getName(),
modelPartContainer.findSubPart( attribute.getName(), null ),
compatibleTableExpressions, compatibleTableExpressions,
index++ attributes,
domainType,
selectionExpression,
(EmbeddableValuedModelPart) existingModelPart,
fetchableIndex
); );
modelParts.put( modelPart.getPartName(), modelPart );
}
return new AnonymousTupleEmbeddableValuedModelPart( modelParts, domainType, selectionExpression, modelPartContainer, fetchableIndex );
} }
else { else {
return new AnonymousTupleBasicValuedModelPart( return new AnonymousTupleBasicValuedModelPart(
mappingType,
partName, partName,
selectionExpression, selectionExpression,
sqmExpressible, sqmExpressible,

View File

@ -11,6 +11,7 @@ import java.util.List;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.query.sqm.tree.cte.SqmCteStatement; import org.hibernate.query.sqm.tree.cte.SqmCteStatement;
import org.hibernate.query.sqm.tree.cte.SqmCteTable; import org.hibernate.query.sqm.tree.cte.SqmCteTable;
@ -43,19 +44,24 @@ public class CteTupleTableGroupProducer extends AnonymousTupleTableGroupProducer
final BasicType<String> stringType = cteStatement.nodeBuilder() final BasicType<String> stringType = cteStatement.nodeBuilder()
.getTypeConfiguration() .getTypeConfiguration()
.getBasicTypeForJavaType( String.class ); .getBasicTypeForJavaType( String.class );
this.searchModelPart = createModelPart( cteStatement.getSearchAttributeName(), stringType ); this.searchModelPart = createModelPart( this, cteStatement.getSearchAttributeName(), stringType );
this.cycleMarkModelPart = createModelPart( this.cycleMarkModelPart = createModelPart(
this,
cteStatement.getCycleMarkAttributeName(), cteStatement.getCycleMarkAttributeName(),
cteStatement.getCycleLiteral() == null cteStatement.getCycleLiteral() == null
? null ? null
: (BasicType<?>) cteStatement.getCycleLiteral().getNodeType() : (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 ) { if ( attributeName != null ) {
return new AnonymousTupleBasicValuedModelPart( return new AnonymousTupleBasicValuedModelPart(
declaringType,
attributeName, attributeName,
attributeName, attributeName,
basicType, basicType,

View File

@ -374,8 +374,8 @@ public class DomainResultCreationStateImpl
@Override @Override
public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) { public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) {
final EntityValuedModelPart entityValuedFetchable = fetchParent.getEntityValuedModelPart(); final EntityValuedModelPart parentModelPart = fetchParent.getEntityValuedModelPart();
final EntityIdentifierMapping identifierMapping = entityValuedFetchable.getEntityMappingType().getIdentifierMapping(); final EntityIdentifierMapping identifierMapping = parentModelPart.getEntityMappingType().getIdentifierMapping();
final String identifierAttributeName = attributeName( identifierMapping ); final String identifierAttributeName = attributeName( identifierMapping );
final Map.Entry<String, NavigablePath> oldEntry = relativePathStack.getCurrent(); final Map.Entry<String, NavigablePath> oldEntry = relativePathStack.getCurrent();
final String fullPath; final String fullPath;
@ -388,7 +388,7 @@ public class DomainResultCreationStateImpl
oldEntry.getKey() + "." + identifierAttributeName; oldEntry.getKey() + "." + identifierAttributeName;
} }
final Fetchable fetchable = (Fetchable) identifierMapping; final Fetchable identifierFetchable = (Fetchable) identifierMapping;
final FetchBuilder explicitFetchBuilder = (FetchBuilder) fetchBuilderResolverStack final FetchBuilder explicitFetchBuilder = (FetchBuilder) fetchBuilderResolverStack
.getCurrent() .getCurrent()
.apply( fullPath ); .apply( fullPath );
@ -423,7 +423,7 @@ public class DomainResultCreationStateImpl
} }
else { else {
if ( fetchBuilderLegacy == null ) { if ( fetchBuilderLegacy == null ) {
fetchBuilder = Builders.implicitFetchBuilder( fetchPath, fetchable, this ); fetchBuilder = Builders.implicitFetchBuilder( fetchPath, identifierFetchable, this );
} }
else { else {
fetchBuilder = fetchBuilderLegacy; fetchBuilder = fetchBuilderLegacy;

View File

@ -54,15 +54,14 @@ public class TableGroupImpl extends AbstractTableGroup {
} }
@Override @Override
public TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
if ( primaryTableReference.getTableReference( navigablePath , tableExpression, allowFkOptimization, resolve ) != null ) { if ( primaryTableReference.getTableReference( navigablePath , tableExpression, resolve ) != null ) {
return primaryTableReference; return primaryTableReference;
} }
return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); return super.getTableReference( navigablePath, tableExpression, resolve );
} }
} }

View File

@ -82,7 +82,7 @@ public class CompleteFetchBuilderBasicPart implements CompleteFetchBuilder, Basi
final String mappedTable = referencedModelPart.getContainingTableExpression(); final String mappedTable = referencedModelPart.getContainingTableExpression();
final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( parent.getNavigablePath() ); 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 String selectedAlias;
final int jdbcPosition; final int jdbcPosition;

View File

@ -86,7 +86,11 @@ public class CompleteFetchBuilderEmbeddableValuedModelPart
final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() ); final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() );
modelPart.forEachSelectable( modelPart.forEachSelectable(
(selectionIndex, selectableMapping) -> { (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 ); final String columnAlias = columnAliases.get( selectionIndex );
creationStateImpl.resolveSqlSelection( creationStateImpl.resolveSqlSelection(
ResultsHelper.resolveSqlExpression( ResultsHelper.resolveSqlExpression(

View File

@ -11,6 +11,7 @@ import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.query.results.ResultsHelper; import org.hibernate.query.results.ResultsHelper;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.query.results.DomainResultCreationStateImpl; import org.hibernate.query.results.DomainResultCreationStateImpl;
@ -86,7 +87,11 @@ public class CompleteFetchBuilderEntityValuedModelPart
final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() ); final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() );
modelPart.forEachSelectable( modelPart.forEachSelectable(
(selectionIndex, selectableMapping) -> { (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 ); final String columnAlias = columnAliases.get( selectionIndex );
creationStateImpl.resolveSqlSelection( creationStateImpl.resolveSqlSelection(
ResultsHelper.resolveSqlExpression( ResultsHelper.resolveSqlExpression(

View File

@ -75,7 +75,7 @@ public class CompleteResultBuilderBasicModelPart
final DomainResultCreationStateImpl creationStateImpl = impl( domainResultCreationState ); final DomainResultCreationStateImpl creationStateImpl = impl( domainResultCreationState );
final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() ); 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( final SqlSelection sqlSelection = creationStateImpl.resolveSqlSelection(
ResultsHelper.resolveSqlExpression( ResultsHelper.resolveSqlExpression(

View File

@ -172,7 +172,11 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
(selectionIndex, selectableMapping) -> { (selectionIndex, selectableMapping) -> {
resolveSqlSelection( resolveSqlSelection(
columnNames.get( selectionIndex ), columnNames.get( selectionIndex ),
tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ), tableGroup.resolveTableReference(
fetchPath,
keyDescriptor.getKeyPart(),
selectableMapping.getContainingTableExpression()
),
selectableMapping, selectableMapping,
jdbcResultsMetadata, jdbcResultsMetadata,
domainResultCreationState domainResultCreationState

View File

@ -12,8 +12,11 @@ import java.util.function.BiFunction;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.BasicValuedMapping; 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.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.NativeQuery; import org.hibernate.query.NativeQuery;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
@ -80,9 +83,108 @@ public class DynamicFetchBuilderStandard
final Fetchable attributeMapping = (Fetchable) parent.getReferencedMappingContainer().findSubPart( fetchableName, null ); final Fetchable attributeMapping = (Fetchable) parent.getReferencedMappingContainer().findSubPart( fetchableName, null );
final SqlExpressionResolver sqlExpressionResolver = domainResultCreationState.getSqlAstCreationState().getSqlExpressionResolver(); 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( final TableReference tableReference = ownerTableGroup.resolveTableReference(
fetchPath, fetchPath,
valuedModelPart,
selectableMapping.getContainingTableExpression() selectableMapping.getContainingTableExpression()
); );
final String columnAlias = columnNames.get( selectionIndex ); final String columnAlias = columnNames.get( selectionIndex );
@ -102,54 +204,6 @@ public class DynamicFetchBuilderStandard
.getTypeConfiguration() .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 @Override

View File

@ -97,7 +97,7 @@ public class ImplicitFetchBuilderBasic implements ImplicitFetchBuilder, BasicVal
final Expression expression = ResultsHelper.resolveSqlExpression( final Expression expression = ResultsHelper.resolveSqlExpression(
creationStateImpl, creationStateImpl,
jdbcResultsMetadata, jdbcResultsMetadata,
parentTableGroup.resolveTableReference( fetchPath, table ), parentTableGroup.resolveTableReference( fetchPath, fetchable, table ),
fetchable, fetchable,
column column
); );

View File

@ -13,7 +13,6 @@ import java.util.Map;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.DomainParameterXref;
@ -155,7 +154,6 @@ public class CteDeleteHandler extends AbstractCteMutationHandler implements Dele
final TableReference updatingTableReference = updatingTableGroup.getTableReference( final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
tableExpression, tableExpression,
true,
true true
); );
final NamedTableReference dmlTableReference = resolveUnionTableReference( final NamedTableReference dmlTableReference = resolveUnionTableReference(

View File

@ -758,7 +758,6 @@ public class CteInsertHandler implements InsertHandler {
final TableReference rootTableReference = updatingTableGroup.getTableReference( final TableReference rootTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
rootTableName, rootTableName,
true,
true true
); );
@ -777,7 +776,6 @@ public class CteInsertHandler implements InsertHandler {
final TableReference updatingTableReference = updatingTableGroup.getTableReference( final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
tableExpression, tableExpression,
true,
true true
); );
final List<Map.Entry<List<CteColumn>, Assignment>> assignmentList = assignmentsByTable.get( updatingTableReference ); final List<Map.Entry<List<CteColumn>, Assignment>> assignmentList = assignmentsByTable.get( updatingTableReference );

View File

@ -159,7 +159,6 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
final TableReference updatingTableReference = updatingTableGroup.getTableReference( final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
tableExpression, tableExpression,
true,
true true
); );
final List<Assignment> assignmentList = assignmentsByTable.get( updatingTableReference ); final List<Assignment> assignmentList = assignmentsByTable.get( updatingTableReference );
@ -272,7 +271,6 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
final TableReference updatingTableReference = updatingTableGroup.getTableReference( final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
tableExpression, tableExpression,
true,
true true
); );
final List<Assignment> assignmentList = assignmentsByTable.get( updatingTableReference ); final List<Assignment> assignmentList = assignmentsByTable.get( updatingTableReference );

View File

@ -332,7 +332,6 @@ public class InlineUpdateHandler implements UpdateHandler {
final TableReference updatingTableReference = updatingTableGroup.getTableReference( final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
tableExpression, tableExpression,
true,
true true
); );

View File

@ -301,7 +301,6 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
final TableReference updatingTableReference = updatingTableGroup.getTableReference( final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
tableExpression, tableExpression,
true,
true true
); );
@ -661,7 +660,6 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
final TableReference updatingTableReference = updatingTableGroup.getTableReference( final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
tableExpression, tableExpression,
true,
true true
); );

View File

@ -23,7 +23,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.MutableBoolean; import org.hibernate.internal.util.MutableBoolean;
import org.hibernate.internal.util.MutableInteger; import org.hibernate.internal.util.MutableInteger;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.MappingModelExpressible;
@ -329,7 +328,6 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
final NamedTableReference tableReference = (NamedTableReference) tableGroup.getTableReference( final NamedTableReference tableReference = (NamedTableReference) tableGroup.getTableReference(
tableGroup.getNavigablePath(), tableGroup.getNavigablePath(),
tableExpression, tableExpression,
true,
true true
); );
final QuerySpec idMatchingSubQuerySpec; final QuerySpec idMatchingSubQuerySpec;

View File

@ -243,7 +243,6 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio
final TableReference updatingTableReference = updatingTableGroup.getTableReference( final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(), updatingTableGroup.getNavigablePath(),
tableExpression, tableExpression,
true,
true true
); );

View File

@ -84,6 +84,7 @@ import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.SqlExpressible; import org.hibernate.metamodel.mapping.SqlExpressible;
import org.hibernate.metamodel.mapping.SqlTypedMapping; import org.hibernate.metamodel.mapping.SqlTypedMapping;
import org.hibernate.metamodel.mapping.ValueMapping; 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.EntityCollectionPart;
import org.hibernate.metamodel.mapping.internal.ManyToManyCollectionPart; import org.hibernate.metamodel.mapping.internal.ManyToManyCollectionPart;
import org.hibernate.metamodel.mapping.internal.OneToManyCollectionPart; 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.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin; import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath; 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.SqmTreatedPath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedRoot; import org.hibernate.query.sqm.tree.domain.SqmTreatedRoot;
import org.hibernate.query.sqm.tree.expression.Conversion; import org.hibernate.query.sqm.tree.expression.Conversion;
@ -2901,7 +2903,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
} }
final int subclassTableSpan = persister.getSubclassTableSpan(); final int subclassTableSpan = persister.getSubclassTableSpan();
for ( int i = 0; i < subclassTableSpan; i++ ) { 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<T extends Statement> extends Base
} }
final int subclassTableSpan = persister.getSubclassTableSpan(); final int subclassTableSpan = persister.getSubclassTableSpan();
for ( int i = 0; i < subclassTableSpan; i++ ) { for ( int i = 0; i < subclassTableSpan; i++ ) {
tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ), false ); tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ) );
} }
} }
@ -3468,9 +3470,17 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
} }
} }
} }
else if ( path instanceof SqmFrom<?, ?> ) { else {
if ( path instanceof SqmFrom<?, ?> ) {
registerTreatUsage( (SqmFrom<?, ?>) path, tableGroup ); 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 );
}
}
} }
private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> joinedPath) { private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> joinedPath) {
@ -3507,8 +3517,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
} }
else { else {
// Check if we can reuse a table group join of the parent // Check if we can reuse a table group join of the parent
final TableGroup compatibleTableGroup = findCompatibleJoinedGroup( final TableGroup compatibleTableGroup = actualParentTableGroup.findCompatibleJoinedGroup(
actualParentTableGroup,
joinProducer, joinProducer,
SqlAstJoinType.INNER SqlAstJoinType.INNER
); );
@ -3898,7 +3907,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
navigablePath, navigablePath,
tableGroupToUse == null ? tableGroup : tableGroupToUse, tableGroupToUse == null ? tableGroup : tableGroupToUse,
expandToAllColumns ? null : resultModelPart, expandToAllColumns ? null : resultModelPart,
true,
interpretationModelPart, interpretationModelPart,
treatedMapping, treatedMapping,
this this
@ -3908,7 +3916,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final EmbeddableValuedModelPart mapping = (EmbeddableValuedModelPart) actualModelPart; final EmbeddableValuedModelPart mapping = (EmbeddableValuedModelPart) actualModelPart;
result = new EmbeddableValuedPathInterpretation<>( result = new EmbeddableValuedPathInterpretation<>(
mapping.toSqlExpression( mapping.toSqlExpression(
tableGroup, getFromClauseAccess().findTableGroup( path.getLhs().getNavigablePath() ),
currentClauseStack.getCurrent(), currentClauseStack.getCurrent(),
this, this,
getSqlAstCreationState() getSqlAstCreationState()
@ -3923,6 +3931,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final BasicValuedModelPart mapping = (BasicValuedModelPart) actualModelPart; final BasicValuedModelPart mapping = (BasicValuedModelPart) actualModelPart;
final TableReference tableReference = tableGroup.resolveTableReference( final TableReference tableReference = tableGroup.resolveTableReference(
navigablePath.append( actualModelPart.getPartName() ), navigablePath.append( actualModelPart.getPartName() ),
mapping,
mapping.getContainingTableExpression() mapping.getContainingTableExpression()
); );
@ -4439,6 +4448,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
new ColumnReference( new ColumnReference(
tableGroup.resolveTableReference( tableGroup.resolveTableReference(
navigablePath, navigablePath,
(ValuedModelPart) modelPart,
selectionMapping.getContainingTableExpression() selectionMapping.getContainingTableExpression()
), ),
selectionMapping selectionMapping
@ -4564,6 +4574,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final ColumnReference columnReference = new ColumnReference( final ColumnReference columnReference = new ColumnReference(
tableGroup.resolveTableReference( tableGroup.resolveTableReference(
navigablePath, navigablePath,
(ValuedModelPart) modelPart,
selectionMapping.getContainingTableExpression() selectionMapping.getContainingTableExpression()
), ),
selectionMapping selectionMapping
@ -7139,11 +7150,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override @Override
public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) { public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) {
final EntityIdentifierMapping identifierMapping = fetchParent.getEntityValuedModelPart() final EntityIdentifierMapping identifierMapping = fetchParent.getReferencedMappingContainer()
.getEntityMappingType()
.getIdentifierMapping(); .getIdentifierMapping();
final Fetchable fetchableIdentifierMapping = (Fetchable) identifierMapping; return createFetch( fetchParent, (Fetchable) identifierMapping, false );
return createFetch( fetchParent, fetchableIdentifierMapping, false );
} }
private Fetch createFetch(FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) { private Fetch createFetch(FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) {

View File

@ -108,6 +108,7 @@ public class BasicValuedPathInterpretation<T> extends AbstractSqmPathInterpretat
final TableReference tableReference = tableGroup.resolveTableReference( final TableReference tableReference = tableGroup.resolveTableReference(
sqmPath.getNavigablePath(), sqmPath.getNavigablePath(),
mapping,
mapping.getContainingTableExpression() mapping.getContainingTableExpression()
); );

View File

@ -21,6 +21,7 @@ import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.MappingModelExpressible; import org.hibernate.metamodel.mapping.MappingModelExpressible;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer; 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.EntityCollectionPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.derived.AnonymousTupleEntityValuedModelPart; 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.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpretation<T> implements SqlTupleContainer, public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpretation<T>
Assignable { implements SqlTupleContainer, Assignable {
public static <T> EntityValuedPathInterpretation<T> from( public static <T> EntityValuedPathInterpretation<T> from(
SqmEntityValuedSimplePath<T> sqmPath, SqmEntityValuedSimplePath<T> sqmPath,
@ -99,7 +100,6 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
sqmPath.getNavigablePath(), sqmPath.getNavigablePath(),
tableGroup, tableGroup,
pathMapping.getEntityMappingType().getIdentifierMapping(), pathMapping.getEntityMappingType().getIdentifierMapping(),
false,
pathMapping, pathMapping,
pathMapping, pathMapping,
sqlAstCreationState sqlAstCreationState
@ -149,7 +149,6 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
EntityValuedModelPart mapping, EntityValuedModelPart mapping,
MappingModelExpressible<?> inferredMapping, MappingModelExpressible<?> inferredMapping,
SqmToSqlAstConverter sqlAstCreationState) { SqmToSqlAstConverter sqlAstCreationState) {
final boolean allowFkOptimization;
final ModelPart resultModelPart; final ModelPart resultModelPart;
final TableGroup resultTableGroup; final TableGroup resultTableGroup;
// For association mappings where the FK optimization i.e. use of the parent table group is allowed, // For association mappings where the FK optimization i.e. use of the parent table group is allowed,
@ -220,7 +219,6 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
} }
resultTableGroup = tableGroup; resultTableGroup = tableGroup;
} }
allowFkOptimization = true;
} }
else if ( inferredMapping == null && hasNotFound( mapping ) ) { else if ( inferredMapping == null && hasNotFound( mapping ) ) {
// This is necessary to allow expression like `where root.notFoundAssociation is null` // This is necessary to allow expression like `where root.notFoundAssociation is null`
@ -229,31 +227,26 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
resultModelPart = keyTargetMatchPart; resultModelPart = keyTargetMatchPart;
resultTableGroup = sqlAstCreationState.getFromClauseAccess() resultTableGroup = sqlAstCreationState.getFromClauseAccess()
.findTableGroup( tableGroup.getNavigablePath().getParent() ); .findTableGroup( tableGroup.getNavigablePath().getParent() );
allowFkOptimization = false;
} }
else { else {
// If the mapping is an inverse association, use the PK and disallow FK optimizations // If the mapping is an inverse association, use the PK and disallow FK optimizations
resultModelPart = ( (EntityAssociationMapping) mapping ).getAssociatedEntityMappingType().getIdentifierMapping(); resultModelPart = ( (EntityAssociationMapping) mapping ).getAssociatedEntityMappingType().getIdentifierMapping();
resultTableGroup = tableGroup; resultTableGroup = tableGroup;
allowFkOptimization = false;
} }
} }
else if ( mapping instanceof AnonymousTupleEntityValuedModelPart ) { else if ( mapping instanceof AnonymousTupleEntityValuedModelPart ) {
resultModelPart = ( (AnonymousTupleEntityValuedModelPart) mapping ).getForeignKeyPart(); resultModelPart = ( (AnonymousTupleEntityValuedModelPart) mapping ).getForeignKeyPart();
resultTableGroup = tableGroup; resultTableGroup = tableGroup;
allowFkOptimization = true;
} }
else { else {
// If the mapping is not an association, use the PK and disallow FK optimizations // If the mapping is not an association, use the PK and disallow FK optimizations
resultModelPart = mapping.getEntityMappingType().getIdentifierMapping(); resultModelPart = mapping.getEntityMappingType().getIdentifierMapping();
resultTableGroup = tableGroup; resultTableGroup = tableGroup;
allowFkOptimization = false;
} }
return from( return from(
navigablePath, navigablePath,
resultTableGroup, resultTableGroup,
resultModelPart, resultModelPart,
allowFkOptimization,
mapping, mapping,
mapping, mapping,
sqlAstCreationState sqlAstCreationState
@ -288,7 +281,6 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
ModelPart resultModelPart, ModelPart resultModelPart,
boolean allowFkOptimization,
EntityValuedModelPart mapping, EntityValuedModelPart mapping,
EntityValuedModelPart treatedMapping, EntityValuedModelPart treatedMapping,
SqmToSqlAstConverter sqlAstCreationState) { SqmToSqlAstConverter sqlAstCreationState) {
@ -309,8 +301,7 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> { final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> {
final TableReference tableReference = parentTableGroup.resolveTableReference( final TableReference tableReference = parentTableGroup.resolveTableReference(
navigablePath, navigablePath,
selectableMapping.getContainingTableExpression(), selectableMapping.getContainingTableExpression()
false
); );
expressions.add( expressions.add(
sqlExprResolver.resolveSqlExpression( tableReference, selectableMapping ) sqlExprResolver.resolveSqlExpression( tableReference, selectableMapping )
@ -333,8 +324,8 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
final BasicValuedModelPart basicValuedModelPart = (BasicValuedModelPart) resultModelPart; final BasicValuedModelPart basicValuedModelPart = (BasicValuedModelPart) resultModelPart;
final TableReference tableReference = tableGroup.resolveTableReference( final TableReference tableReference = tableGroup.resolveTableReference(
navigablePath, navigablePath,
basicValuedModelPart.getContainingTableExpression(), basicValuedModelPart,
allowFkOptimization basicValuedModelPart.getContainingTableExpression()
); );
sqlExpression = sqlExprResolver.resolveSqlExpression( tableReference, basicValuedModelPart ); sqlExpression = sqlExprResolver.resolveSqlExpression( tableReference, basicValuedModelPart );
} }
@ -344,8 +335,8 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
(selectionIndex, selectableMapping) -> { (selectionIndex, selectableMapping) -> {
final TableReference tableReference = tableGroup.resolveTableReference( final TableReference tableReference = tableGroup.resolveTableReference(
navigablePath, navigablePath,
selectableMapping.getContainingTableExpression(), (ValuedModelPart) resultModelPart,
allowFkOptimization selectableMapping.getContainingTableExpression()
); );
expressions.add( sqlExprResolver.resolveSqlExpression( tableReference, selectableMapping ) ); expressions.add( sqlExprResolver.resolveSqlExpression( tableReference, selectableMapping ) );
} }

View File

@ -199,7 +199,7 @@ public class NavigablePath implements DotIdentifierSequence, Serializable {
if ( dotIdentifierSequence == null ) { if ( dotIdentifierSequence == null ) {
return true; return true;
} }
if ( !getLocalName().equals( dotIdentifierSequence.getLocalName() ) ) { if ( !localNamesMatch( dotIdentifierSequence ) ) {
return false; return false;
} }
return getParent() != null && getParent().isSuffix( dotIdentifierSequence.getParent() ); return getParent() != null && getParent().isSuffix( dotIdentifierSequence.getParent() );

View File

@ -67,10 +67,9 @@ public class CteTableGroup extends AbstractTableGroup {
} }
@Override @Override
protected TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
if ( compatibleTableExpressions.contains( tableExpression ) ) { if ( compatibleTableExpressions.contains( tableExpression ) ) {
return getPrimaryTableReference(); return getPrimaryTableReference();
@ -78,7 +77,7 @@ public class CteTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) {
final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() final TableReference groupTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference() .getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); .getTableReference( navigablePath, tableExpression, resolve );
if ( groupTableReference != null ) { if ( groupTableReference != null ) {
return groupTableReference; return groupTableReference;
} }
@ -86,7 +85,7 @@ public class CteTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() final TableReference groupTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference() .getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); .getTableReference( navigablePath, tableExpression, resolve );
if ( groupTableReference != null ) { if ( groupTableReference != null ) {
return groupTableReference; return groupTableReference;
} }

View File

@ -7,7 +7,6 @@
package org.hibernate.sql.ast.tree.from; package org.hibernate.sql.ast.tree.from;
import java.util.List; import java.util.List;
import java.util.Locale;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
@ -25,53 +24,14 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// TableReference handling // 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 @Override
public TableReference getTableReference( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) {
return getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve );
}
protected TableReference getTableReferenceInternal(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
final TableReference primaryTableReference = getPrimaryTableReference().getTableReference( final TableReference primaryTableReference = getPrimaryTableReference().getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( primaryTableReference != null) { if ( primaryTableReference != null) {
@ -82,7 +42,6 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc
final TableReference tableReference = tableJoin.getJoinedTableReference().getTableReference( final TableReference tableReference = tableJoin.getJoinedTableReference().getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( tableReference != null) { if ( tableReference != null) {

View File

@ -79,15 +79,13 @@ public class CollectionTableGroup extends StandardTableGroup implements PluralTa
} }
@Override @Override
protected TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
final TableReference tableReference = super.getTableReferenceInternal( final TableReference tableReference = super.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( tableReference != null ) { if ( tableReference != null ) {
@ -97,7 +95,6 @@ public class CollectionTableGroup extends StandardTableGroup implements PluralTa
final TableReference indexTableReference = indexTableGroup.getTableReference( final TableReference indexTableReference = indexTableGroup.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( indexTableReference != null ) { if ( indexTableReference != null ) {
@ -108,7 +105,6 @@ public class CollectionTableGroup extends StandardTableGroup implements PluralTa
final TableReference elementTableReference = elementTableGroup.getTableReference( final TableReference elementTableReference = elementTableGroup.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( elementTableReference != null ) { if ( elementTableReference != null ) {

View File

@ -6,41 +6,89 @@
*/ */
package org.hibernate.sql.ast.tree.from; package org.hibernate.sql.ast.tree.from;
import java.util.Locale;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface ColumnReferenceQualifier { public interface ColumnReferenceQualifier {
default TableReference resolveTableReference(NavigablePath navigablePath, String tableExpression) {
return resolveTableReference( navigablePath, tableExpression, true );
}
default TableReference resolveTableReference(String tableExpression) { 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. * 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 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 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 * @throws UnknownTableReferenceException to indicate that the given tableExpression could not be resolved
*/ */
TableReference resolveTableReference( default TableReference resolveTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression) {
boolean allowFkOptimization); 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) { default TableReference getTableReference(NavigablePath navigablePath, String tableExpression) {
return getTableReference( navigablePath, tableExpression, true, false ); return getTableReference( navigablePath, tableExpression, false );
} }
default TableReference getTableReference(String tableExpression) { 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 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 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 * @param resolve Whether to potentially create table reference joins for this table group
*/ */
TableReference getTableReference( TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve); boolean resolve);
default TableReference getTableReference(
NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression,
boolean resolve) {
return getTableReference( navigablePath, tableExpression, resolve );
}
} }

View File

@ -10,6 +10,7 @@ import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -60,15 +61,53 @@ public class CorrelatedPluralTableGroup extends CorrelatedTableGroup implements
} }
@Override @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, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
final TableReference tableReference = super.getTableReferenceInternal( final TableReference tableReference = super.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( tableReference != null ) { if ( tableReference != null ) {
@ -78,7 +117,6 @@ public class CorrelatedPluralTableGroup extends CorrelatedTableGroup implements
final TableReference indexTableReference = indexTableGroup.getTableReference( final TableReference indexTableReference = indexTableGroup.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( indexTableReference != null ) { if ( indexTableReference != null ) {
@ -89,7 +127,6 @@ public class CorrelatedPluralTableGroup extends CorrelatedTableGroup implements
final TableReference elementTableReference = elementTableGroup.getTableReference( final TableReference elementTableReference = elementTableGroup.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( elementTableReference != null ) { if ( elementTableReference != null ) {

View File

@ -11,6 +11,7 @@ import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBase;
@ -71,15 +72,15 @@ public class CorrelatedTableGroup extends AbstractTableGroup {
} }
@Override @Override
protected TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
final TableReference tableReference = correlatedTableGroup.getTableReference( final TableReference tableReference = correlatedTableGroup.getTableReference(
navigablePath, navigablePath,
modelPart,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( tableReference != null ) { if ( tableReference != null ) {
@ -88,7 +89,7 @@ public class CorrelatedTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) {
final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() final TableReference groupTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference() .getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); .getTableReference( navigablePath, modelPart, tableExpression, resolve );
if ( groupTableReference != null ) { if ( groupTableReference != null ) {
return groupTableReference; return groupTableReference;
} }
@ -96,7 +97,39 @@ public class CorrelatedTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() final TableReference groupTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference() .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 ) { if ( groupTableReference != null ) {
return groupTableReference; return groupTableReference;
} }

View File

@ -11,6 +11,7 @@ import java.util.function.Consumer;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
@ -58,40 +59,20 @@ public abstract class DelegatingTableGroup implements TableGroup {
} }
@Override @Override
public TableReference resolveTableReference(NavigablePath navigablePath, String tableExpression) { public TableReference getTableReference(
return resolveTableReference( navigablePath, tableExpression, true );
}
@Override
public TableReference resolveTableReference(String tableExpression) {
return resolveTableReference( null, tableExpression, true );
}
@Override
public TableReference resolveTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization) { boolean resolve) {
return getTableGroup().resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); return getTableGroup().getTableReference( navigablePath, tableExpression, resolve );
}
@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 );
} }
@Override @Override
public TableReference getTableReference( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
return getTableGroup().getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); return getTableGroup().getTableReference( navigablePath, modelPart, tableExpression, resolve );
} }
@Override @Override

View File

@ -9,6 +9,7 @@ package org.hibernate.sql.ast.tree.from;
import java.util.List; import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
/** /**
@ -45,8 +46,18 @@ public abstract class DerivedTableReference extends AbstractTableReference {
@Override @Override
public TableReference resolveTableReference( public TableReference resolveTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression) {
boolean allowFkOptimization) { 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( throw new UnknownTableReferenceException(
tableExpression, tableExpression,
"TableReferences cannot be resolved relative to DerivedTableReferences - `" + tableExpression + "` : " + navigablePath "TableReferences cannot be resolved relative to DerivedTableReferences - `" + tableExpression + "` : " + navigablePath
@ -57,7 +68,6 @@ public abstract class DerivedTableReference extends AbstractTableReference {
public TableReference getTableReference( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
return null; return null;
} }

View File

@ -55,10 +55,9 @@ public class FunctionTableGroup extends AbstractTableGroup {
} }
@Override @Override
protected TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
if ( tableExpression == null ) { if ( tableExpression == null ) {
return getPrimaryTableReference(); return getPrimaryTableReference();
@ -66,7 +65,7 @@ public class FunctionTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) {
final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() final TableReference groupTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference() .getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); .getTableReference( navigablePath, tableExpression, resolve );
if ( groupTableReference != null ) { if ( groupTableReference != null ) {
return groupTableReference; return groupTableReference;
} }
@ -74,7 +73,7 @@ public class FunctionTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() final TableReference groupTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference() .getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); .getTableReference( navigablePath, tableExpression, resolve );
if ( groupTableReference != null ) { if ( groupTableReference != null ) {
return groupTableReference; return groupTableReference;
} }

View File

@ -9,13 +9,12 @@ package org.hibernate.sql.ast.tree.from;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.function.BiPredicate;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBase;
@ -37,7 +36,7 @@ public class LazyTableGroup extends DelegatingTableGroup {
private final SqlAliasBase sqlAliasBase; private final SqlAliasBase sqlAliasBase;
private final Supplier<TableGroup> tableGroupSupplier; private final Supplier<TableGroup> tableGroupSupplier;
private final TableGroup parentTableGroup; private final TableGroup parentTableGroup;
private final BiPredicate<NavigablePath, String> navigablePathChecker; private final ParentTableGroupUseChecker parentTableGroupUseChecker;
private List<TableGroupJoin> tableGroupJoins; private List<TableGroupJoin> tableGroupJoins;
private List<TableGroupJoin> nestedTableGroupJoins; private List<TableGroupJoin> nestedTableGroupJoins;
private Consumer<TableGroup> tableGroupConsumer; private Consumer<TableGroup> tableGroupConsumer;
@ -48,7 +47,7 @@ public class LazyTableGroup extends DelegatingTableGroup {
NavigablePath navigablePath, NavigablePath navigablePath,
boolean fetched, boolean fetched,
Supplier<TableGroup> tableGroupSupplier, Supplier<TableGroup> tableGroupSupplier,
BiPredicate<NavigablePath, String> navigablePathChecker, ParentTableGroupUseChecker parentTableGroupUseChecker,
TableGroupProducer tableGroupProducer, TableGroupProducer tableGroupProducer,
String sourceAlias, String sourceAlias,
SqlAliasBase sqlAliasBase, SqlAliasBase sqlAliasBase,
@ -61,7 +60,7 @@ public class LazyTableGroup extends DelegatingTableGroup {
this.sourceAlias = sourceAlias; this.sourceAlias = sourceAlias;
this.sqlAliasBase = sqlAliasBase; this.sqlAliasBase = sqlAliasBase;
this.tableGroupSupplier = tableGroupSupplier; this.tableGroupSupplier = tableGroupSupplier;
this.navigablePathChecker = navigablePathChecker; this.parentTableGroupUseChecker = parentTableGroupUseChecker;
this.parentTableGroup = parentTableGroup; this.parentTableGroup = parentTableGroup;
} }
@ -237,60 +236,34 @@ public class LazyTableGroup extends DelegatingTableGroup {
} }
@Override @Override
public TableReference resolveTableReference( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization) { boolean resolve) {
assert tableExpression != null; return getTableGroup().getTableReference( navigablePath, tableExpression, resolve );
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 @Override
public TableReference getTableReference( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
return getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); if ( parentTableGroupUseChecker.canUseParentTableGroup( producer, navigablePath, modelPart ) ) {
}
protected TableReference getTableReferenceInternal(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization,
boolean resolve) {
if ( allowFkOptimization && ( navigablePath == null || navigablePathChecker.test( navigablePath, tableExpression ) ) ) {
final TableReference reference = parentTableGroup.getTableReference( final TableReference reference = parentTableGroup.getTableReference(
navigablePath, navigablePath,
(ValuedModelPart) producer,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( reference != null ) { if ( reference != null ) {
return reference; 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);
}
} }

View File

@ -9,10 +9,10 @@ package org.hibernate.sql.ast.tree.from;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.function.BiPredicate;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
/** /**
@ -21,25 +21,25 @@ import org.hibernate.spi.NavigablePath;
public class MappedByTableGroup extends DelegatingTableGroup implements VirtualTableGroup { public class MappedByTableGroup extends DelegatingTableGroup implements VirtualTableGroup {
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final ModelPartContainer modelPart; private final TableGroupProducer producer;
private final TableGroup underlyingTableGroup; private final TableGroup underlyingTableGroup;
private final boolean fetched; private final boolean fetched;
private final TableGroup parentTableGroup; private final TableGroup parentTableGroup;
private final BiPredicate<NavigablePath, String> navigablePathChecker; private final LazyTableGroup.ParentTableGroupUseChecker parentTableGroupUseChecker;
public MappedByTableGroup( public MappedByTableGroup(
NavigablePath navigablePath, NavigablePath navigablePath,
ModelPartContainer modelPart, TableGroupProducer producer,
TableGroup underlyingTableGroup, TableGroup underlyingTableGroup,
boolean fetched, boolean fetched,
TableGroup parentTableGroup, TableGroup parentTableGroup,
BiPredicate<NavigablePath, String> navigablePathChecker) { LazyTableGroup.ParentTableGroupUseChecker parentTableGroupUseChecker) {
this.navigablePath = navigablePath; this.navigablePath = navigablePath;
this.modelPart = modelPart; this.producer = producer;
this.underlyingTableGroup = underlyingTableGroup; this.underlyingTableGroup = underlyingTableGroup;
this.fetched = fetched; this.fetched = fetched;
this.parentTableGroup = parentTableGroup; this.parentTableGroup = parentTableGroup;
this.navigablePathChecker = navigablePathChecker; this.parentTableGroupUseChecker = parentTableGroupUseChecker;
} }
@Override @Override
@ -75,7 +75,7 @@ public class MappedByTableGroup extends DelegatingTableGroup implements VirtualT
@Override @Override
public ModelPartContainer getModelPart() { 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" // 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 @Override
public TableReference resolveTableReference( public TableReference resolveTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression) {
boolean allowFkOptimization) {
final TableReference tableReference = getTableReference( final TableReference tableReference = getTableReference(
navigablePath, navigablePath,
tableExpression, 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 true
); );
@ -147,25 +174,31 @@ public class MappedByTableGroup extends DelegatingTableGroup implements VirtualT
public TableReference getTableReference( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
if ( allowFkOptimization && ( navigablePath == null || navigablePathChecker.test( navigablePath, tableExpression ) ) ) { return getTableGroup().getTableReference(
final TableReference reference = parentTableGroup.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization, 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,
resolve resolve
); );
if ( reference != null ) { if ( reference != null ) {
return reference; return reference;
} }
} }
return getTableGroup().getTableReference( navigablePath, modelPart, tableExpression, resolve );
return underlyingTableGroup.getTableReference(
navigablePath,
tableExpression,
allowFkOptimization,
resolve
);
} }
} }

View File

@ -63,21 +63,12 @@ public class MutatingTableReferenceGroupWrapper implements TableGroup {
public TableReference getTableReference( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
return mutatingTableReference.getTableExpression().equals( tableExpression ) return mutatingTableReference.getTableExpression().equals( tableExpression )
? mutatingTableReference ? mutatingTableReference
: null; : null;
} }
@Override
public TableReference resolveTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization) {
return getTableReference( navigablePath, tableExpression, allowFkOptimization, true );
}
@Override @Override
public void applyAffectedTableNames(Consumer<String> nameCollector) { public void applyAffectedTableNames(Consumer<String> nameCollector) {
nameCollector.accept( mutatingTableReference.getTableExpression() ); nameCollector.accept( mutatingTableReference.getTableExpression() );

View File

@ -83,8 +83,7 @@ public class NamedTableReference extends AbstractTableReference {
@Override @Override
public TableReference resolveTableReference( public TableReference resolveTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression) {
boolean allowFkOptimization) {
if ( tableExpression.equals( getTableExpression() ) ) { if ( tableExpression.equals( getTableExpression() ) ) {
return this; return this;
} }
@ -104,12 +103,8 @@ public class NamedTableReference extends AbstractTableReference {
public TableReference getTableReference( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
if ( this.tableExpression.equals( tableExpression ) ) { return this.tableExpression.equals( tableExpression ) ? this : null;
return this;
}
return null;
} }
@Override @Override

View File

@ -165,15 +165,13 @@ public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implem
} }
@Override @Override
protected TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
final TableReference tableReference = elementTableGroup.getTableReference( final TableReference tableReference = elementTableGroup.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( tableReference != null || indexTableGroup == null if ( tableReference != null || indexTableGroup == null
@ -184,7 +182,6 @@ public class OneToManyTableGroup extends AbstractColumnReferenceQualifier implem
return indexTableGroup.getTableReference( return indexTableGroup.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
} }

View File

@ -8,13 +8,11 @@ package org.hibernate.sql.ast.tree.from;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.tree.select.QueryPart;
import org.hibernate.sql.ast.tree.select.SelectStatement; import org.hibernate.sql.ast.tree.select.SelectStatement;
/** /**
@ -83,10 +81,9 @@ public class QueryPartTableGroup extends AbstractTableGroup {
} }
@Override @Override
protected TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
if ( compatibleTableExpressions.contains( tableExpression ) ) { if ( compatibleTableExpressions.contains( tableExpression ) ) {
return getPrimaryTableReference(); return getPrimaryTableReference();
@ -94,7 +91,7 @@ public class QueryPartTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) {
final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() final TableReference groupTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference() .getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); .getTableReference( navigablePath, tableExpression, resolve );
if ( groupTableReference != null ) { if ( groupTableReference != null ) {
return groupTableReference; return groupTableReference;
} }
@ -102,7 +99,7 @@ public class QueryPartTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() final TableReference groupTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference() .getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); .getTableReference( navigablePath, tableExpression, resolve );
if ( groupTableReference != null ) { if ( groupTableReference != null ) {
return groupTableReference; return groupTableReference;
} }

View File

@ -131,15 +131,13 @@ public class StandardTableGroup extends AbstractTableGroup {
} }
@Override @Override
protected TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
final TableReference tableReference = primaryTableReference.getTableReference( final TableReference tableReference = primaryTableReference.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( tableReference != null ) { if ( tableReference != null ) {
@ -152,7 +150,7 @@ public class StandardTableGroup extends AbstractTableGroup {
final TableReferenceJoin join = tableJoins.get( i ); final TableReferenceJoin join = tableJoins.get( i );
assert join != null; assert join != null;
final TableReference resolveTableReference = join.getJoinedTableReference() final TableReference resolveTableReference = join.getJoinedTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); .getTableReference( navigablePath, tableExpression, resolve );
if ( resolveTableReference != null ) { if ( resolveTableReference != null ) {
return resolveTableReference; return resolveTableReference;
} }
@ -164,13 +162,13 @@ public class StandardTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) {
final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference();
if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { if ( primaryTableReference.getTableReference( navigablePath, tableExpression, resolve ) != null ) {
return primaryTableReference; return primaryTableReference;
} }
} }
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference();
if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { if ( primaryTableReference.getTableReference( navigablePath, tableExpression, resolve ) != null ) {
return primaryTableReference; return primaryTableReference;
} }
} }

View File

@ -9,7 +9,11 @@ package org.hibernate.sql.ast.tree.from;
import java.util.List; import java.util.List;
import java.util.function.Consumer; 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.ModelPartContainer;
import org.hibernate.metamodel.mapping.OwnedValuedModelPart;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
/** /**
@ -105,21 +109,68 @@ public class StandardVirtualTableGroup extends AbstractTableGroup implements Vir
} }
@Override @Override
public TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
final TableReference tableReference = underlyingTableGroup.getTableReference( final TableReference tableReference = underlyingTableGroup.getTableReference(
navigablePath, navigablePath,
tableExpression, tableExpression,
allowFkOptimization,
resolve resolve
); );
if ( tableReference != null ) { if ( tableReference != null ) {
return tableReference; 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;
}
} }

View File

@ -15,6 +15,7 @@ import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer; import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation; 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.SqlAstWalker;
import org.hibernate.sql.ast.tree.SqlAstNode; import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -159,4 +160,37 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat
default boolean isInitialized() { default boolean isInitialized() {
return true; 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;
}
} }

View File

@ -65,13 +65,11 @@ public interface TableReference extends SqlAstNode, ColumnReferenceQualifier {
@Override @Override
TableReference resolveTableReference( TableReference resolveTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression);
boolean allowFkOptimization);
@Override @Override
TableReference getTableReference( TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve); boolean resolve);
} }

View File

@ -44,14 +44,13 @@ public class UnionTableGroup extends AbstractTableGroup {
} }
@Override @Override
public TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
if ( tableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) { if ( tableReference.getTableReference( navigablePath, tableExpression, resolve ) != null ) {
return tableReference; return tableReference;
} }
return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve ); return super.getTableReference( navigablePath, tableExpression, resolve );
} }
} }

View File

@ -39,8 +39,7 @@ public class UnionTableReference extends NamedTableReference {
@Override @Override
public TableReference resolveTableReference( public TableReference resolveTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression) {
boolean allowFkOptimization) {
if ( hasTableExpression( tableExpression ) ) { if ( hasTableExpression( tableExpression ) ) {
return this; return this;
} }
@ -60,7 +59,6 @@ public class UnionTableReference extends NamedTableReference {
public TableReference getTableReference( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
if ( hasTableExpression( tableExpression ) ) { if ( hasTableExpression( tableExpression ) ) {
return this; return this;

View File

@ -43,10 +43,9 @@ public class ValuesTableGroup extends AbstractTableGroup {
} }
@Override @Override
protected TableReference getTableReferenceInternal( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization,
boolean resolve) { boolean resolve) {
if ( ( (TableGroupProducer) getModelPart() ).containsTableReference( tableExpression ) ) { if ( ( (TableGroupProducer) getModelPart() ).containsTableReference( tableExpression ) ) {
return getPrimaryTableReference(); return getPrimaryTableReference();
@ -54,7 +53,7 @@ public class ValuesTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getNestedTableGroupJoins() ) {
final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() final TableReference groupTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference() .getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); .getTableReference( navigablePath, tableExpression, resolve );
if ( groupTableReference != null ) { if ( groupTableReference != null ) {
return groupTableReference; return groupTableReference;
} }
@ -62,7 +61,7 @@ public class ValuesTableGroup extends AbstractTableGroup {
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
final TableReference groupTableReference = tableGroupJoin.getJoinedGroup() final TableReference groupTableReference = tableGroupJoin.getJoinedGroup()
.getPrimaryTableReference() .getPrimaryTableReference()
.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ); .getTableReference( navigablePath, tableExpression, resolve );
if ( groupTableReference != null ) { if ( groupTableReference != null ) {
return groupTableReference; return groupTableReference;
} }

View File

@ -10,6 +10,7 @@ import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
@ -61,7 +62,9 @@ public class MutatingTableReference implements TableReference {
} }
@Override @Override
public TableReference resolveTableReference(NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization) { public TableReference resolveTableReference(
NavigablePath navigablePath,
String tableExpression) {
if ( getTableName().equals( tableExpression ) ) { if ( getTableName().equals( tableExpression ) ) {
return this; return this;
} }
@ -77,8 +80,36 @@ public class MutatingTableReference implements TableReference {
} }
@Override @Override
public TableReference getTableReference(NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization, boolean resolve) { public TableReference resolveTableReference(
return resolveTableReference( navigablePath, tableExpression, allowFkOptimization ); 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 @Override

View File

@ -24,9 +24,9 @@ public interface DatabaseSnapshotContributor extends Fetchable {
*/ */
default <T> DomainResult<T> createSnapshotDomainResult( default <T> DomainResult<T> createSnapshotDomainResult(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup parentTableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
return createDomainResult( navigablePath, tableGroup, null, creationState ); return createDomainResult( navigablePath, parentTableGroup, null, creationState );
} }
} }

View File

@ -11,9 +11,12 @@ import java.util.List;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.AssociationKey; 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.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.spi.EntityIdentifierNavigablePath; import org.hibernate.spi.EntityIdentifierNavigablePath;
@ -88,8 +91,7 @@ public interface DomainResultCreationState {
ModelPart resolveModelPart(NavigablePath navigablePath); ModelPart resolveModelPart(NavigablePath navigablePath);
default Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) { default Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) {
final EntityIdentifierMapping identifierMapping = fetchParent.getEntityValuedModelPart() final EntityIdentifierMapping identifierMapping = fetchParent.getReferencedMappingContainer()
.getEntityMappingType()
.getIdentifierMapping(); .getIdentifierMapping();
return fetchParent.generateFetchableFetch( return fetchParent.generateFetchableFetch(
(Fetchable) identifierMapping, (Fetchable) identifierMapping,

View File

@ -37,7 +37,7 @@ public interface FetchParent extends DomainResultGraphNode {
default NavigablePath resolveNavigablePath(Fetchable fetchable) { default NavigablePath resolveNavigablePath(Fetchable fetchable) {
final String fetchableName = fetchable.getFetchableName(); final String fetchableName = fetchable.getFetchableName();
if ( NavigablePath.IDENTIFIER_MAPPER_PROPERTY.equals( fetchableName ) || fetchable instanceof EntityIdentifierMapping ) { if ( fetchable instanceof EntityIdentifierMapping ) {
return new EntityIdentifierNavigablePath( getNavigablePath(), fetchableName ); return new EntityIdentifierNavigablePath( getNavigablePath(), fetchableName );
} }
else { else {

View File

@ -92,7 +92,7 @@ public interface Fetchable extends ModelPart {
default boolean isSelectable() { default boolean isSelectable() {
final AttributeMapping attributeMapping = asAttributeMapping(); final AttributeMapping attributeMapping = asAttributeMapping();
if ( attributeMapping != null ) { if ( attributeMapping != null && attributeMapping.getAttributeMetadata() != null ) {
return attributeMapping.getAttributeMetadata().isSelectable(); return attributeMapping.getAttributeMetadata().isSelectable();
} }
else { else {

View File

@ -12,6 +12,7 @@ import java.util.List;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.collection.spi.CollectionInitializerProducer; import org.hibernate.collection.spi.CollectionInitializerProducer;
import org.hibernate.collection.spi.CollectionSemantics; import org.hibernate.collection.spi.CollectionSemantics;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
@ -53,10 +54,11 @@ public class CollectionDomainResult implements DomainResult, CollectionResultGra
this.loadingAttribute = loadingAttribute; this.loadingAttribute = loadingAttribute;
this.resultVariable = resultVariable; this.resultVariable = resultVariable;
this.tableGroup = tableGroup; this.tableGroup = tableGroup;
// The collection is always the target side
fkResult = loadingAttribute.getKeyDescriptor().createKeyDomainResult( this.fkResult = loadingAttribute.getKeyDescriptor().createKeyDomainResult(
loadingPath, loadingPath,
tableGroup, tableGroup,
ForeignKeyDescriptor.Nature.TARGET,
this, this,
creationState creationState
); );

View File

@ -99,9 +99,11 @@ public class EagerCollectionFetch extends CollectionFetch implements FetchParent
fetchParent, fetchParent,
creationState creationState
); );
// The collection is always the target side
collectionValueKeyResult = keyDescriptor.createKeyDomainResult( collectionValueKeyResult = keyDescriptor.createKeyDomainResult(
fetchedPath, fetchedPath,
collectionTableGroup, collectionTableGroup,
ForeignKeyDescriptor.Nature.TARGET,
fetchParent, fetchParent,
creationState creationState
); );

View File

@ -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. // 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, // 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. // so we can't use the fetch parent access in that case.
if ( fetchParentAccess != null && embedded instanceof VirtualModelPart if ( fetchParentAccess != null && embedded instanceof VirtualModelPart && !isPartOfKey ) {
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() )
&& !ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() )
&& !ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getLocalName() ) ) {
fetchParentAccess.resolveInstance( processingState ); fetchParentAccess.resolveInstance( processingState );
compositeInstance = fetchParentAccess.getInitializedInstance(); compositeInstance = fetchParentAccess.getInitializedInstance();
EntityInitializer entityInitializer = fetchParentAccess.asEntityInitializer(); EntityInitializer entityInitializer = fetchParentAccess.asEntityInitializer();

View File

@ -6,15 +6,10 @@
*/ */
package org.hibernate.sql.results.graph.entity; package org.hibernate.sql.results.graph.entity;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityRowIdMapping; import org.hibernate.metamodel.mapping.EntityRowIdMapping;
import org.hibernate.metamodel.mapping.EntityValuedModelPart; 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.persister.entity.AbstractEntityPersister;
import org.hibernate.spi.EntityIdentifierNavigablePath;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.results.graph.AbstractFetchParent; 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.sql.results.graph.basic.BasicFetch;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
import static org.hibernate.query.results.ResultsHelper.attributeName;
/** /**
* AbstractFetchParent sub-class for entity-valued graph nodes * AbstractFetchParent sub-class for entity-valued graph nodes
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent implements EntityResultGraphNode { public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent implements EntityResultGraphNode {
private final EntityValuedModelPart referencedModelPart;
private Fetch identifierFetch; private Fetch identifierFetch;
private BasicFetch<?> discriminatorFetch; private BasicFetch<?> discriminatorFetch;
private DomainResult<Object> rowIdResult; private DomainResult<Object> rowIdResult;
public AbstractEntityResultGraphNode(EntityValuedModelPart referencedModelPart, NavigablePath navigablePath) { public AbstractEntityResultGraphNode(EntityValuedModelPart referencedModelPart, NavigablePath navigablePath) {
super( referencedModelPart.getEntityMappingType(), navigablePath ); super( referencedModelPart, navigablePath );
this.referencedModelPart = referencedModelPart;
} }
@Override @Override
public void afterInitialize(FetchParent fetchParent, DomainResultCreationState creationState) { public void afterInitialize(FetchParent fetchParent, DomainResultCreationState creationState) {
final EntityMappingType entityDescriptor = referencedModelPart.getEntityMappingType();
final EntityIdentifierMapping identifierMapping = entityDescriptor.getIdentifierMapping();
final NavigablePath navigablePath = getNavigablePath(); final NavigablePath navigablePath = getNavigablePath();
final TableGroup entityTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess() final TableGroup entityTableGroup = creationState.getSqlAstCreationState().getFromClauseAccess()
.getTableGroup( navigablePath ); .getTableGroup( navigablePath );
final EntityResultGraphNode entityResultGraphNode = (EntityResultGraphNode) fetchParent;
if ( navigablePath.getParent() == null && !creationState.forceIdentifierSelection() ) { if ( navigablePath.getParent() == null && !creationState.forceIdentifierSelection() ) {
identifierFetch = null; identifierFetch = null;
visitIdentifierMapping( creationState.visitIdentifierFetch( entityResultGraphNode );
new EntityIdentifierNavigablePath( navigablePath, attributeName( identifierMapping ) ),
creationState,
identifierMapping,
entityTableGroup
);
} }
else { 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 ) { if ( rowIdMapping == null ) {
rowIdResult = null; rowIdResult = null;
} }
@ -80,45 +65,6 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
super.afterInitialize( fetchParent, creationState ); 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 @Override
public EntityMappingType getReferencedMappingContainer() { public EntityMappingType getReferencedMappingContainer() {
return getEntityValuedModelPart().getEntityMappingType(); return getEntityValuedModelPart().getEntityMappingType();
@ -126,7 +72,7 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
@Override @Override
public EntityValuedModelPart getEntityValuedModelPart() { public EntityValuedModelPart getEntityValuedModelPart() {
return referencedModelPart; return (EntityValuedModelPart) getFetchContainer();
} }
@Override @Override
@ -145,4 +91,5 @@ public abstract class AbstractEntityResultGraphNode extends AbstractFetchParent
public DomainResult<Object> getRowIdResult() { public DomainResult<Object> getRowIdResult() {
return rowIdResult; return rowIdResult;
} }
} }

View File

@ -22,20 +22,18 @@ import org.hibernate.sql.results.graph.entity.internal.EntityAssembler;
*/ */
public abstract class AbstractNonLazyEntityFetch extends AbstractFetchParent implements EntityFetch { public abstract class AbstractNonLazyEntityFetch extends AbstractFetchParent implements EntityFetch {
private final FetchParent fetchParent; private final FetchParent fetchParent;
private final EntityValuedFetchable referencedModelPart;
public AbstractNonLazyEntityFetch( public AbstractNonLazyEntityFetch(
FetchParent fetchParent, FetchParent fetchParent,
EntityValuedFetchable fetchedPart, EntityValuedFetchable fetchedPart,
NavigablePath navigablePath) { NavigablePath navigablePath) {
super( fetchedPart.getEntityMappingType(), navigablePath ); super( fetchedPart, navigablePath );
this.referencedModelPart = fetchedPart;
this.fetchParent = fetchParent; this.fetchParent = fetchParent;
} }
@Override @Override
public EntityValuedFetchable getEntityValuedModelPart() { public EntityValuedFetchable getEntityValuedModelPart() {
return referencedModelPart; return (EntityValuedFetchable) getFetchContainer();
} }
@Override @Override

View File

@ -8,6 +8,7 @@ package org.hibernate.sql.results.graph.entity.internal;
import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.spi.NavigablePath; import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
@ -33,13 +34,14 @@ public class EntityDelayedResultImpl implements DomainResult {
public EntityDelayedResultImpl( public EntityDelayedResultImpl(
NavigablePath navigablePath, NavigablePath navigablePath,
EntityAssociationMapping entityValuedModelPart, EntityAssociationMapping entityValuedModelPart,
TableGroup rootTableGroup, TableGroup targetTableGroup,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
this.navigablePath = navigablePath; this.navigablePath = navigablePath;
this.entityValuedModelPart = entityValuedModelPart; this.entityValuedModelPart = entityValuedModelPart;
this.identifierResult = entityValuedModelPart.getForeignKeyDescriptor().createKeyDomainResult( this.identifierResult = entityValuedModelPart.getForeignKeyDescriptor().createKeyDomainResult(
navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ), navigablePath.append( EntityIdentifierMapping.ROLE_LOCAL_NAME ),
rootTableGroup, targetTableGroup,
entityValuedModelPart.getSideNature(),
null, null,
creationState creationState
); );

View File

@ -44,7 +44,12 @@ public class NotFoundSnapshotResult implements DomainResult {
// however, that would mean a 1-1 with a join-table which // however, that would mean a 1-1 with a join-table which
// is pretty odd mapping // is pretty odd mapping
final ForeignKeyDescriptor fkDescriptor = toOneMapping.getForeignKeyDescriptor(); 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( this.targetResult = fkDescriptor.createTargetDomainResult(
navigablePath, navigablePath,
targetTableGroup, targetTableGroup,

View File

@ -20,6 +20,7 @@ public class CardField implements Serializable {
@EmbeddedId @EmbeddedId
private PrimaryKey primaryKey; private PrimaryKey primaryKey;
private String name;
CardField(Card card, Key key) { CardField(Card card, Key key) {
this.primaryKey = new PrimaryKey( card, key); this.primaryKey = new PrimaryKey( card, key);

View File

@ -21,6 +21,7 @@ import jakarta.persistence.Table;
public class Key implements Serializable { public class Key implements Serializable {
@Id @Id
private String id; private String id;
private String name;
public Key(String id) { public Key(String id) {
this.id = id; this.id = id;

View File

@ -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;
}
}
}

View File

@ -108,7 +108,7 @@ public class LazyManyToOneEmbeddedIdWithToOneFKTest {
assertTrue( Hibernate.isInitialized( system.getUser() ) ); assertTrue( Hibernate.isInitialized( system.getUser() ) );
statementInspector.assertExecutedCount( 1 ); 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() ) ); assertTrue( Hibernate.isInitialized( system.getUser() ) );
statementInspector.assertExecutedCount( 1 ); statementInspector.assertExecutedCount( 1 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 );
} }
); );

View File

@ -89,7 +89,7 @@ public class ManyToOneEmbeddedIdWithToOneFKTest {
statementInspector.assertExecutedCount( 3 ); statementInspector.assertExecutedCount( 3 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 ); statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 0 );
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 );
statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 0 ); statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 1 );
assertTrue( Hibernate.isInitialized( system.getDataCenterUser() ) ); assertTrue( Hibernate.isInitialized( system.getDataCenterUser() ) );
@ -119,7 +119,7 @@ public class ManyToOneEmbeddedIdWithToOneFKTest {
statementInspector.assertExecutedCount( 3 ); statementInspector.assertExecutedCount( 3 );
statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 ); statementInspector.assertNumberOfOccurrenceInQuery( 0, "join", 1 );
statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 ); statementInspector.assertNumberOfOccurrenceInQuery( 1, "join", 0 );
statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 0 ); statementInspector.assertNumberOfOccurrenceInQuery( 2, "join", 1 );
assertThat( system, is( notNullValue() ) ); assertThat( system, is( notNullValue() ) );
DataCenterUser user = system.getDataCenterUser(); DataCenterUser user = system.getDataCenterUser();
assertThat( user, is( notNullValue() ) ); assertThat( user, is( notNullValue() ) );

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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<OrganisationUser> 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<OrganisationUser> 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<OrganisationUser> 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<OrganisationUser> 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<OrganisationUser> 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<OrganisationUser> 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;
}
}
}

View File

@ -22,6 +22,7 @@ import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.hql.spi.SqmQueryImplementor;
import org.hibernate.query.spi.QueryImplementor; 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.instanceOf;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue; 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.AssignableMatcher.assignableTo;
import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize; import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize;
import static org.hibernate.testing.hamcrest.CollectionMatchers.isEmpty; import static org.hibernate.testing.hamcrest.CollectionMatchers.isEmpty;
import static org.junit.Assert.assertThat;
/** /**
* @author Nathan Xu * @author Nathan Xu
@ -286,8 +287,11 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware {
.next() .next()
.getJoinedGroup(); .getJoinedGroup();
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) ); assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() );
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() ); assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) );
final TableGroup joinedGroup = compositeTableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup();
assertThat( joinedGroup.isInitialized(), is( false ) );
} }
else { else {
assertThat( tableGroup.getTableGroupJoins(), isEmpty() ); assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
@ -295,8 +299,11 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware {
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup(); final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup();
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) ); assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() );
assertThat( compositeTableGroup.getNestedTableGroupJoins(), 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 ) ); assertThat( fromClause.getRoots(), hasSize( 1 ) );
final TableGroup rootTableGroup = fromClause.getRoots().get( 0 ); 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<TableGroup> tableGroupConsumer) { private void assertEntityValuedJoinedGroup(SelectStatement sqlAst, String expectedAttributeName, Class<?> expectedEntityJpaClass, Consumer<TableGroup> tableGroupConsumer) {
@ -343,12 +353,18 @@ public class CriteriaEntityGraphTest implements SessionFactoryScopeAware {
} }
private void assertPersonHomeAddressJoinedGroup(TableGroup tableGroup) { private void assertPersonHomeAddressJoinedGroup(TableGroup tableGroup) {
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) ); assertThat( tableGroup.getTableGroupJoins(), hasSize( 2 ) );
final TableGroup joinedGroup = tableGroup.getTableGroupJoins().iterator().next().getJoinedGroup(); final TableGroup company = tableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup();
assertThat( joinedGroup.getModelPart().getPartName(), is( "homeAddress" ) ); assertThat( company.getModelPart().getPartName(), is( "company" ) );
assertThat( joinedGroup.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) ); assertThat( company.getModelPart(), instanceOf( ToOneAttributeMapping.class ) );
assertThat( joinedGroup, instanceOf( StandardVirtualTableGroup.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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // util methods for verifying 'domain-result' graph ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -24,6 +24,7 @@ import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.sql.ast.tree.from.FromClause; import org.hibernate.sql.ast.tree.from.FromClause;
import org.hibernate.sql.ast.tree.from.LazyTableGroup; 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.instanceOf;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue; 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.AssignableMatcher.assignableTo;
import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize; import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize;
import static org.hibernate.testing.hamcrest.CollectionMatchers.isEmpty; import static org.hibernate.testing.hamcrest.CollectionMatchers.isEmpty;
import static org.junit.Assert.assertThat;
/** /**
* @author Strong Liu * @author Strong Liu
@ -268,8 +269,11 @@ public class EntityGraphLoadPlanBuilderTest implements SessionFactoryScopeAware
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup(); final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup();
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) ); assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() );
assertThat( compositeTableGroup.getNestedTableGroupJoins(), 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 ) ); assertThat( fromClause.getRoots(), hasSize( 1 ) );
final TableGroup rootTableGroup = fromClause.getRoots().get( 0 ); 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<TableGroup> tableGroupConsumer) { private void assertEntityValuedJoinedGroup(SelectStatement sqlAst, String expectedAttributeName, Class<?> expectedEntityJpaClass, Consumer<TableGroup> tableGroupConsumer) {
@ -316,12 +323,18 @@ public class EntityGraphLoadPlanBuilderTest implements SessionFactoryScopeAware
} }
private void assertPersonHomeAddressJoinedGroup(TableGroup tableGroup) { private void assertPersonHomeAddressJoinedGroup(TableGroup tableGroup) {
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) ); assertThat( tableGroup.getTableGroupJoins(), hasSize( 2 ) );
final TableGroup joinedGroup = CollectionUtils.getOnlyElement( tableGroup.getTableGroupJoins() ).getJoinedGroup(); final TableGroup company = tableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup();
assertThat( joinedGroup.getModelPart().getPartName(), is( "homeAddress" ) ); assertThat( company.getModelPart().getPartName(), is( "company" ) );
assertThat( joinedGroup.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) ); assertThat( company.getModelPart(), instanceOf( ToOneAttributeMapping.class ) );
assertThat( joinedGroup, instanceOf( StandardVirtualTableGroup.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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // util methods for verifying 'domain-result' graph ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -22,6 +22,7 @@ import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping; import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.hql.spi.SqmQueryImplementor; import org.hibernate.query.hql.spi.SqmQueryImplementor;
import org.hibernate.query.spi.QueryImplementor; 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.instanceOf;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue; 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.AssignableMatcher.assignableTo;
import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize; import static org.hibernate.testing.hamcrest.CollectionMatchers.hasSize;
import static org.hibernate.testing.hamcrest.CollectionMatchers.isEmpty; import static org.hibernate.testing.hamcrest.CollectionMatchers.isEmpty;
import static org.junit.Assert.assertThat;
/** /**
* @author Nathan Xu * @author Nathan Xu
@ -284,8 +285,11 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware {
.next() .next()
.getJoinedGroup(); .getJoinedGroup();
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) ); assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty( ) );
assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() ); assertThat( compositeTableGroup.getNestedTableGroupJoins(), isEmpty() );
assertThat( compositeTableGroup.getTableGroupJoins(), hasSize( 1 ) );
final TableGroup joinedGroup = compositeTableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup();
assertThat( joinedGroup.isInitialized(), is( false ) );
} }
else { else {
assertThat( tableGroup.getTableGroupJoins(), isEmpty() ); assertThat( tableGroup.getTableGroupJoins(), isEmpty() );
@ -293,8 +297,11 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware {
final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup(); final TableGroup compositeTableGroup = CollectionUtils.getOnlyElement( tableGroup.getNestedTableGroupJoins() ).getJoinedGroup();
assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) ); assertThat( compositeTableGroup, instanceOf( StandardVirtualTableGroup.class ) );
assertThat( compositeTableGroup.getTableGroupJoins(), isEmpty() );
assertThat( compositeTableGroup.getNestedTableGroupJoins(), 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 ) ); assertThat( fromClause.getRoots(), hasSize( 1 ) );
final TableGroup rootTableGroup = fromClause.getRoots().get( 0 ); 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<TableGroup> tableGroupConsumer) { private void assertEntityValuedJoinedGroup(SelectStatement sqlAst, String expectedAttributeName, Class<?> expectedEntityJpaClass, Consumer<TableGroup> tableGroupConsumer) {
@ -333,19 +343,26 @@ public class HqlEntityGraphTest implements SessionFactoryScopeAware {
final TableGroup root = fromClause.getRoots().get( 0 ); final TableGroup root = fromClause.getRoots().get( 0 );
assertThat( root.getTableGroupJoins(), hasSize( 1 ) ); 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().getPartName(), is( expectedPluralAttributeName ) );
assertThat( joinedGroup.getModelPart(), instanceOf( PluralAttributeMapping.class ) ); assertThat( joinedGroup.getModelPart(), instanceOf( PluralAttributeMapping.class ) );
tableGroupConsumer.accept( joinedGroup ); tableGroupConsumer.accept( joinedGroup );
} }
private void assertPersonHomeAddressJoinedGroup(TableGroup tableGroup) { private void assertPersonHomeAddressJoinedGroup(TableGroup tableGroup) {
assertThat( tableGroup.getTableGroupJoins(), hasSize( 1 ) );
final TableGroup joinedGroup = tableGroup.getTableGroupJoins().iterator().next().getJoinedGroup(); assertThat( tableGroup.getTableGroupJoins(), hasSize( 2 ) );
assertThat( joinedGroup.getModelPart().getPartName(), is( "homeAddress" ) );
assertThat( joinedGroup.getModelPart(), instanceOf( EmbeddedAttributeMapping.class ) ); final TableGroup company = tableGroup.getTableGroupJoins().get( 0 ).getJoinedGroup();
assertThat( joinedGroup, instanceOf( StandardVirtualTableGroup.class ) ); 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // util methods for verifying 'domain-result' graph ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -46,7 +46,7 @@ class HHH15065Test {
SQLStatementInspector statementInspector = scope.getCollectingStatementInspector(); SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
List<String> sqlQueries = statementInspector.getSqlQueries(); List<String> sqlQueries = statementInspector.getSqlQueries();
assertEquals( 1, sqlQueries.size() ); 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" + " from Book b1_0" +
" left join Person a1_0 on a1_0.id=b1_0.author_id" + " 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" + " left join Person c1_0 on c1_0.id=b1_0.coAuthor_id" +
@ -73,9 +73,10 @@ class HHH15065Test {
} }
@Entity(name = "Person") @Entity(name = "Person")
public class Person { public static class Person {
@Id @Id
Long id; Long id;
String name;
} }
} }

View File

@ -25,6 +25,8 @@ import jakarta.persistence.Table;
import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.JoinType;
import static org.junit.jupiter.api.Assertions.assertEquals;
/** /**
* @author Christian Beikov * @author Christian Beikov
*/ */
@ -41,9 +43,9 @@ public class ManyToOneJoinReuseTest {
@TestForIssue(jiraKey = "HHH-15648") @TestForIssue(jiraKey = "HHH-15648")
public void fetchAndImplicitPath(SessionFactoryScope scope) { public void fetchAndImplicitPath(SessionFactoryScope scope) {
SQLStatementInspector sqlStatementInterceptor = scope.getCollectingStatementInspector(); SQLStatementInspector sqlStatementInterceptor = scope.getCollectingStatementInspector();
sqlStatementInterceptor.clear();
scope.inTransaction( scope.inTransaction(
session -> { session -> {
sqlStatementInterceptor.clear();
HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
JpaCriteriaQuery<BookList> query = cb.createQuery( BookList.class ); JpaCriteriaQuery<BookList> query = cb.createQuery( BookList.class );
@ -52,7 +54,11 @@ public class ManyToOneJoinReuseTest {
query.where( root.get( "book" ).isNotNull() ); query.where( root.get( "book" ).isNotNull() );
session.createQuery( query ).getResultList(); 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") @TestForIssue(jiraKey = "HHH-15645")
public void joinAndImplicitPath(SessionFactoryScope scope) { public void joinAndImplicitPath(SessionFactoryScope scope) {
SQLStatementInspector sqlStatementInterceptor = scope.getCollectingStatementInspector(); SQLStatementInspector sqlStatementInterceptor = scope.getCollectingStatementInspector();
sqlStatementInterceptor.clear();
scope.inTransaction( scope.inTransaction(
session -> { session -> {
sqlStatementInterceptor.clear();
HibernateCriteriaBuilder cb = session.getCriteriaBuilder(); HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
JpaCriteriaQuery<BookList> query = cb.createQuery( BookList.class ); JpaCriteriaQuery<BookList> query = cb.createQuery( BookList.class );
@ -77,7 +83,11 @@ public class ManyToOneJoinReuseTest {
); );
session.createQuery( query ).getResultList(); 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 )
);
} }
); );
} }

View File

@ -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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
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<OrganisationUser> 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<OrganisationUser> 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<OrganisationUser> 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<OrganisationUser> 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<OrganisationUser> 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<OrganisationUser> 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;
}
}
}

Some files were not shown because too many files have changed in this diff Show More