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

View File

@ -291,4 +291,8 @@ public abstract class AbstractCompositeIdentifierMapping
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
*/
public interface AttributeMapping
extends ValuedModelPart, Fetchable, DatabaseSnapshotContributor, PropertyBasedMapping, MutabilityPlanExposer {
extends OwnedValuedModelPart, Fetchable, DatabaseSnapshotContributor, PropertyBasedMapping, MutabilityPlanExposer {
/**
* The name of the mapped attribute
*/

View File

@ -18,4 +18,11 @@ public interface BasicEntityIdentifierMapping extends SingleAttributeIdentifierM
default int getFetchableKey() {
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.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.mapping.internal.VirtualIdEmbeddable;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.tree.from.TableGroup;
@ -42,7 +43,9 @@ public interface ForeignKeyDescriptor extends VirtualModelPart, ValuedModelPart
ValuedModelPart getTargetPart();
default ModelPart getPart(Nature nature) {
boolean isKeyPart(ValuedModelPart modelPart);
default ValuedModelPart getPart(Nature nature) {
if ( nature == Nature.KEY ) {
return getKeyPart();
}
@ -76,27 +79,45 @@ public interface ForeignKeyDescriptor extends VirtualModelPart, ValuedModelPart
/**
* Create a DomainResult for the referring-side of the fk
* The table group must be the one containing the target.
*/
DomainResult<?> createKeyDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
TableGroup targetTableGroup,
FetchParent fetchParent,
DomainResultCreationState creationState);
/**
* Create a DomainResult for the referring-side of the fk
* The table group must be the one containing the target.
* The {@link Nature} is the association side of the foreign key i.e. {@link Association#getSideNature()}.
*/
DomainResult<?> createKeyDomainResult(
NavigablePath navigablePath,
TableGroup targetTableGroup,
Nature fromSide,
FetchParent fetchParent,
DomainResultCreationState creationState);
/**
* Create a DomainResult for the target-side of the fk
* The table group must be the one containing the target
*/
DomainResult<?> createTargetDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
TableGroup targetTableGroup,
FetchParent fetchParent,
DomainResultCreationState creationState);
DomainResult<?> createDomainResult(
/**
* Create a DomainResult for the referring-side of the fk
* The table group must be the one containing the target.
*/
@Override
<T> DomainResult<T> createDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
Nature side,
FetchParent fetchParent,
TableGroup targetTableGroup,
String resultVariable,
DomainResultCreationState creationState);
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
default <T> DomainResult<T> createSnapshotDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
TableGroup parentTableGroup,
String resultVariable,
DomainResultCreationState creationState) {
return new BasicResult( 0, null, getJavaType() );

View File

@ -63,7 +63,11 @@ public abstract class AbstractDomainPath implements DomainPath {
SqlAstCreationState creationState) {
if ( referenceModelPart instanceof BasicValuedModelPart ) {
final BasicValuedModelPart selection = (BasicValuedModelPart) referenceModelPart;
final TableReference tableReference = tableGroup.resolveTableReference( getNavigablePath(), selection.getContainingTableExpression() );
final TableReference tableReference = tableGroup.resolveTableReference(
getNavigablePath(),
selection,
selection.getContainingTableExpression()
);
return creationState.getSqlExpressionResolver().resolveSqlExpression(
createColumnReferenceKey( tableReference, selection.getSelectionExpression() ),
processingState -> new ColumnReference(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -298,12 +298,12 @@ public class EmbeddedAttributeMapping
}
final List<ColumnReference> columnReferences = CollectionHelper.arrayList( embeddableMappingType.getJdbcTypeCount() );
final NavigablePath navigablePath = tableGroup.getNavigablePath().append( getNavigableRole().getNavigableName() );
final TableReference defaultTableReference = tableGroup.resolveTableReference( navigablePath, getContainingTableExpression() );
final TableReference defaultTableReference = tableGroup.resolveTableReference( navigablePath, this, getContainingTableExpression() );
getEmbeddableTypeDescriptor().forEachSelectable(
(columnIndex, selection) -> {
final TableReference tableReference = getContainingTableExpression().equals( selection.getContainingTableExpression() )
? defaultTableReference
: tableGroup.resolveTableReference( navigablePath, selection.getContainingTableExpression() );
: tableGroup.resolveTableReference( navigablePath, this, selection.getContainingTableExpression() );
final Expression columnReference = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression(
tableReference,
selection
@ -394,4 +394,17 @@ public class EmbeddedAttributeMapping
public boolean isSelectable() {
return selectable;
}
@Override
public boolean containsTableReference(String tableExpression) {
final ManagedMappingType declaringType = getDeclaringType();
final TableGroupProducer producer;
if ( declaringType instanceof TableGroupProducer ) {
producer = (TableGroupProducer) declaringType;
}
else {
producer = ( (EmbeddableMappingType) declaringType ).getEmbeddedValueMapping();
}
return producer.containsTableReference( tableExpression );
}
}

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

View File

@ -16,6 +16,7 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.internal.util.MutableInteger;
import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.CompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType;
@ -24,9 +25,11 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.query.sqm.ComparisonOperator;
import org.hibernate.spi.NavigablePath;
@ -35,10 +38,12 @@ import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.from.OneToManyTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.VirtualTableGroup;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction;
import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -171,6 +176,27 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
return targetSide.getModelPart().getEmbeddableTypeDescriptor().getEmbeddedValueMapping();
}
@Override
public boolean isKeyPart(ValuedModelPart modelPart) {
final EmbeddableValuedModelPart keyPart = getKeyPart();
if ( this == modelPart || keyPart == modelPart ) {
return true;
}
else if ( keyPart instanceof NonAggregatedIdentifierMapping ) {
final AttributeMappingsList attributeMappings = ( (NonAggregatedIdentifierMapping) keyPart ).getVirtualIdEmbeddable()
.getAttributeMappings();
for ( int i = 0; i < attributeMappings.size(); i++ ) {
if ( modelPart == attributeMappings.get( i ) ) {
return true;
}
}
}
else if ( keyPart.isVirtual() && keyPart.getNumberOfFetchables() == 1 ) {
return keyPart.getFetchable( 0 ) == modelPart;
}
return false;
}
@Override
public Side getKeySide() {
return keySide;
@ -209,15 +235,35 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override
public DomainResult<?> createKeyDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
TableGroup targetTableGroup,
FetchParent fetchParent,
DomainResultCreationState creationState) {
assert isTargetTableGroup( targetTableGroup );
return createDomainResult(
navigablePath,
tableGroup,
targetTableGroup,
null,
keyTable,
keySide.getModelPart(),
Nature.KEY,
fetchParent,
creationState
);
}
@Override
public DomainResult<?> createKeyDomainResult(
NavigablePath navigablePath,
TableGroup targetTableGroup,
Nature fromSide,
FetchParent fetchParent,
DomainResultCreationState creationState) {
assert fromSide == Nature.TARGET
? targetTableGroup.getTableReference( navigablePath, associationKey.getTable(), false ) != null
: isTargetTableGroup( targetTableGroup );
return createDomainResult(
navigablePath.append( ForeignKeyDescriptor.PART_NAME ),
targetTableGroup,
null,
Nature.KEY,
fetchParent,
creationState
);
@ -226,70 +272,57 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override
public DomainResult<?> createTargetDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
TableGroup targetTableGroup,
FetchParent fetchParent,
DomainResultCreationState creationState) {
assert tableGroup.getTableReference( navigablePath, targetTable ) != null;
assert isTargetTableGroup( targetTableGroup );
return createDomainResult(
navigablePath,
tableGroup,
targetTableGroup,
null,
targetTable,
targetSide.getModelPart(),
Nature.TARGET,
fetchParent,
creationState
);
}
@Override
public DomainResult<?> createDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
Nature side,
FetchParent fetchParent,
DomainResultCreationState creationState) {
if ( side == Nature.KEY ) {
return createDomainResult(
navigablePath,
tableGroup,
null,
keyTable,
keySide.getModelPart(),
fetchParent,
creationState
);
}
else {
return createDomainResult(
navigablePath,
tableGroup,
null,
targetTable,
targetSide.getModelPart(),
fetchParent,
creationState
);
}
}
@Override
public <T> DomainResult<T> createDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
TableGroup targetTableGroup,
String resultVariable,
DomainResultCreationState creationState) {
assert isTargetTableGroup( targetTableGroup );
return createDomainResult(
navigablePath,
tableGroup,
targetTableGroup,
resultVariable,
keyTable,
keySide.getModelPart(),
Nature.KEY,
null,
creationState
);
}
private boolean isTargetTableGroup(TableGroup tableGroup) {
tableGroup = getUnderlyingTableGroup( tableGroup );
final TableGroupProducer tableGroupProducer;
if ( tableGroup instanceof OneToManyTableGroup ) {
tableGroupProducer = (TableGroupProducer) ( (OneToManyTableGroup) tableGroup ).getElementTableGroup()
.getModelPart();
}
else {
tableGroupProducer = (TableGroupProducer) tableGroup.getModelPart();
}
return tableGroupProducer.containsTableReference( targetSide.getModelPart().getContainingTableExpression() );
}
private static TableGroup getUnderlyingTableGroup(TableGroup tableGroup) {
if ( tableGroup instanceof VirtualTableGroup ) {
tableGroup = getUnderlyingTableGroup( ( (VirtualTableGroup) tableGroup ).getUnderlyingTableGroup() );
}
return tableGroup;
}
@Override
public void applySqlSelections(
NavigablePath navigablePath,
@ -311,15 +344,17 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
NavigablePath navigablePath,
TableGroup tableGroup,
String resultVariable,
String columnContainingTable,
EmbeddableValuedModelPart modelPart,
Nature nature,
FetchParent fetchParent,
DomainResultCreationState creationState) {
final EmbeddableValuedModelPart modelPart;
final NavigablePath resultNavigablePath;
if ( modelPart == keySide.getModelPart() ) {
if ( nature == Nature.KEY ) {
modelPart = keySide.getModelPart();
resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.PART_NAME );
}
else {
modelPart = targetSide.getModelPart();
resultNavigablePath = navigablePath.append( ForeignKeyDescriptor.TARGET_PART_NAME );
}
@ -343,7 +378,7 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
final Nature currentForeignKeyResolvingKey = creationState.getCurrentlyResolvingForeignKeyPart();
try {
creationState.setCurrentlyResolvingForeignKeyPart( keySide.getModelPart() == modelPart ? Nature.KEY : Nature.TARGET );
creationState.setCurrentlyResolvingForeignKeyPart( nature );
return new EmbeddableForeignKeyResultImpl<>(
resultNavigablePath,
modelPart,
@ -364,13 +399,11 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
SqlAstCreationState creationState) {
final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference(
targetSideTableGroup.getNavigablePath(),
targetTable,
false
targetTable
);
final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference(
null,
keyTable,
false
keyTable
);
return generateJoinPredicate( lhsTableReference, rhsTableKeyReference, creationState );

View File

@ -139,7 +139,7 @@ public class IdClassEmbeddable extends AbstractEmbeddableMapping implements Iden
selectableMappings,
inverseMappingType,
creationProcess,
valueMapping.getDeclaringType(),
this,
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.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.type.EntityType;
@ -74,7 +75,8 @@ import static org.hibernate.metamodel.mapping.internal.MappingModelCreationHelpe
*
* @author Steve Ebersole
*/
public class ManyToManyCollectionPart extends AbstractEntityCollectionPart implements EntityAssociationMapping {
public class ManyToManyCollectionPart extends AbstractEntityCollectionPart implements EntityAssociationMapping,
LazyTableGroup.ParentTableGroupUseChecker {
private ForeignKeyDescriptor foreignKey;
private ValuedModelPart fkTargetModelPart;
@ -292,24 +294,7 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
sqlAliasBase,
creationState
),
(np, tableExpression) -> {
if ( ! foreignKey.getKeyTable().equals( tableExpression ) ) {
return false;
}
if ( navigablePath.equals( np.getParent() ) ) {
return getTargetKeyPropertyNames().contains( np.getLocalName() );
}
final String relativePath = np.relativize( navigablePath );
if ( relativePath == null ) {
return false;
}
// Empty relative path means the navigable paths are equal,
// in which case we allow resolving the parent table group
return relativePath.isEmpty() || getTargetKeyPropertyNames().contains( relativePath );
},
this,
this,
explicitSourceAlias,
sqlAliasBase,
@ -337,6 +322,11 @@ public class ManyToManyCollectionPart extends AbstractEntityCollectionPart imple
return lazyTableGroup;
}
@Override
public boolean canUseParentTableGroup(TableGroupProducer producer, NavigablePath navigablePath, ValuedModelPart valuedModelPart) {
return foreignKey.isKeyPart( valuedModelPart );
}
@Override
public boolean hasPartitionedSelectionMapping() {
return foreignKey.hasPartitionedSelectionMapping();

View File

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

View File

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

View File

@ -6,18 +6,85 @@
*/
package org.hibernate.metamodel.mapping.internal;
import org.hibernate.generator.Generator;
import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMetadata;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.PropertyBasedMapping;
import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.type.descriptor.java.ImmutableMutabilityPlan;
import org.hibernate.type.descriptor.java.MutabilityPlan;
/**
* @author Steve Ebersole
*/
public interface SingleAttributeIdentifierMapping extends EntityIdentifierMapping, PropertyBasedMapping {
public interface SingleAttributeIdentifierMapping extends EntityIdentifierMapping, PropertyBasedMapping,
AttributeMapping, AttributeMetadata {
/**
* Access to the identifier attribute's PropertyAccess
*/
PropertyAccess getPropertyAccess();
String getAttributeName();
@Override
default String getPartName() {
return ROLE_LOCAL_NAME;
}
@Override
default Generator getGenerator() {
return null;
}
@Override
default int getStateArrayPosition() {
return -1;
}
@Override
default AttributeMetadata getAttributeMetadata() {
return this;
}
@Override
default ManagedMappingType getDeclaringType() {
return findContainingEntityMapping();
}
@Override
default boolean isSelectable() {
return true;
}
@Override
default boolean isNullable() {
return false;
}
@Override
default boolean isInsertable() {
return true;
}
@Override
default boolean isUpdatable() {
return false;
}
@Override
default boolean isIncludedInDirtyChecking() {
return false;
}
@Override
default boolean isIncludedInOptimisticLocking() {
return true;
}
@Override
default MutabilityPlan getMutabilityPlan() {
return ImmutableMutabilityPlan.INSTANCE;
}
}

View File

@ -51,6 +51,7 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectablePath;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.VirtualModelPart;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.collection.AbstractCollectionPersister;
@ -106,7 +107,8 @@ import org.hibernate.type.Type;
*/
public class ToOneAttributeMapping
extends AbstractSingularAttributeMapping
implements EntityValuedFetchable, EntityAssociationMapping, TableGroupJoinProducer {
implements EntityValuedFetchable, EntityAssociationMapping, TableGroupJoinProducer,
LazyTableGroup.ParentTableGroupUseChecker {
public enum Cardinality {
ONE_TO_ONE,
@ -409,18 +411,18 @@ public class ToOneAttributeMapping
compositeType.getSubtypes()[0],
declaringEntityPersister.getFactory()
);
}
else {
this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME;
addPrefixedPropertyNames(
targetKeyPropertyNames,
null,
EntityIdentifierMapping.ROLE_LOCAL_NAME,
propertyType,
declaringEntityPersister.getFactory()
);
}
else {
this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME;
addPrefixedPropertyPaths(
targetKeyPropertyNames,
targetKeyPropertyName,
null,
propertyType,
declaringEntityPersister.getFactory()
);
@ -440,7 +442,7 @@ public class ToOneAttributeMapping
else if ( bootValue.isReferenceToPrimaryKey() ) {
this.targetKeyPropertyName = referencedPropertyName;
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
addPrefixedPropertyPaths(
addPrefixedPropertyNames(
targetKeyPropertyNames,
targetKeyPropertyName,
bootValue.getType(),
@ -463,30 +465,41 @@ public class ToOneAttributeMapping
compositeType.getSubtypes()[0],
declaringEntityPersister.getFactory()
);
addPrefixedPropertyNames(
targetKeyPropertyNames,
EntityIdentifierMapping.ROLE_LOCAL_NAME,
propertyType,
declaringEntityPersister.getFactory()
);
this.targetKeyPropertyNames = targetKeyPropertyNames;
}
else {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
this.targetKeyPropertyName = referencedPropertyName;
final String mapsIdAttributeName;
// If there is a "virtual property" for a non-PK join mapping, we try to see if the columns match the
// primary key columns and if so, we add the primary key property name as target key property
if ( ( mapsIdAttributeName = findMapsIdPropertyName( entityMappingType, referencedPropertyName ) ) != null ) {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
targetKeyPropertyNames.add( targetKeyPropertyName );
addPrefixedPropertyPaths(
targetKeyPropertyNames,
mapsIdAttributeName,
entityMappingType.getEntityPersister().getIdentifierType(),
declaringEntityPersister.getFactory()
);
this.targetKeyPropertyNames = targetKeyPropertyNames;
}
else {
this.targetKeyPropertyNames = Set.of(
targetKeyPropertyName,
ForeignKeyDescriptor.PART_NAME
);
}
addPrefixedPropertyNames(
targetKeyPropertyNames,
targetKeyPropertyName,
propertyType,
declaringEntityPersister.getFactory()
);
addPrefixedPropertyNames(
targetKeyPropertyNames,
ForeignKeyDescriptor.PART_NAME,
propertyType,
declaringEntityPersister.getFactory()
);
this.targetKeyPropertyNames = targetKeyPropertyNames;
}
}
}
@ -653,7 +666,7 @@ public class ToOneAttributeMapping
return null;
}
private static void addPrefixedPropertyPaths(
public static void addPrefixedPropertyPaths(
Set<String> targetKeyPropertyNames,
String prefix,
Type type,
@ -715,21 +728,40 @@ public class ToOneAttributeMapping
propertyName = entityType.getRHSUniqueKeyPropertyName();
}
final String newPrefix;
final String newPkPrefix;
final String newFkPrefix;
if ( prefix == null ) {
newPrefix = propertyName;
newPkPrefix = propertyName + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME;
newFkPrefix = ForeignKeyDescriptor.PART_NAME;
}
else if ( propertyName == null ) {
newPrefix = prefix;
newPkPrefix = prefix + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME;
newFkPrefix = prefix + "." + ForeignKeyDescriptor.PART_NAME;
}
else {
newPrefix = prefix + "." + propertyName;
newPkPrefix = prefix + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME;
newFkPrefix = prefix + "." + ForeignKeyDescriptor.PART_NAME;
}
addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, identifierOrUniqueKeyType, factory );
addPrefixedPropertyNames( targetKeyPropertyNames, newPkPrefix, identifierOrUniqueKeyType, factory );
addPrefixedPropertyNames( targetKeyPropertyNames, newFkPrefix, identifierOrUniqueKeyType, factory );
if ( identifierOrUniqueKeyType instanceof EmbeddedComponentType ) {
final String newEmbeddedPkPrefix;
final String newEmbeddedFkPrefix;
if ( prefix == null ) {
newEmbeddedPkPrefix = EntityIdentifierMapping.ROLE_LOCAL_NAME;
newEmbeddedFkPrefix = ForeignKeyDescriptor.PART_NAME;
}
else {
newEmbeddedPkPrefix = prefix + "." + EntityIdentifierMapping.ROLE_LOCAL_NAME;
newEmbeddedFkPrefix = prefix + "." + ForeignKeyDescriptor.PART_NAME;
}
addPrefixedPropertyNames( targetKeyPropertyNames, newEmbeddedPkPrefix, identifierOrUniqueKeyType, factory );
addPrefixedPropertyNames( targetKeyPropertyNames, newEmbeddedFkPrefix, identifierOrUniqueKeyType, factory );
}
}
}
@ -843,7 +875,11 @@ public class ToOneAttributeMapping
else {
fkPart = foreignKeyDescriptor.getTargetPart();
}
if ( fkPart instanceof EmbeddableValuedModelPart && fkPart instanceof VirtualModelPart ) {
if ( fkPart instanceof EmbeddableValuedModelPart && fkPart instanceof VirtualModelPart
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( name )
&& !ForeignKeyDescriptor.PART_NAME.equals( name )
&& !ForeignKeyDescriptor.TARGET_PART_NAME.equals( name )
&& !fkPart.getPartName().equals( name ) ) {
return ( (ModelPartContainer) fkPart ).findSubPart( name, targetType );
}
return fkPart;
@ -940,13 +976,22 @@ public class ToOneAttributeMapping
assert !creationState.isResolvingCircularFetch();
try {
creationState.setResolvingCircularFetch( true );
foreignKeyDomainResult = foreignKeyDescriptor.createDomainResult(
fetchablePath,
parentTableGroup,
sideNature,
fetchParent,
creationState
);
if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) {
foreignKeyDomainResult = foreignKeyDescriptor.createKeyDomainResult(
fetchablePath,
createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ),
fetchParent,
creationState
);
}
else {
foreignKeyDomainResult = foreignKeyDescriptor.createTargetDomainResult(
fetchablePath,
parentTableGroup,
fetchParent,
creationState
);
}
}
finally {
creationState.setResolvingCircularFetch( false );
@ -1155,9 +1200,14 @@ public class ToOneAttributeMapping
if ( sideNature == ForeignKeyDescriptor.Nature.KEY && !isKeyTableNullable ) {
keyDomainResult = foreignKeyDescriptor.createKeyDomainResult(
fetchablePath,
creationState.getSqlAstCreationState()
.getFromClauseAccess()
.findTableGroup( realFetchParent.getNavigablePath() ),
createTableGroupForDelayedFetch(
fetchablePath,
creationState.getSqlAstCreationState()
.getFromClauseAccess()
.findTableGroup( realFetchParent.getNavigablePath() ),
null,
creationState
),
fetchParent,
creationState
);
@ -1207,14 +1257,24 @@ public class ToOneAttributeMapping
else {
realParent = parentNavigablePath;
}
final TableGroup tableGroup = fromClauseAccess.getTableGroup( realParent );
final DomainResult<?> domainResult = foreignKeyDescriptor.createDomainResult(
fetchablePath,
tableGroup,
sideNature,
fetchParent,
creationState
);
final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( realParent );
final DomainResult<?> domainResult;
if ( sideNature == ForeignKeyDescriptor.Nature.KEY ) {
domainResult = foreignKeyDescriptor.createKeyDomainResult(
fetchablePath,
createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState ),
fetchParent,
creationState
);
}
else {
domainResult = foreignKeyDescriptor.createTargetDomainResult(
fetchablePath,
parentTableGroup,
fetchParent,
creationState
);
}
if ( fetchTiming == FetchTiming.IMMEDIATE ) {
return buildEntityFetchSelect(
fetchParent,
@ -1421,7 +1481,7 @@ public class ToOneAttributeMapping
if ( notFoundAction != null || !isInternalLoadNullable ) {
keyResult = foreignKeyDescriptor.createKeyDomainResult(
fetchablePath,
parentTableGroup,
tableGroup,
fetchParent,
creationState
);
@ -1484,13 +1544,29 @@ public class ToOneAttributeMapping
else {
side = this.sideNature;
}
final DomainResult<?> keyResult = foreignKeyDescriptor.createDomainResult(
fetchablePath,
parentTableGroup,
side,
fetchParent,
creationState
);
final DomainResult<?> keyResult;
if ( side == ForeignKeyDescriptor.Nature.KEY ) {
final TableGroup tableGroup = sideNature == ForeignKeyDescriptor.Nature.KEY
? createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState )
: parentTableGroup;
keyResult = foreignKeyDescriptor.createKeyDomainResult(
fetchablePath,
tableGroup,
fetchParent,
creationState
);
}
else {
final TableGroup tableGroup = sideNature == ForeignKeyDescriptor.Nature.TARGET
? parentTableGroup
: createTableGroupForDelayedFetch( fetchablePath, parentTableGroup, null, creationState );
keyResult = foreignKeyDescriptor.createTargetDomainResult(
fetchablePath,
tableGroup,
fetchParent,
creationState
);
}
final boolean selectByUniqueKey = isSelectByUniqueKey( side );
// Consider all associations annotated with @NotFound as EAGER
@ -1577,6 +1653,38 @@ public class ToOneAttributeMapping
);
}
private TableGroup createTableGroupForDelayedFetch(
NavigablePath fetchablePath,
TableGroup parentTableGroup,
String resultVariable,
DomainResultCreationState creationState) {
// Check if we can reuse a table group join of the parent
final TableGroup compatibleTableGroup = parentTableGroup.findCompatibleJoinedGroup(
this,
SqlAstJoinType.LEFT
);
if ( compatibleTableGroup != null ) {
return compatibleTableGroup;
}
// We have to create the table group that points to the target so that table reference resolving works
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
fetchablePath,
parentTableGroup,
resultVariable,
null,
SqlAstJoinType.LEFT,
false,
false,
creationState.getSqlAstCreationState()
);
parentTableGroup.addTableGroupJoin( tableGroupJoin );
creationState.getSqlAstCreationState().getFromClauseAccess().registerTableGroup(
fetchablePath,
tableGroupJoin.getJoinedGroup()
);
return tableGroupJoin.getJoinedGroup();
}
private boolean isSelectByUniqueKey(ForeignKeyDescriptor.Nature side) {
if ( side == ForeignKeyDescriptor.Nature.KEY ) {
// case 1.2
@ -1598,7 +1706,7 @@ public class ToOneAttributeMapping
@Override
public <T> DomainResult<T> createSnapshotDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
TableGroup parentTableGroup,
String resultVariable,
DomainResultCreationState creationState) {
// We need a join if either
@ -1615,31 +1723,36 @@ public class ToOneAttributeMapping
np -> {
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
navigablePath,
tableGroup,
parentTableGroup,
null,
null,
getDefaultSqlAstJoinType( tableGroup ),
getDefaultSqlAstJoinType( parentTableGroup ),
true,
false,
creationState.getSqlAstCreationState()
);
tableGroup.addTableGroupJoin( tableGroupJoin );
parentTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup();
}
);
}
else {
tableGroupToUse = tableGroup;
tableGroupToUse = createTableGroupForDelayedFetch(
navigablePath,
parentTableGroup,
resultVariable,
creationState
);
}
if ( hasNotFoundAction() ) {
assert tableGroupToUse != tableGroup;
assert tableGroupToUse != parentTableGroup;
//noinspection unchecked
return new NotFoundSnapshotResult(
navigablePath,
this,
parentTableGroup,
tableGroupToUse,
tableGroup,
creationState
);
}
@ -1727,6 +1840,11 @@ public class ToOneAttributeMapping
return predicate == null || foreignKeyDescriptor.isSimpleJoinPredicate( predicate );
}
@Override
public boolean containsTableReference(String tableExpression) {
return getEntityMappingType().containsTableReference( tableExpression );
}
@Override
public int getNumberOfFetchables() {
return getEntityMappingType().getNumberOfFetchables();
@ -1767,8 +1885,18 @@ public class ToOneAttributeMapping
embeddablePathSb = new StringBuilder();
}
embeddablePathSb.insert( 0, parentContainer.getPartName() + "." );
parentTableGroup = fromClauseAccess.findTableGroup( parentTableGroup.getNavigablePath().getParent() );
parentContainer = parentTableGroup.getModelPart();
final NavigablePath parentNavigablePath = parentTableGroup.getNavigablePath();
final TableGroup tableGroup = fromClauseAccess.findTableGroup( parentNavigablePath.getParent() );
if ( tableGroup == null ) {
assert parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.PART_NAME )
|| parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.TARGET_PART_NAME );
// Might happen that we don't register a table group for the collection role if this is a
// foreign key part and the collection is delayed. We can just break out in this case though,
// since these checks here are only for reusing a map key property, which we won't have
break;
}
parentTableGroup = tableGroup;
parentContainer = tableGroup.getModelPart();
}
else {
break;
@ -1803,28 +1931,7 @@ public class ToOneAttributeMapping
indexTableGroup,
fetched,
pluralTableGroup,
(np, tableExpression) -> {
if ( !canUseParentTableGroup ) {
return false;
}
if ( !identifyingColumnsTableExpression.equals( tableExpression ) ) {
return false;
}
if ( navigablePath.equals( np.getParent() ) ) {
return targetKeyPropertyNames.contains( np.getLocalName() );
}
final String relativePath = np.relativize( navigablePath );
if ( relativePath == null ) {
return false;
}
// Empty relative path means the navigable paths are equal,
// in which case we allow resolving the parent table group
return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath );
}
this
),
null
);
@ -1850,8 +1957,11 @@ public class ToOneAttributeMapping
lazyTableGroup,
null
);
final TableReference lhsTableReference = lhs.resolveTableReference( navigablePath, identifyingColumnsTableExpression );
final TableReference lhsTableReference = lhs.resolveTableReference(
navigablePath,
this,
identifyingColumnsTableExpression
);
lazyTableGroup.setTableGroupInitializerCallback(
tableGroup -> {
@ -1932,7 +2042,19 @@ public class ToOneAttributeMapping
TableGroup realParentTableGroup = lhs;
final FromClauseAccess fromClauseAccess = creationState.getFromClauseAccess();
while ( realParentTableGroup.getModelPart() instanceof EmbeddableValuedModelPart ) {
realParentTableGroup = fromClauseAccess.findTableGroup( realParentTableGroup.getNavigablePath().getParent() );
final NavigablePath parentNavigablePath = realParentTableGroup.getNavigablePath();
final TableGroup tableGroup = fromClauseAccess.findTableGroup( parentNavigablePath.getParent() );
if ( tableGroup == null ) {
assert parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.PART_NAME )
|| parentNavigablePath.getLocalName().equals( ForeignKeyDescriptor.TARGET_PART_NAME );
// Might happen that we don't register a table group for the collection role if this is a
// foreign key part and the collection is delayed. We can just break out in this case though,
// since the realParentTableGroup is only relevant if this association is actually joined,
// which it is not, because this is part of the target FK
realParentTableGroup = null;
break;
}
realParentTableGroup = tableGroup;
}
final TableGroupProducer tableGroupProducer;
@ -1958,28 +2080,7 @@ public class ToOneAttributeMapping
sqlAliasBase,
creationState
),
(np, tableExpression) -> {
if ( !canUseParentTableGroup || tableGroupProducer != ToOneAttributeMapping.this ) {
return false;
}
if ( !identifyingColumnsTableExpression.equals( tableExpression ) ) {
return false;
}
if ( navigablePath.pathsMatch( np.getParent() ) ) {
return targetKeyPropertyNames.contains( np.getLocalName() );
}
final String relativePath = np.relativize( navigablePath );
if ( relativePath == null ) {
return false;
}
// Empty relative path means the navigable paths are equal,
// in which case we allow resolving the parent table group
return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath );
},
this,
tableGroupProducer,
explicitSourceAlias,
sqlAliasBase,
@ -2015,6 +2116,16 @@ public class ToOneAttributeMapping
return lazyTableGroup;
}
@Override
public boolean canUseParentTableGroup(
TableGroupProducer producer,
NavigablePath navigablePath,
ValuedModelPart valuedModelPart) {
return producer == this
&& sideNature == ForeignKeyDescriptor.Nature.KEY
&& foreignKeyDescriptor.isKeyPart( valuedModelPart );
}
private void initializeIfNeeded(TableGroup lhs, SqlAstJoinType sqlAstJoinType, TableGroup tableGroup) {
if ( sqlAstJoinType == SqlAstJoinType.INNER && ( isNullable || !lhs.canUseInnerJoins() ) ) {
// Force initialization of the underlying table group join to retain cardinality

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -40,6 +40,7 @@ import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.TableDetails;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.OneToManyCollectionPart;
import org.hibernate.metamodel.mapping.internal.SingleAttributeIdentifierMapping;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
@ -59,6 +60,7 @@ import org.hibernate.sql.ast.tree.from.StandardTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -74,7 +76,8 @@ import static org.hibernate.internal.util.collections.CollectionHelper.arrayList
*/
@Incubating
public class AnonymousTupleEntityValuedModelPart
implements EntityValuedModelPart, EntityMappingType, TableGroupJoinProducer {
implements EntityValuedModelPart, EntityMappingType, TableGroupJoinProducer, ValuedModelPart,
LazyTableGroup.ParentTableGroupUseChecker {
private final EntityIdentifierMapping identifierMapping;
private final DomainType<?> domainType;
@ -115,7 +118,7 @@ public class AnonymousTupleEntityValuedModelPart
@Override
public ModelPart findSubPart(String name, EntityMappingType treatTargetType) {
if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) {
if ( ( (SingleAttributeIdentifierMapping) identifierMapping ).getAttributeName().equals( name ) ) {
if ( identifierMapping.getAttributeName().equals( name ) ) {
return identifierMapping;
}
}
@ -141,6 +144,11 @@ public class AnonymousTupleEntityValuedModelPart
return this;
}
@Override
public MappingType getMappedType() {
return getPartMappingType();
}
@Override
public JavaType<?> getJavaType() {
return domainType.getExpressibleJavaType();
@ -151,6 +159,11 @@ public class AnonymousTupleEntityValuedModelPart
return componentName;
}
@Override
public String getContainingTableExpression() {
return "";
}
@Override
public int getJdbcTypeCount() {
return delegate.getJdbcTypeCount();
@ -227,6 +240,11 @@ public class AnonymousTupleEntityValuedModelPart
return identifierMapping.forEachSelectable( offset, consumer );
}
@Override
public SelectableMapping getSelectable(int columnIndex) {
return identifierMapping.getSelectable( columnIndex );
}
@Override
public JavaType<?> getMappedJavaType() {
return delegate.getJavaType();
@ -375,8 +393,7 @@ public class AnonymousTupleEntityValuedModelPart
final SelectableMapping targetMapping = targetMappings.get( i );
final TableReference targetTableReference = tg.resolveTableReference(
null,
targetMapping.getContainingTableExpression(),
false
targetMapping.getContainingTableExpression()
);
predicateConsumer.accept(
new ComparisonPredicate(
@ -444,7 +461,6 @@ public class AnonymousTupleEntityValuedModelPart
creationState.getSqlAliasBaseGenerator()
);
final boolean canUseInnerJoin = sqlAstJoinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins();
final EntityPersister entityPersister = delegate.getEntityMappingType().getEntityPersister();
final LazyTableGroup lazyTableGroup = new LazyTableGroup(
canUseInnerJoin,
navigablePath,
@ -457,23 +473,7 @@ public class AnonymousTupleEntityValuedModelPart
sqlAliasBase,
creationState
),
(np, tableExpression) -> {
if ( !tableExpression.isEmpty() && !entityPersister.containsTableReference( tableExpression ) ) {
return false;
}
if ( navigablePath.equals( np.getParent() ) ) {
return targetKeyPropertyNames.contains( np.getLocalName() );
}
final String relativePath = np.relativize( navigablePath );
if ( relativePath == null ) {
return false;
}
// Empty relative path means the navigable paths are equal,
// in which case we allow resolving the parent table group
return relativePath.isEmpty() || targetKeyPropertyNames.contains( relativePath );
},
this,
this,
explicitSourceAlias,
sqlAliasBase,
@ -490,6 +490,22 @@ public class AnonymousTupleEntityValuedModelPart
return lazyTableGroup;
}
@Override
public boolean canUseParentTableGroup(TableGroupProducer producer, NavigablePath navigablePath, ValuedModelPart valuedModelPart) {
final ModelPart foreignKeyPart = getForeignKeyPart();
if ( foreignKeyPart instanceof AnonymousTupleNonAggregatedEntityIdentifierMapping ) {
final AnonymousTupleNonAggregatedEntityIdentifierMapping identifierMapping = (AnonymousTupleNonAggregatedEntityIdentifierMapping) foreignKeyPart;
final int numberOfFetchables = identifierMapping.getNumberOfFetchables();
for ( int i = 0; i< numberOfFetchables; i++ ) {
if ( valuedModelPart == identifierMapping.getFetchable( i ) ) {
return true;
}
}
return false;
}
return foreignKeyPart == valuedModelPart;
}
@Override
public String getSqlAliasStem() {
return getPartName();
@ -707,4 +723,9 @@ public class AnonymousTupleEntityValuedModelPart
? ( (TableGroupJoinProducer) delegate ).isSimpleJoinPredicate( predicate )
: false;
}
@Override
public boolean containsTableReference(String tableExpression) {
return ( (TableGroupProducer) delegate ).containsTableReference( tableExpression );
}
}

View File

@ -6,7 +6,9 @@
*/
package org.hibernate.query.derived;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.Incubating;
import org.hibernate.engine.FetchStyle;
@ -19,6 +21,10 @@ import org.hibernate.metamodel.mapping.NonAggregatedIdentifierMapping;
import org.hibernate.metamodel.mapping.internal.IdClassEmbeddable;
import org.hibernate.metamodel.mapping.internal.VirtualIdEmbeddable;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.sql.ast.spi.SqlSelection;
import jakarta.persistence.metamodel.Attribute;
/**
* @author Christian Beikov
@ -30,12 +36,22 @@ public class AnonymousTupleNonAggregatedEntityIdentifierMapping extends Anonymou
private final NonAggregatedIdentifierMapping delegate;
public AnonymousTupleNonAggregatedEntityIdentifierMapping(
Map<String, ModelPart> modelParts,
SqmExpressible<?> sqmExpressible,
List<SqlSelection> sqlSelections,
int selectionIndex,
String selectionExpression,
Set<String> compatibleTableExpressions,
Set<Attribute<?, ?>> attributes,
DomainType<?> domainType,
String componentName,
NonAggregatedIdentifierMapping delegate) {
super(
modelParts,
sqmExpressible,
sqlSelections,
selectionIndex,
selectionExpression,
compatibleTableExpressions,
attributes,
domainType,
componentName,
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.ManagedDomainType;
import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
import org.hibernate.query.sqm.SqmExpressible;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.select.SqmSelectableNode;
@ -92,6 +91,7 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
final SqmPath<?> sqmPath = (SqmPath<?>) selectableNode;
final TableGroup tableGroup = fromClauseAccess.findTableGroup( sqmPath.getNavigablePath() );
modelPart = createModelPart(
this,
selectableNode.getExpressible(),
sqmPath.getNodeType().getSqmPathType(),
sqlSelections,
@ -105,6 +105,7 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
}
else {
modelPart = new AnonymousTupleBasicValuedModelPart(
this,
partName,
partName,
selectableNode.getExpressible(),
@ -129,7 +130,8 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
return tableGroup.getModelPart();
}
private ModelPart createModelPart(
public static ModelPart createModelPart(
MappingType mappingType,
SqmExpressible<?> sqmExpressible,
DomainType<?> domainType,
List<SqlSelection> sqlSelections,
@ -145,41 +147,24 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
.getIdentifierMapping();
final EntityIdentifierMapping newIdentifierMapping;
if ( identifierMapping instanceof SingleAttributeIdentifierMapping ) {
final String attributeName = identifierMapping.getAttributeName();
if ( identifierMapping.getPartMappingType() instanceof ManagedMappingType ) {
//noinspection unchecked
final Set<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,
selectionExpression + "_" + attributeName + "_" + attribute.getName(),
attribute.getName(),
modelPartContainer.findSubPart( attribute.getName(), null ),
compatibleTableExpressions,
index++
);
modelParts.put( modelPart.getPartName(), modelPart );
}
newIdentifierMapping = new AnonymousTupleEmbeddedEntityIdentifierMapping(
modelParts,
sqmExpressible,
sqlSelections,
selectionIndex,
selectionExpression + "_" + identifierMapping.getAttributeName(),
compatibleTableExpressions,
attributes,
domainType,
attributeName,
(CompositeIdentifierMapping) identifierMapping
);
}
else {
newIdentifierMapping = new AnonymousTupleBasicEntityIdentifierMapping(
selectionExpression + "_" + attributeName,
mappingType,
selectionExpression + "_" + identifierMapping.getAttributeName(),
sqmExpressible,
sqlSelections.get( selectionIndex ).getExpressionType().getSingleJdbcMapping(),
(BasicEntityIdentifierMapping) identifierMapping
@ -189,29 +174,13 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
else {
//noinspection unchecked
final Set<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(
modelParts,
sqmExpressible,
sqlSelections,
selectionIndex,
selectionExpression,
compatibleTableExpressions,
attributes,
domainType,
selectionExpression,
(NonAggregatedIdentifierMapping) identifierMapping
@ -232,31 +201,22 @@ public class AnonymousTupleTableGroupProducer implements TableGroupProducer, Map
else if ( domainType instanceof ManagedDomainType<?> ) {
//noinspection unchecked
final Set<Attribute<?, ?>> attributes = (Set<Attribute<?, ?>>) ( (ManagedDomainType<?>) domainType ).getAttributes();
final Map<String, ModelPart> modelParts = CollectionHelper.linkedMapOfSize( attributes.size() );
final EmbeddableValuedModelPart modelPartContainer = (EmbeddableValuedModelPart) existingModelPart;
int index = 0;
for ( Attribute<?, ?> attribute : attributes ) {
if ( !( attribute instanceof SingularPersistentAttribute<?, ?> ) ) {
throw new IllegalArgumentException( "Only embeddables without collections are supported" );
}
final DomainType<?> attributeType = ( (SingularPersistentAttribute<?, ?>) attribute ).getType();
final ModelPart modelPart = createModelPart(
sqmExpressible,
attributeType,
sqlSelections,
selectionIndex + index,
selectionExpression + "_" + attribute.getName(),
attribute.getName(),
modelPartContainer.findSubPart( attribute.getName(), null ),
compatibleTableExpressions,
index++
);
modelParts.put( modelPart.getPartName(), modelPart );
}
return new AnonymousTupleEmbeddableValuedModelPart( modelParts, domainType, selectionExpression, modelPartContainer, fetchableIndex );
return new AnonymousTupleEmbeddableValuedModelPart(
sqmExpressible,
sqlSelections,
selectionIndex,
selectionExpression,
compatibleTableExpressions,
attributes,
domainType,
selectionExpression,
(EmbeddableValuedModelPart) existingModelPart,
fetchableIndex
);
}
else {
return new AnonymousTupleBasicValuedModelPart(
mappingType,
partName,
selectionExpression,
sqmExpressible,

View File

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

View File

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

View File

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

View File

@ -82,7 +82,7 @@ public class CompleteFetchBuilderBasicPart implements CompleteFetchBuilder, Basi
final String mappedTable = referencedModelPart.getContainingTableExpression();
final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( parent.getNavigablePath() );
final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, mappedTable );
final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, referencedModelPart, mappedTable );
final String selectedAlias;
final int jdbcPosition;

View File

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

View File

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

View File

@ -75,7 +75,7 @@ public class CompleteResultBuilderBasicModelPart
final DomainResultCreationStateImpl creationStateImpl = impl( domainResultCreationState );
final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() );
final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, modelPart.getContainingTableExpression() );
final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, modelPart, modelPart.getContainingTableExpression() );
final SqlSelection sqlSelection = creationStateImpl.resolveSqlSelection(
ResultsHelper.resolveSqlExpression(

View File

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

View File

@ -12,8 +12,11 @@ import java.util.function.BiFunction;
import org.hibernate.engine.FetchTiming;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping;
import org.hibernate.query.NativeQuery;
import org.hibernate.spi.NavigablePath;
@ -80,9 +83,108 @@ public class DynamicFetchBuilderStandard
final Fetchable attributeMapping = (Fetchable) parent.getReferencedMappingContainer().findSubPart( fetchableName, null );
final SqlExpressionResolver sqlExpressionResolver = domainResultCreationState.getSqlAstCreationState().getSqlExpressionResolver();
final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> {
if ( attributeMapping instanceof BasicValuedModelPart ) {
attributeMapping.forEachSelectable(
getSelectableConsumer(
fetchPath,
jdbcResultsMetadata,
domainResultCreationState,
creationStateImpl,
ownerTableGroup,
sqlExpressionResolver,
(BasicValuedModelPart) attributeMapping
)
);
return parent.generateFetchableFetch(
attributeMapping,
fetchPath,
FetchTiming.IMMEDIATE,
true,
null,
creationStateImpl
);
}
else if ( attributeMapping instanceof EmbeddableValuedFetchable ) {
attributeMapping.forEachSelectable(
getSelectableConsumer(
fetchPath,
jdbcResultsMetadata,
domainResultCreationState,
creationStateImpl,
ownerTableGroup,
sqlExpressionResolver,
(EmbeddableValuedFetchable) attributeMapping
)
);
return parent.generateFetchableFetch(
attributeMapping,
fetchPath,
FetchTiming.IMMEDIATE,
false,
null,
creationStateImpl
);
}
else if ( attributeMapping instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
toOneAttributeMapping.getForeignKeyDescriptor().getPart( toOneAttributeMapping.getSideNature() )
.forEachSelectable(
getSelectableConsumer(
fetchPath,
jdbcResultsMetadata,
domainResultCreationState,
creationStateImpl,
ownerTableGroup,
sqlExpressionResolver,
toOneAttributeMapping.getForeignKeyDescriptor()
)
);
return parent.generateFetchableFetch(
attributeMapping,
fetchPath,
FetchTiming.DELAYED,
false,
null,
creationStateImpl
);
}
else {
assert attributeMapping instanceof PluralAttributeMapping;
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) attributeMapping;
pluralAttributeMapping.getKeyDescriptor().visitTargetSelectables(
getSelectableConsumer(
fetchPath,
jdbcResultsMetadata,
domainResultCreationState,
creationStateImpl,
ownerTableGroup,
sqlExpressionResolver,
pluralAttributeMapping.getKeyDescriptor()
)
);
return parent.generateFetchableFetch(
attributeMapping,
fetchPath,
FetchTiming.DELAYED,
false,
null,
creationStateImpl
);
}
}
private SelectableConsumer getSelectableConsumer(
NavigablePath fetchPath,
JdbcValuesMetadata jdbcResultsMetadata,
DomainResultCreationState domainResultCreationState,
DomainResultCreationStateImpl creationStateImpl,
TableGroup ownerTableGroup,
SqlExpressionResolver sqlExpressionResolver,
ValuedModelPart valuedModelPart) {
return (selectionIndex, selectableMapping) -> {
final TableReference tableReference = ownerTableGroup.resolveTableReference(
fetchPath,
valuedModelPart,
selectableMapping.getContainingTableExpression()
);
final String columnAlias = columnNames.get( selectionIndex );
@ -102,54 +204,6 @@ public class DynamicFetchBuilderStandard
.getTypeConfiguration()
);
};
if ( attributeMapping instanceof BasicValuedMapping ) {
attributeMapping.forEachSelectable( selectableConsumer );
return parent.generateFetchableFetch(
attributeMapping,
fetchPath,
FetchTiming.IMMEDIATE,
true,
null,
creationStateImpl
);
}
else if ( attributeMapping instanceof EmbeddableValuedFetchable ) {
attributeMapping.forEachSelectable( selectableConsumer );
return parent.generateFetchableFetch(
attributeMapping,
fetchPath,
FetchTiming.IMMEDIATE,
false,
null,
creationStateImpl
);
}
else if ( attributeMapping instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) attributeMapping;
toOneAttributeMapping.getForeignKeyDescriptor().getPart( toOneAttributeMapping.getSideNature() )
.forEachSelectable( selectableConsumer );
return parent.generateFetchableFetch(
attributeMapping,
fetchPath,
FetchTiming.DELAYED,
false,
null,
creationStateImpl
);
}
else {
assert attributeMapping instanceof PluralAttributeMapping;
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) attributeMapping;
pluralAttributeMapping.getKeyDescriptor().visitTargetSelectables( selectableConsumer );
return parent.generateFetchableFetch(
attributeMapping,
fetchPath,
FetchTiming.DELAYED,
false,
null,
creationStateImpl
);
}
}
@Override

View File

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

View File

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

View File

@ -758,7 +758,6 @@ public class CteInsertHandler implements InsertHandler {
final TableReference rootTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(),
rootTableName,
true,
true
);
@ -777,7 +776,6 @@ public class CteInsertHandler implements InsertHandler {
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(),
tableExpression,
true,
true
);
final List<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(
updatingTableGroup.getNavigablePath(),
tableExpression,
true,
true
);
final List<Assignment> assignmentList = assignmentsByTable.get( updatingTableReference );
@ -272,7 +271,6 @@ public class CteUpdateHandler extends AbstractCteMutationHandler implements Upda
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(),
tableExpression,
true,
true
);
final List<Assignment> assignmentList = assignmentsByTable.get( updatingTableReference );

View File

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

View File

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

View File

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

View File

@ -243,7 +243,6 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio
final TableReference updatingTableReference = updatingTableGroup.getTableReference(
updatingTableGroup.getNavigablePath(),
tableExpression,
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.SqlTypedMapping;
import org.hibernate.metamodel.mapping.ValueMapping;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
import org.hibernate.metamodel.mapping.internal.ManyToManyCollectionPart;
import org.hibernate.metamodel.mapping.internal.OneToManyCollectionPart;
@ -182,6 +183,7 @@ import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedRoot;
import org.hibernate.query.sqm.tree.expression.Conversion;
@ -2901,7 +2903,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
final int subclassTableSpan = persister.getSubclassTableSpan();
for ( int i = 0; i < subclassTableSpan; i++ ) {
tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ), false );
tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ) );
}
}
@ -2927,7 +2929,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
final int subclassTableSpan = persister.getSubclassTableSpan();
for ( int i = 0; i < subclassTableSpan; i++ ) {
tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ), false );
tableGroup.resolveTableReference( null, persister.getSubclassTableName( i ) );
}
}
@ -3468,8 +3470,16 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
}
}
else if ( path instanceof SqmFrom<?, ?> ) {
registerTreatUsage( (SqmFrom<?, ?>) path, tableGroup );
else {
if ( path instanceof SqmFrom<?, ?> ) {
registerTreatUsage( (SqmFrom<?, ?>) path, tableGroup );
}
if ( path instanceof SqmSimplePath<?> && CollectionPart.Nature.fromName( path.getNavigablePath().getLocalName() ) == null ) {
// If a table group for a selection already exists, we must make sure that the join type is INNER
fromClauseIndex.findTableGroup( path.getNavigablePath().getParent() )
.findTableGroupJoin( tableGroup )
.setJoinType( SqlAstJoinType.INNER );
}
}
}
@ -3507,8 +3517,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
else {
// Check if we can reuse a table group join of the parent
final TableGroup compatibleTableGroup = findCompatibleJoinedGroup(
actualParentTableGroup,
final TableGroup compatibleTableGroup = actualParentTableGroup.findCompatibleJoinedGroup(
joinProducer,
SqlAstJoinType.INNER
);
@ -3898,7 +3907,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
navigablePath,
tableGroupToUse == null ? tableGroup : tableGroupToUse,
expandToAllColumns ? null : resultModelPart,
true,
interpretationModelPart,
treatedMapping,
this
@ -3908,7 +3916,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final EmbeddableValuedModelPart mapping = (EmbeddableValuedModelPart) actualModelPart;
result = new EmbeddableValuedPathInterpretation<>(
mapping.toSqlExpression(
tableGroup,
getFromClauseAccess().findTableGroup( path.getLhs().getNavigablePath() ),
currentClauseStack.getCurrent(),
this,
getSqlAstCreationState()
@ -3923,6 +3931,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final BasicValuedModelPart mapping = (BasicValuedModelPart) actualModelPart;
final TableReference tableReference = tableGroup.resolveTableReference(
navigablePath.append( actualModelPart.getPartName() ),
mapping,
mapping.getContainingTableExpression()
);
@ -4439,6 +4448,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
new ColumnReference(
tableGroup.resolveTableReference(
navigablePath,
(ValuedModelPart) modelPart,
selectionMapping.getContainingTableExpression()
),
selectionMapping
@ -4564,6 +4574,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final ColumnReference columnReference = new ColumnReference(
tableGroup.resolveTableReference(
navigablePath,
(ValuedModelPart) modelPart,
selectionMapping.getContainingTableExpression()
),
selectionMapping
@ -7139,11 +7150,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
@Override
public Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) {
final EntityIdentifierMapping identifierMapping = fetchParent.getEntityValuedModelPart()
.getEntityMappingType()
final EntityIdentifierMapping identifierMapping = fetchParent.getReferencedMappingContainer()
.getIdentifierMapping();
final Fetchable fetchableIdentifierMapping = (Fetchable) identifierMapping;
return createFetch( fetchParent, fetchableIdentifierMapping, false );
return createFetch( fetchParent, (Fetchable) identifierMapping, false );
}
private Fetch createFetch(FetchParent fetchParent, Fetchable fetchable, Boolean isKeyFetchable) {

View File

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

View File

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

View File

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

View File

@ -7,7 +7,6 @@
package org.hibernate.sql.ast.tree.from;
import java.util.List;
import java.util.Locale;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.spi.NavigablePath;
@ -25,53 +24,14 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// TableReference handling
@Override
public TableReference resolveTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization) {
assert tableExpression != null;
final TableReference tableReference = getTableReferenceInternal(
navigablePath,
tableExpression,
allowFkOptimization,
true
);
if ( tableReference == null ) {
throw new UnknownTableReferenceException(
tableExpression,
String.format(
Locale.ROOT,
"Unable to determine TableReference (`%s`) for `%s`",
tableExpression,
navigablePath
)
);
}
return tableReference;
}
@Override
public TableReference getTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization,
boolean resolve) {
return getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve );
}
protected TableReference getTableReferenceInternal(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization,
boolean resolve) {
final TableReference primaryTableReference = getPrimaryTableReference().getTableReference(
navigablePath,
tableExpression,
allowFkOptimization,
resolve
);
if ( primaryTableReference != null) {
@ -82,7 +42,6 @@ public abstract class AbstractColumnReferenceQualifier implements ColumnReferenc
final TableReference tableReference = tableJoin.getJoinedTableReference().getTableReference(
navigablePath,
tableExpression,
allowFkOptimization,
resolve
);
if ( tableReference != null) {

View File

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

View File

@ -6,41 +6,89 @@
*/
package org.hibernate.sql.ast.tree.from;
import java.util.Locale;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath;
/**
* @author Steve Ebersole
*/
public interface ColumnReferenceQualifier {
default TableReference resolveTableReference(NavigablePath navigablePath, String tableExpression) {
return resolveTableReference( navigablePath, tableExpression, true );
}
default TableReference resolveTableReference(String tableExpression) {
return resolveTableReference( null, tableExpression, true );
return resolveTableReference( null, tableExpression );
}
/**
* Like {@link #getTableReference(NavigablePath, String, boolean, boolean)}, but will throw an exception if no
* Like {@link #getTableReference(NavigablePath, String, boolean)}, but will throw an exception if no
* table reference can be found, even after resolving possible table reference joins.
*
* @param navigablePath The path for which to look up the table reference, may be null
* @param tableExpression The table expression for which to look up the table reference
* @param allowFkOptimization Whether a foreign key optimization is allowed i.e. use the FK column on the key-side
*
* @throws UnknownTableReferenceException to indicate that the given tableExpression could not be resolved
*/
TableReference resolveTableReference(
default TableReference resolveTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization);
String tableExpression) {
assert tableExpression != null;
final TableReference tableReference = getTableReference(
navigablePath,
tableExpression,
true
);
if ( tableReference == null ) {
throw new UnknownTableReferenceException(
tableExpression,
String.format(
Locale.ROOT,
"Unable to determine TableReference (`%s`) for `%s`",
tableExpression,
navigablePath
)
);
}
return tableReference;
}
default TableReference resolveTableReference(
NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression) {
assert modelPart != null;
final TableReference tableReference = getTableReference(
navigablePath,
modelPart,
tableExpression,
true
);
if ( tableReference == null ) {
throw new UnknownTableReferenceException(
tableExpression,
String.format(
Locale.ROOT,
"Unable to determine TableReference (`%s`) for `%s`",
tableExpression,
navigablePath
)
);
}
return tableReference;
}
default TableReference getTableReference(NavigablePath navigablePath, String tableExpression) {
return getTableReference( navigablePath, tableExpression, true, false );
return getTableReference( navigablePath, tableExpression, false );
}
default TableReference getTableReference(String tableExpression) {
return getTableReference( null, tableExpression, true, false );
return getTableReference( null, tableExpression, false );
}
/**
@ -48,12 +96,17 @@ public interface ColumnReferenceQualifier {
*
* @param navigablePath The path for which to look up the table reference, may be null
* @param tableExpression The table expression for which to look up the table reference
* @param allowFkOptimization Whether a foreign key optimization is allowed i.e. use the FK column on the key-side
* @param resolve Whether to potentially create table reference joins for this table group
*/
TableReference getTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization,
boolean resolve);
default TableReference getTableReference(
NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression,
boolean resolve) {
return getTableReference( navigablePath, tableExpression, resolve );
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,10 +9,10 @@ package org.hibernate.sql.ast.tree.from;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath;
/**
@ -21,25 +21,25 @@ import org.hibernate.spi.NavigablePath;
public class MappedByTableGroup extends DelegatingTableGroup implements VirtualTableGroup {
private final NavigablePath navigablePath;
private final ModelPartContainer modelPart;
private final TableGroupProducer producer;
private final TableGroup underlyingTableGroup;
private final boolean fetched;
private final TableGroup parentTableGroup;
private final BiPredicate<NavigablePath, String> navigablePathChecker;
private final LazyTableGroup.ParentTableGroupUseChecker parentTableGroupUseChecker;
public MappedByTableGroup(
NavigablePath navigablePath,
ModelPartContainer modelPart,
TableGroupProducer producer,
TableGroup underlyingTableGroup,
boolean fetched,
TableGroup parentTableGroup,
BiPredicate<NavigablePath, String> navigablePathChecker) {
LazyTableGroup.ParentTableGroupUseChecker parentTableGroupUseChecker) {
this.navigablePath = navigablePath;
this.modelPart = modelPart;
this.producer = producer;
this.underlyingTableGroup = underlyingTableGroup;
this.fetched = fetched;
this.parentTableGroup = parentTableGroup;
this.navigablePathChecker = navigablePathChecker;
this.parentTableGroupUseChecker = parentTableGroupUseChecker;
}
@Override
@ -75,7 +75,7 @@ public class MappedByTableGroup extends DelegatingTableGroup implements VirtualT
@Override
public ModelPartContainer getModelPart() {
return modelPart;
return producer;
}
// Don't provide access to table group joins as this is table group is just a "named reference"
@ -119,12 +119,39 @@ public class MappedByTableGroup extends DelegatingTableGroup implements VirtualT
@Override
public TableReference resolveTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization) {
String tableExpression) {
final TableReference tableReference = getTableReference(
navigablePath,
tableExpression,
allowFkOptimization,
true
);
if ( tableReference == null ) {
throw new UnknownTableReferenceException(
tableExpression,
String.format(
Locale.ROOT,
"Unable to determine TableReference (`%s`) for `%s`",
tableExpression,
navigablePath
)
);
}
return tableReference;
}
@Override
public TableReference resolveTableReference(
NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression) {
assert modelPart != null;
final TableReference tableReference = getTableReference(
navigablePath,
modelPart,
tableExpression,
true
);
@ -147,25 +174,31 @@ public class MappedByTableGroup extends DelegatingTableGroup implements VirtualT
public TableReference getTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization,
boolean resolve) {
if ( allowFkOptimization && ( navigablePath == null || navigablePathChecker.test( navigablePath, tableExpression ) ) ) {
return getTableGroup().getTableReference(
navigablePath,
tableExpression,
resolve
);
}
@Override
public TableReference getTableReference(
NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression,
boolean resolve) {
if ( parentTableGroupUseChecker.canUseParentTableGroup( producer, navigablePath, modelPart ) ) {
final TableReference reference = parentTableGroup.getTableReference(
navigablePath,
(ValuedModelPart) producer,
tableExpression,
allowFkOptimization,
resolve
);
if ( reference != null ) {
return reference;
}
}
return underlyingTableGroup.getTableReference(
navigablePath,
tableExpression,
allowFkOptimization,
resolve
);
return getTableGroup().getTableReference( navigablePath, modelPart, tableExpression, resolve );
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -9,7 +9,11 @@ package org.hibernate.sql.ast.tree.from;
import java.util.List;
import java.util.function.Consumer;
import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.metamodel.mapping.OwnedValuedModelPart;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath;
/**
@ -105,21 +109,68 @@ public class StandardVirtualTableGroup extends AbstractTableGroup implements Vir
}
@Override
public TableReference getTableReferenceInternal(
public TableReference getTableReference(
NavigablePath navigablePath,
String tableExpression,
boolean allowFkOptimization,
boolean resolve) {
final TableReference tableReference = underlyingTableGroup.getTableReference(
navigablePath,
tableExpression,
allowFkOptimization,
resolve
);
if ( tableReference != null ) {
return tableReference;
}
return super.getTableReferenceInternal( navigablePath, tableExpression, allowFkOptimization, resolve );
for ( TableReferenceJoin tableJoin : getTableReferenceJoins() ) {
final TableReference joinedTableReference = tableJoin.getJoinedTableReference().getTableReference(
navigablePath,
tableExpression,
resolve
);
if ( joinedTableReference != null) {
return joinedTableReference;
}
}
return null;
}
@Override
public TableReference getTableReference(
NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression,
boolean resolve) {
final ValuedModelPart parentModelPart;
final MappingType declaringType;
if ( modelPart instanceof OwnedValuedModelPart
&& ( declaringType = ( (OwnedValuedModelPart) modelPart ).getDeclaringType() ) instanceof EmbeddableMappingType ) {
parentModelPart = ( (EmbeddableMappingType) declaringType ).getEmbeddedValueMapping();
}
else {
parentModelPart = modelPart;
}
final TableReference tableReference = underlyingTableGroup.getTableReference(
navigablePath,
parentModelPart,
tableExpression,
resolve
);
if ( tableReference != null ) {
return tableReference;
}
for ( TableReferenceJoin tableJoin : getTableReferenceJoins() ) {
final TableReference joinedTableReference = tableJoin.getJoinedTableReference().getTableReference(
navigablePath,
modelPart,
tableExpression,
resolve
);
if ( joinedTableReference != null) {
return joinedTableReference;
}
}
return null;
}
}

View File

@ -15,6 +15,7 @@ import org.hibernate.metamodel.mapping.ModelPartContainer;
import org.hibernate.spi.NavigablePath;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.SqlAstNode;
import org.hibernate.sql.results.graph.DomainResult;
@ -159,4 +160,37 @@ public interface TableGroup extends SqlAstNode, ColumnReferenceQualifier, SqmPat
default boolean isInitialized() {
return true;
}
default TableGroup findCompatibleJoinedGroup(
TableGroupJoinProducer joinProducer,
SqlAstJoinType requestedJoinType) {
// We don't look into nested table group joins as that wouldn't be "compatible"
for ( TableGroupJoin join : getTableGroupJoins() ) {
// Compatibility obviously requires the same model part but also join type compatibility
// Note that if the requested join type is left, we can also use an existing inner join
// The other case, when the requested join type is inner and there is an existing left join,
// is not compatible though because the cardinality is different.
// We could reuse the join though if we alter the join type to INNER, but that's an optimization for later
final SqlAstJoinType joinType = join.getJoinType();
if ( join.getJoinedGroup().getModelPart() == joinProducer
&& ( requestedJoinType == joinType || requestedJoinType == SqlAstJoinType.LEFT && joinType == SqlAstJoinType.INNER ) ) {
// If there is an existing inner join, we can always use that as a new join can never produce results
// regardless of the join type or predicate since the LHS is the same table group
// If this is a left join though, we have to check if the predicate is simply the association predicate
if ( joinType == SqlAstJoinType.INNER || joinProducer.isSimpleJoinPredicate( join.getPredicate() ) ) {
return join.getJoinedGroup();
}
}
}
return null;
}
default TableGroupJoin findTableGroupJoin(TableGroup tableGroup) {
for ( TableGroupJoin join : getTableGroupJoins() ) {
if ( join.getJoinedGroup() == tableGroup ) {
return join;
}
}
return null;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -10,6 +10,7 @@ import java.util.Locale;
import java.util.Objects;
import java.util.function.Function;
import org.hibernate.metamodel.mapping.ValuedModelPart;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.from.TableReference;
@ -61,7 +62,9 @@ public class MutatingTableReference implements TableReference {
}
@Override
public TableReference resolveTableReference(NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization) {
public TableReference resolveTableReference(
NavigablePath navigablePath,
String tableExpression) {
if ( getTableName().equals( tableExpression ) ) {
return this;
}
@ -77,8 +80,36 @@ public class MutatingTableReference implements TableReference {
}
@Override
public TableReference getTableReference(NavigablePath navigablePath, String tableExpression, boolean allowFkOptimization, boolean resolve) {
return resolveTableReference( navigablePath, tableExpression, allowFkOptimization );
public TableReference resolveTableReference(
NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression) {
if ( getTableName().equals( tableExpression ) ) {
return this;
}
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"Table-expression (%s) did not match mutating table name - %s",
tableExpression,
getTableName()
)
);
}
@Override
public TableReference getTableReference(NavigablePath navigablePath, String tableExpression, boolean resolve) {
return getTableName().equals( tableExpression ) ? this : null;
}
@Override
public TableReference getTableReference(
NavigablePath navigablePath,
ValuedModelPart modelPart,
String tableExpression,
boolean resolve) {
return getTableName().equals( tableExpression ) ? this : null;
}
@Override

View File

@ -24,9 +24,9 @@ public interface DatabaseSnapshotContributor extends Fetchable {
*/
default <T> DomainResult<T> createSnapshotDomainResult(
NavigablePath navigablePath,
TableGroup tableGroup,
TableGroup parentTableGroup,
String resultVariable,
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.engine.FetchTiming;
import org.hibernate.metamodel.mapping.AssociationKey;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.spi.EntityIdentifierNavigablePath;
@ -88,8 +91,7 @@ public interface DomainResultCreationState {
ModelPart resolveModelPart(NavigablePath navigablePath);
default Fetch visitIdentifierFetch(EntityResultGraphNode fetchParent) {
final EntityIdentifierMapping identifierMapping = fetchParent.getEntityValuedModelPart()
.getEntityMappingType()
final EntityIdentifierMapping identifierMapping = fetchParent.getReferencedMappingContainer()
.getIdentifierMapping();
return fetchParent.generateFetchableFetch(
(Fetchable) identifierMapping,

View File

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

View File

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

View File

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

View File

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

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.
// For an identifier or foreign key this is called during the resolveKey phase of the fetch parent,
// so we can't use the fetch parent access in that case.
if ( fetchParentAccess != null && embedded instanceof VirtualModelPart
&& !EntityIdentifierMapping.ROLE_LOCAL_NAME.equals( embedded.getFetchableName() )
&& !ForeignKeyDescriptor.PART_NAME.equals( navigablePath.getLocalName() )
&& !ForeignKeyDescriptor.TARGET_PART_NAME.equals( navigablePath.getLocalName() ) ) {
if ( fetchParentAccess != null && embedded instanceof VirtualModelPart && !isPartOfKey ) {
fetchParentAccess.resolveInstance( processingState );
compositeInstance = fetchParentAccess.getInitializedInstance();
EntityInitializer entityInitializer = fetchParentAccess.asEntityInitializer();

View File

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

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

View File

@ -44,7 +44,12 @@ public class NotFoundSnapshotResult implements DomainResult {
// however, that would mean a 1-1 with a join-table which
// is pretty odd mapping
final ForeignKeyDescriptor fkDescriptor = toOneMapping.getForeignKeyDescriptor();
this.keyResult = fkDescriptor.createKeyDomainResult( navigablePath, keyTableGroup, null, creationState );
this.keyResult = fkDescriptor.createKeyDomainResult(
navigablePath,
targetTableGroup,
null,
creationState
);
this.targetResult = fkDescriptor.createTargetDomainResult(
navigablePath,
targetTableGroup,

View File

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

@ -25,6 +25,8 @@ import jakarta.persistence.Table;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.JoinType;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* @author Christian Beikov
*/
@ -41,9 +43,9 @@ public class ManyToOneJoinReuseTest {
@TestForIssue(jiraKey = "HHH-15648")
public void fetchAndImplicitPath(SessionFactoryScope scope) {
SQLStatementInspector sqlStatementInterceptor = scope.getCollectingStatementInspector();
sqlStatementInterceptor.clear();
scope.inTransaction(
session -> {
sqlStatementInterceptor.clear();
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
JpaCriteriaQuery<BookList> query = cb.createQuery( BookList.class );
@ -52,7 +54,11 @@ public class ManyToOneJoinReuseTest {
query.where( root.get( "book" ).isNotNull() );
session.createQuery( query ).getResultList();
sqlStatementInterceptor.assertExecuted( "select b1_0.id,b2_0.isbn,b2_0.title from BookList b1_0 join book b2_0 on b2_0.isbn=b1_0.book_isbn where b2_0.isbn is not null" );
assertEquals( 1, sqlStatementInterceptor.getSqlQueries().size() );
assertEquals(
"select b1_0.id,b2_0.isbn,b2_0.title from BookList b1_0 join book b2_0 on b2_0.isbn=b1_0.book_isbn where b2_0.isbn is not null",
sqlStatementInterceptor.getSqlQueries().get( 0 )
);
}
);
}
@ -61,9 +67,9 @@ public class ManyToOneJoinReuseTest {
@TestForIssue(jiraKey = "HHH-15645")
public void joinAndImplicitPath(SessionFactoryScope scope) {
SQLStatementInspector sqlStatementInterceptor = scope.getCollectingStatementInspector();
sqlStatementInterceptor.clear();
scope.inTransaction(
session -> {
sqlStatementInterceptor.clear();
HibernateCriteriaBuilder cb = session.getCriteriaBuilder();
JpaCriteriaQuery<BookList> query = cb.createQuery( BookList.class );
@ -77,7 +83,11 @@ public class ManyToOneJoinReuseTest {
);
session.createQuery( query ).getResultList();
sqlStatementInterceptor.assertExecuted( "select b1_0.id,b1_0.book_isbn from BookList b1_0 join book b2_0 on b2_0.isbn=b1_0.book_isbn where b2_0.isbn is not null and b1_0.book_isbn is not null" );
assertEquals( 1, sqlStatementInterceptor.getSqlQueries().size() );
assertEquals(
"select b1_0.id,b1_0.book_isbn from BookList b1_0 join book b2_0 on b2_0.isbn=b1_0.book_isbn where b2_0.isbn is not null and b1_0.book_isbn is not null",
sqlStatementInterceptor.getSqlQueries().get( 0 )
);
}
);
}

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