Various fixes and move tests from test.jpa

* Implement parameter list expansion for native queries
* Fix empty subselect fetched collection initialization
* Implement support for nested table group joins to allow joins on the map-key
* Replace `getTableReference` with `resolveTableReference` where appropriate to distinguish which calls can cause table reference joins to be created
* Fix some table reference resolving issues with inverse embeddable model parts
* Use a Fetch for entity ids instead of a DomainResult
* Fix bidirectional fetching for collection initializtion
* Implement table reference join pruning for treat usages
* Implement strict JPA compliance for different parameter styles
* Ensure From nodes in Criteria are unique
* Add unique key support to DelayedEntityFetch
* Check if FetchParent is enhanced for lazy loading for DelayedEntityFetch
* Register entity instances under all possible EntityUniqueKey
* Introduce EntityJavaTypeDescriptor that implements equality based on object identity
This commit is contained in:
Christian Beikov 2021-11-04 16:24:50 +01:00
parent 24c758c2e9
commit 38d1c122eb
173 changed files with 2927 additions and 1469 deletions

View File

@ -1052,6 +1052,7 @@ identifier
| UPDATE | UPDATE
| UPPER | UPPER
| VALUE | VALUE
| VALUES
| VERSION | VERSION
| VERSIONED | VERSIONED
| WEEK | WEEK

View File

@ -35,7 +35,7 @@ public class NativeQueryInterpreterStandardImpl implements NativeQueryInterprete
return new NativeSelectQueryPlanImpl<>( return new NativeSelectQueryPlanImpl<>(
queryDefinition.getSqlString(), queryDefinition.getSqlString(),
queryDefinition.getAffectedTableNames(), queryDefinition.getAffectedTableNames(),
queryDefinition.getQueryParameterList(), queryDefinition.getQueryParameterOccurrences(),
queryDefinition.getResultSetMapping(), queryDefinition.getResultSetMapping(),
sessionFactory sessionFactory
); );

View File

@ -44,7 +44,7 @@ public interface NativeQueryInterpreter extends Service {
return new NativeSelectQueryPlanImpl<>( return new NativeSelectQueryPlanImpl<>(
queryDefinition.getSqlString(), queryDefinition.getSqlString(),
queryDefinition.getAffectedTableNames(), queryDefinition.getAffectedTableNames(),
queryDefinition.getQueryParameterList(), queryDefinition.getQueryParameterOccurrences(),
queryDefinition.getResultSetMapping(), queryDefinition.getResultSetMapping(),
sessionFactory sessionFactory
); );

View File

@ -26,7 +26,7 @@ public class TableGroupFilterAliasGenerator implements FilterAliasGenerator {
if ( table == null ) { if ( table == null ) {
table = defaultTable; table = defaultTable;
} }
final TableReference tableReference = tableGroup.getTableReference( table ); final TableReference tableReference = tableGroup.getTableReference( null, table, true, true );
return tableReference == null ? null : tableReference.getIdentificationVariable(); return tableReference == null ? null : tableReference.getIdentificationVariable();
} }

View File

@ -33,6 +33,7 @@ import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcSelect; import org.hibernate.sql.exec.spi.JdbcSelect;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.entity.LoadingEntityEntry; import org.hibernate.sql.results.graph.entity.LoadingEntityEntry;
import org.hibernate.sql.results.internal.ResultsHelper;
import org.hibernate.sql.results.internal.RowTransformerPassThruImpl; import org.hibernate.sql.results.internal.RowTransformerPassThruImpl;
import org.hibernate.sql.results.spi.ListResultsConsumer; import org.hibernate.sql.results.spi.ListResultsConsumer;
@ -163,13 +164,22 @@ public class CollectionLoaderSubSelectFetch implements CollectionLoader {
); );
if ( subSelectFetchedCollections != null && ! subSelectFetchedCollections.isEmpty() ) { if ( subSelectFetchedCollections != null && ! subSelectFetchedCollections.isEmpty() ) {
subSelectFetchedCollections.forEach( (c) -> { subSelectFetchedCollections.forEach(
if ( c.wasInitialized() ) { c -> {
return; if ( c.wasInitialized() ) {
} return;
}
c.initializeEmptyCollection( getLoadable().getCollectionDescriptor() ); c.initializeEmptyCollection( getLoadable().getCollectionDescriptor() );
} ); ResultsHelper.finalizeCollectionLoading(
persistenceContext,
getLoadable().getCollectionDescriptor(),
c,
c.getKey(),
true
);
}
);
subSelectFetchedCollections.clear(); subSelectFetchedCollections.clear();
} }

View File

@ -60,9 +60,9 @@ import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.JdbcParameter; import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.RootTableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InListPredicate;
@ -401,21 +401,9 @@ public class LoaderSelectBuilder {
for ( ModelPart part : partsToSelect ) { for ( ModelPart part : partsToSelect ) {
final NavigablePath navigablePath = rootNavigablePath.append( part.getPartName() ); final NavigablePath navigablePath = rootNavigablePath.append( part.getPartName() );
final TableGroup tableGroup; final TableGroup tableGroup;
if ( part instanceof RootTableGroupProducer ) { if ( part instanceof TableGroupJoinProducer ) {
tableGroup = ( (RootTableGroupProducer) part ).createRootTableGroup( final TableGroupJoinProducer tableGroupJoinProducer = (TableGroupJoinProducer) part;
true, final TableGroupJoin tableGroupJoin = tableGroupJoinProducer.createTableGroupJoin(
navigablePath,
null,
() -> rootQuerySpec::applyPredicate,
sqlAstCreationState,
creationContext
);
rootQuerySpec.getFromClause().addRoot( tableGroup );
sqlAstCreationState.getFromClauseAccess().registerTableGroup( navigablePath, tableGroup );
}
else if ( part instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) part;
final TableGroupJoin tableGroupJoin = toOneAttributeMapping.createTableGroupJoin(
navigablePath, navigablePath,
rootTableGroup, rootTableGroup,
null, null,
@ -613,8 +601,18 @@ public class LoaderSelectBuilder {
tableGroup tableGroup
); );
if ( manyToManyFilterPredicate != null ) { if ( manyToManyFilterPredicate != null ) {
assert tableGroup.getTableReferenceJoins().size() == 1; TableGroupJoin elementTableGroupJoin = null;
tableGroup.getTableReferenceJoins().get( 0 ).applyPredicate( manyToManyFilterPredicate ); for ( TableGroupJoin nestedTableGroupJoin : tableGroup.getNestedTableGroupJoins() ) {
final NavigablePath navigablePath = nestedTableGroupJoin.getNavigablePath();
if ( navigablePath.getParent() == tableGroup.getNavigablePath()
&& CollectionPart.Nature.ELEMENT.getName().equals( navigablePath.getUnaliasedLocalName() ) ) {
elementTableGroupJoin = nestedTableGroupJoin;
break;
}
}
assert elementTableGroupJoin != null;
elementTableGroupJoin.applyPredicate( manyToManyFilterPredicate );
} }
} }
} }

View File

@ -16,6 +16,7 @@ import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.from.RootTableGroupProducer; import org.hibernate.sql.ast.tree.from.RootTableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
@ -42,7 +43,8 @@ public interface Loadable extends ModelPart, RootTableGroupProducer {
NavigablePath navigablePath, NavigablePath navigablePath,
String explicitSourceAlias, String explicitSourceAlias,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess, Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAstCreationState creationState, SqlAstCreationContext creationContext) { SqlAstCreationState creationState,
SqlAstCreationContext creationContext) {
throw new NotYetImplementedFor6Exception( getClass() ); throw new NotYetImplementedFor6Exception( getClass() );
} }
@ -53,7 +55,8 @@ public interface Loadable extends ModelPart, RootTableGroupProducer {
String explicitSourceAlias, String explicitSourceAlias,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess, Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAliasBase sqlAliasBase, SqlAliasBase sqlAliasBase,
SqlAstCreationState creationState, SqlAstCreationContext creationContext) { SqlExpressionResolver expressionResolver,
SqlAstCreationContext creationContext) {
throw new NotYetImplementedFor6Exception( getClass() ); throw new NotYetImplementedFor6Exception( getClass() );
} }
} }

View File

@ -159,7 +159,6 @@ public abstract class AbstractCompositeIdentifierMapping
fetchParent, fetchParent,
fetchTiming, fetchTiming,
selected, selected,
attributeMetadataAccess.resolveAttributeMetadata( null ).isNullable(),
creationState creationState
); );
} }

View File

@ -87,7 +87,7 @@ public class EntityRepresentationStrategyPojoStandard implements EntityRepresent
.getJavaTypeDescriptorRegistry(); .getJavaTypeDescriptorRegistry();
final Class<?> mappedJavaType = bootDescriptor.getMappedClass(); final Class<?> mappedJavaType = bootDescriptor.getMappedClass();
this.mappedJtd = jtdRegistry.resolveManagedTypeDescriptor( mappedJavaType ); this.mappedJtd = jtdRegistry.resolveEntityTypeDescriptor( mappedJavaType );
final Class<?> proxyJavaType = bootDescriptor.getProxyInterface(); final Class<?> proxyJavaType = bootDescriptor.getProxyInterface();
if ( proxyJavaType != null ) { if ( proxyJavaType != null ) {

View File

@ -38,6 +38,7 @@ import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping;
import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping; import org.hibernate.metamodel.mapping.internal.DiscriminatedAssociationAttributeMapping;
import org.hibernate.metamodel.mapping.internal.EmbeddedAttributeMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper; import org.hibernate.metamodel.mapping.internal.MappingModelCreationHelper;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.mapping.internal.SelectableMappingsImpl; import org.hibernate.metamodel.mapping.internal.SelectableMappingsImpl;
@ -52,6 +53,7 @@ import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetchable; import org.hibernate.sql.results.graph.Fetchable;
@ -176,7 +178,8 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp
} }
private EmbeddableMappingType( private EmbeddableMappingType(
EmbeddableValuedModelPart valueMapping, EmbeddedAttributeMapping valueMapping,
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings, SelectableMappings selectableMappings,
EmbeddableMappingType inverseMappingType, EmbeddableMappingType inverseMappingType,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
@ -186,6 +189,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp
this.valueMapping = valueMapping; this.valueMapping = valueMapping;
this.createEmptyCompositesEnabled = inverseMappingType.isCreateEmptyCompositesEnabled(); this.createEmptyCompositesEnabled = inverseMappingType.isCreateEmptyCompositesEnabled();
this.selectableMappings = selectableMappings; this.selectableMappings = selectableMappings;
final ManagedMappingType declaringType = valueMapping.getDeclaringType();
creationProcess.registerInitializationCallback( creationProcess.registerInitializationCallback(
"EmbeddableMappingType(" + inverseMappingType.getNavigableRole().getFullPath() + ".{inverse})#finishInitialization", "EmbeddableMappingType(" + inverseMappingType.getNavigableRole().getFullPath() + ".{inverse})#finishInitialization",
() -> { () -> {
@ -201,6 +205,7 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp
final BasicAttributeMapping original = (BasicAttributeMapping) attributeMapping; final BasicAttributeMapping original = (BasicAttributeMapping) attributeMapping;
final SelectableMapping selectableMapping = selectableMappings.getSelectable( currentIndex ); final SelectableMapping selectableMapping = selectableMappings.getSelectable( currentIndex );
attributeMapping = BasicAttributeMapping.withSelectableMapping( attributeMapping = BasicAttributeMapping.withSelectableMapping(
declaringType,
original, original,
original.getPropertyAccess(), original.getPropertyAccess(),
original.getValueGeneration(), original.getValueGeneration(),
@ -210,13 +215,18 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp
} }
else if ( attributeMapping instanceof ToOneAttributeMapping ) { else if ( attributeMapping instanceof ToOneAttributeMapping ) {
final ToOneAttributeMapping original = (ToOneAttributeMapping) attributeMapping; final ToOneAttributeMapping original = (ToOneAttributeMapping) attributeMapping;
final ToOneAttributeMapping toOne = original.copy(); final ToOneAttributeMapping toOne = original.copy(
declaringType,
declaringTableGroupProducer
);
final int offset = currentIndex; final int offset = currentIndex;
toOne.setIdentifyingColumnsTableExpression( toOne.setIdentifyingColumnsTableExpression(
selectableMappings.getSelectable( offset ).getContainingTableExpression() selectableMappings.getSelectable( offset ).getContainingTableExpression()
); );
toOne.setForeignKeyDescriptor( toOne.setForeignKeyDescriptor(
original.getForeignKeyDescriptor().withKeySelectionMapping( original.getForeignKeyDescriptor().withKeySelectionMapping(
declaringType,
declaringTableGroupProducer,
index -> selectableMappings.getSelectable( offset + index ), index -> selectableMappings.getSelectable( offset + index ),
creationProcess creationProcess
) )
@ -237,10 +247,17 @@ public class EmbeddableMappingType implements ManagedMappingType, SelectableMapp
} }
public EmbeddableMappingType createInverseMappingType( public EmbeddableMappingType createInverseMappingType(
EmbeddableValuedModelPart valueMapping, EmbeddedAttributeMapping valueMapping,
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings, SelectableMappings selectableMappings,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
return new EmbeddableMappingType( valueMapping, selectableMappings, this, creationProcess ); return new EmbeddableMappingType(
valueMapping,
declaringTableGroupProducer,
selectableMappings,
this,
creationProcess
);
} }
private boolean finishInitialization( private boolean finishInitialization(

View File

@ -9,6 +9,7 @@ package org.hibernate.metamodel.mapping;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -195,8 +196,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
return superMappingType.getRootEntityDescriptor(); return superMappingType.getRootEntityDescriptor();
} }
default TableReference locateTableReference(TableGroup tableGroup) { default void pruneForSubclasses(TableGroup tableGroup, Set<String> treatedEntityNames) {
return tableGroup.getPrimaryTableReference();
} }
default boolean isAbstract() { default boolean isAbstract() {
@ -296,7 +296,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
explicitSourceAlias, explicitSourceAlias,
additionalPredicateCollectorAccess, additionalPredicateCollectorAccess,
creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ), creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ),
creationState, creationState.getSqlExpressionResolver(),
creationContext creationContext
); );
} }
@ -308,7 +308,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
String explicitSourceAlias, String explicitSourceAlias,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess, Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAliasBase sqlAliasBase, SqlAliasBase sqlAliasBase,
SqlAstCreationState creationState, SqlExpressionResolver expressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
return getEntityPersister().createRootTableGroup( return getEntityPersister().createRootTableGroup(
canUseInnerJoins, canUseInnerJoins,
@ -316,7 +316,7 @@ public interface EntityMappingType extends ManagedMappingType, EntityValuedModel
explicitSourceAlias, explicitSourceAlias,
additionalPredicateCollectorAccess, additionalPredicateCollectorAccess,
sqlAliasBase, sqlAliasBase,
creationState, expressionResolver,
creationContext creationContext
); );
} }

View File

@ -15,6 +15,7 @@ import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -102,15 +103,15 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
DomainResultCreationState creationState); DomainResultCreationState creationState);
Predicate generateJoinPredicate( Predicate generateJoinPredicate(
TableGroup lhs, TableGroup targetSideTableGroup,
TableGroup tableGroup, TableGroup keySideTableGroup,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext); SqlAstCreationContext creationContext);
Predicate generateJoinPredicate( Predicate generateJoinPredicate(
TableReference lhs, TableReference targetSideReference,
TableReference rhs, TableReference keySideReference,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext); SqlAstCreationContext creationContext);
@ -149,6 +150,8 @@ public interface ForeignKeyDescriptor extends VirtualModelPart {
* Return a copy of this foreign key descriptor with the selectable mappings as provided by the given accessor. * Return a copy of this foreign key descriptor with the selectable mappings as provided by the given accessor.
*/ */
ForeignKeyDescriptor withKeySelectionMapping( ForeignKeyDescriptor withKeySelectionMapping(
ManagedMappingType declaringType,
TableGroupProducer declaringTableGroupProducer,
IntFunction<SelectableMapping> selectableMappingAccess, IntFunction<SelectableMapping> selectableMappingAccess,
MappingModelCreationProcess creationProcess); MappingModelCreationProcess creationProcess);

View File

@ -46,7 +46,7 @@ public class MappingModelHelper {
qualifier = selection.getContainingTableExpression(); qualifier = selection.getContainingTableExpression();
} }
else { else {
qualifier = tableGroup.getTableReference( selection.getContainingTableExpression() ).getIdentificationVariable(); qualifier = tableGroup.resolveTableReference( selection.getContainingTableExpression() ).getIdentificationVariable();
} }
if ( sqlExpressionResolver == null ) { if ( sqlExpressionResolver == null ) {
colRef = new ColumnReference( colRef = new ColumnReference(
@ -78,7 +78,7 @@ public class MappingModelHelper {
qualifier = basicPart.getContainingTableExpression(); qualifier = basicPart.getContainingTableExpression();
} }
else { else {
qualifier = tableGroup.getTableReference( basicPart.getContainingTableExpression() ).getIdentificationVariable(); qualifier = tableGroup.resolveTableReference( basicPart.getContainingTableExpression() ).getIdentificationVariable();
} }
if ( sqlExpressionResolver == null ) { if ( sqlExpressionResolver == null ) {
return new ColumnReference( return new ColumnReference(

View File

@ -177,7 +177,6 @@ public abstract class AbstractDiscriminatorMapping implements EntityDiscriminato
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
false,
null, null,
fetchTiming, fetchTiming,
creationState creationState

View File

@ -188,7 +188,7 @@ public class AnyDiscriminatorPart implements BasicValuedModelPart, FetchOptions,
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
final TableGroup tableGroup = fromClauseAccess.getTableGroup( fetchablePath.getParent().getParent() ); final TableGroup tableGroup = fromClauseAccess.getTableGroup( fetchablePath.getParent().getParent() );
final TableReference tableReference = tableGroup.getTableReference( fetchablePath, table ); final TableReference tableReference = tableGroup.resolveTableReference( fetchablePath, table );
final Expression columnReference = sqlExpressionResolver.resolveSqlExpression( final Expression columnReference = sqlExpressionResolver.resolveSqlExpression(
createColumnReferenceKey( tableReference, column ), createColumnReferenceKey( tableReference, column ),
processingState -> new ColumnReference( processingState -> new ColumnReference(
@ -212,7 +212,6 @@ public class AnyDiscriminatorPart implements BasicValuedModelPart, FetchOptions,
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
nullable,
null, null,
fetchTiming, fetchTiming,
creationState creationState

View File

@ -151,7 +151,7 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
.getSessionFactory(); .getSessionFactory();
final TableGroup tableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath().getParent() ); final TableGroup tableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath().getParent() );
final TableReference tableReference = tableGroup.getTableReference( fetchablePath, table ); final TableReference tableReference = tableGroup.resolveTableReference( fetchablePath, table );
final Expression columnReference = sqlExpressionResolver.resolveSqlExpression( final Expression columnReference = sqlExpressionResolver.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( tableReference, column ), SqlExpressionResolver.createColumnReferenceKey( tableReference, column ),
@ -177,7 +177,6 @@ public class AnyKeyPart implements BasicValuedModelPart, FetchOptions {
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
nullable,
null, null,
fetchTiming, fetchTiming,
creationState creationState

View File

@ -106,6 +106,7 @@ public class BasicAttributeMapping
} }
public static BasicAttributeMapping withSelectableMapping( public static BasicAttributeMapping withSelectableMapping(
ManagedMappingType declaringType,
BasicValuedModelPart original, BasicValuedModelPart original,
PropertyAccess propertyAccess, PropertyAccess propertyAccess,
ValueGeneration valueGeneration, ValueGeneration valueGeneration,
@ -114,19 +115,16 @@ public class BasicAttributeMapping
int stateArrayPosition = 0; int stateArrayPosition = 0;
StateArrayContributorMetadataAccess attributeMetadataAccess = null; StateArrayContributorMetadataAccess attributeMetadataAccess = null;
BasicValueConverter<?, ?> valueConverter = null; BasicValueConverter<?, ?> valueConverter = null;
ManagedMappingType declaringType = null;
if ( original instanceof SingleAttributeIdentifierMapping ) { if ( original instanceof SingleAttributeIdentifierMapping ) {
final SingleAttributeIdentifierMapping mapping = (SingleAttributeIdentifierMapping) original; final SingleAttributeIdentifierMapping mapping = (SingleAttributeIdentifierMapping) original;
attributeName = mapping.getAttributeName(); attributeName = mapping.getAttributeName();
attributeMetadataAccess = null; attributeMetadataAccess = null;
declaringType = mapping.findContainingEntityMapping();
} }
else if ( original instanceof SingularAttributeMapping ) { else if ( original instanceof SingularAttributeMapping ) {
final SingularAttributeMapping mapping = (SingularAttributeMapping) original; final SingularAttributeMapping mapping = (SingularAttributeMapping) original;
attributeName = mapping.getAttributeName(); attributeName = mapping.getAttributeName();
stateArrayPosition = mapping.getStateArrayPosition(); stateArrayPosition = mapping.getStateArrayPosition();
attributeMetadataAccess = mapping.getAttributeMetadataAccess(); attributeMetadataAccess = mapping.getAttributeMetadataAccess();
declaringType = mapping.getDeclaringType();
} }
if ( original instanceof ConvertibleModelPart ) { if ( original instanceof ConvertibleModelPart ) {
valueConverter = ( (ConvertibleModelPart) original ).getValueConverter(); valueConverter = ( (ConvertibleModelPart) original ).getValueConverter();
@ -212,7 +210,7 @@ public class BasicAttributeMapping
TableGroup tableGroup, TableGroup tableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, true, creationState ); final SqlSelection sqlSelection = resolveSqlSelection( navigablePath, tableGroup, true, creationState );
//noinspection unchecked //noinspection unchecked
return new BasicResult( return new BasicResult(
@ -225,12 +223,13 @@ public class BasicAttributeMapping
} }
private SqlSelection resolveSqlSelection( private SqlSelection resolveSqlSelection(
NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
boolean allowFkOptimization, boolean allowFkOptimization,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver(); final SqlExpressionResolver expressionResolver = creationState.getSqlAstCreationState().getSqlExpressionResolver();
final TableReference tableReference = tableGroup.resolveTableReference( final TableReference tableReference = tableGroup.resolveTableReference(
tableGroup.getNavigablePath().append( getNavigableRole().getNavigableName() ), navigablePath,
getContainingTableExpression(), getContainingTableExpression(),
allowFkOptimization allowFkOptimization
); );
@ -257,7 +256,7 @@ public class BasicAttributeMapping
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
resolveSqlSelection( tableGroup, true, creationState ); resolveSqlSelection( navigablePath, tableGroup, true, creationState );
} }
@Override @Override
@ -266,7 +265,7 @@ public class BasicAttributeMapping
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState, DomainResultCreationState creationState,
BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) { BiConsumer<SqlSelection, JdbcMapping> selectionConsumer) {
selectionConsumer.accept( resolveSqlSelection( tableGroup, true, creationState ), getJdbcMapping() ); selectionConsumer.accept( resolveSqlSelection( navigablePath, tableGroup, true, creationState ), getJdbcMapping() );
} }
@Override @Override
@ -293,7 +292,7 @@ public class BasicAttributeMapping
assert tableGroup != null; assert tableGroup != null;
final SqlSelection sqlSelection = resolveSqlSelection( tableGroup, false, creationState ); final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, true, creationState );
valuesArrayPosition = sqlSelection.getValuesArrayPosition(); valuesArrayPosition = sqlSelection.getValuesArrayPosition();
} }
@ -302,7 +301,6 @@ public class BasicAttributeMapping
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(),
valueConverter, valueConverter,
fetchTiming, fetchTiming,
creationState creationState

View File

@ -31,6 +31,7 @@ import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.proxy.HibernateProxy; import org.hibernate.proxy.HibernateProxy;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
@ -326,12 +327,19 @@ public class BasicEntityIdentifierMappingImpl implements BasicEntityIdentifierMa
boolean selected, boolean selected,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup(
fetchParent.getNavigablePath()
);
assert tableGroup != null;
final SqlSelection sqlSelection = resolveSqlSelection( fetchablePath, tableGroup, false, creationState );
return new BasicFetch<>( return new BasicFetch<>(
0, sqlSelection.getValuesArrayPosition(),
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
false,
null, null,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
creationState creationState

View File

@ -230,7 +230,6 @@ public class BasicValuedCollectionPart
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
false,
valueConverter, valueConverter,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
creationState creationState

View File

@ -9,20 +9,29 @@ package org.hibernate.metamodel.mapping.internal;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
import org.hibernate.persister.entity.DiscriminatorType; import org.hibernate.persister.entity.DiscriminatorType;
import org.hibernate.persister.entity.JoinedSubclassEntityPersister; import org.hibernate.persister.entity.JoinedSubclassEntityPersister;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.basic.BasicFetch;
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey; import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
@ -69,6 +78,31 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator
} }
} }
@Override
public BasicFetch generateFetch(
FetchParent fetchParent,
NavigablePath fetchablePath,
FetchTiming fetchTiming,
boolean selected,
String resultVariable,
DomainResultCreationState creationState) {
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final TableGroup tableGroup = sqlAstCreationState.getFromClauseAccess().getTableGroup(
fetchParent.getNavigablePath()
);
// Since the expression is lazy, based on the available table reference joins,
// we need to force the initialization in case this is a fetch
tableDiscriminatorDetailsMap.forEach(
(tableName, tableDiscriminatorDetails) -> tableGroup.getTableReference(
fetchablePath,
tableName,
false,
true
)
);
return super.generateFetch( fetchParent, fetchablePath, fetchTiming, selected, resultVariable, creationState );
}
@Override @Override
public Expression resolveSqlExpression( public Expression resolveSqlExpression(
NavigablePath navigablePath, NavigablePath navigablePath,
@ -82,37 +116,63 @@ public class CaseStatementDiscriminatorMappingImpl extends AbstractDiscriminator
); );
} }
private CaseSearchedExpression createCaseSearchedExpression(TableGroup entityTableGroup) { private Expression createCaseSearchedExpression(TableGroup entityTableGroup) {
final CaseSearchedExpression caseSearchedExpression = new CaseSearchedExpression( this ); return new SelfRenderingExpression() {
CaseSearchedExpression caseSearchedExpression;
tableDiscriminatorDetailsMap.forEach( (tableName, tableDiscriminatorDetails) -> { @Override
final TableReference tableReference = entityTableGroup.getTableReference( entityTableGroup.getNavigablePath(), tableName ); public void renderToSql(
SqlAppender sqlAppender,
SqlAstTranslator<?> walker,
SessionFactoryImplementor sessionFactory) {
if ( caseSearchedExpression == null ) {
// todo (6.0): possible optimization is to omit cases for table reference joins, that touch a super class, where a subclass is inner joined due to pruning
caseSearchedExpression = new CaseSearchedExpression( CaseStatementDiscriminatorMappingImpl.this );
tableDiscriminatorDetailsMap.forEach(
(tableName, tableDiscriminatorDetails) -> {
final TableReference tableReference = entityTableGroup.getTableReference(
entityTableGroup.getNavigablePath(),
tableName,
false,
false
);
if ( tableReference == null ) { if ( tableReference == null ) {
// assume this is because it is a table that is not part of the processing entity's sub-hierarchy // assume this is because it is a table that is not part of the processing entity's sub-hierarchy
return; return;
}
final Predicate predicate = new NullnessPredicate(
new ColumnReference(
tableReference,
tableDiscriminatorDetails.getCheckColumnName(),
false,
null,
null,
getJdbcMapping(),
getSessionFactory()
),
true
);
caseSearchedExpression.when(
predicate,
new QueryLiteral<>(
tableDiscriminatorDetails.getDiscriminatorValue(),
getUnderlyingJdbcMappingType()
)
);
}
);
}
caseSearchedExpression.accept( walker );
} }
final Predicate predicate = new NullnessPredicate( @Override
new ColumnReference( public JdbcMappingContainer getExpressionType() {
tableReference, return CaseStatementDiscriminatorMappingImpl.this;
tableDiscriminatorDetails.getCheckColumnName(), }
false, };
null,
null,
getJdbcMapping(),
getSessionFactory()
),
true
);
caseSearchedExpression.when( predicate, new QueryLiteral<>(
tableDiscriminatorDetails.getDiscriminatorValue(),
getUnderlyingJdbcMappingType()
) );
} );
return caseSearchedExpression;
} }
@Override @Override

View File

@ -180,7 +180,6 @@ public class CollectionIdentifierDescriptorImpl implements CollectionIdentifierD
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
! selected,
null, null,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
creationState creationState

View File

@ -43,6 +43,7 @@ import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.from.CompositeTableGroup; import org.hibernate.sql.ast.tree.from.CompositeTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -110,6 +111,8 @@ public class EmbeddedAttributeMapping
// Constructor is only used for creating the inverse attribute mapping // Constructor is only used for creating the inverse attribute mapping
private EmbeddedAttributeMapping( private EmbeddedAttributeMapping(
ManagedMappingType keyDeclaringType,
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings, SelectableMappings selectableMappings,
EmbeddableValuedModelPart inverseModelPart, EmbeddableValuedModelPart inverseModelPart,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
@ -118,11 +121,7 @@ public class EmbeddedAttributeMapping
-1, -1,
null, null,
inverseModelPart.getMappedFetchOptions(), inverseModelPart.getMappedFetchOptions(),
inverseModelPart instanceof AttributeMapping keyDeclaringType,
? ( (AttributeMapping) inverseModelPart ).getDeclaringType()
: inverseModelPart instanceof EntityIdentifierMapping
? inverseModelPart.findContainingEntityMapping()
: null,
null, null,
null null
); );
@ -132,6 +131,7 @@ public class EmbeddedAttributeMapping
this.tableExpression = selectableMappings.getSelectable( 0 ).getContainingTableExpression(); this.tableExpression = selectableMappings.getSelectable( 0 ).getContainingTableExpression();
this.embeddableMappingType = inverseModelPart.getEmbeddableTypeDescriptor().createInverseMappingType( this.embeddableMappingType = inverseModelPart.getEmbeddableTypeDescriptor().createInverseMappingType(
this, this,
declaringTableGroupProducer,
selectableMappings, selectableMappings,
creationProcess creationProcess
); );
@ -140,9 +140,17 @@ public class EmbeddedAttributeMapping
public static EmbeddableValuedModelPart createInverseModelPart( public static EmbeddableValuedModelPart createInverseModelPart(
EmbeddableValuedModelPart modelPart, EmbeddableValuedModelPart modelPart,
ManagedMappingType keyDeclaringType,
TableGroupProducer declaringTableGroupProducer,
SelectableMappings selectableMappings, SelectableMappings selectableMappings,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
return new EmbeddedAttributeMapping( selectableMappings, modelPart, creationProcess ); return new EmbeddedAttributeMapping(
keyDeclaringType,
declaringTableGroupProducer,
selectableMappings,
modelPart,
creationProcess
);
} }
@Override @Override
@ -225,7 +233,6 @@ public class EmbeddedAttributeMapping
fetchParent, fetchParent,
fetchTiming, fetchTiming,
selected, selected,
getAttributeMetadataAccess().resolveAttributeMetadata( null ).isNullable(),
creationState creationState
); );
} }

View File

@ -159,7 +159,6 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
fetchParent, fetchParent,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
selected, selected,
true,
creationState creationState
); );
} }

View File

@ -19,6 +19,7 @@ import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.ModelPartContainer; import org.hibernate.metamodel.mapping.ModelPartContainer;
@ -27,6 +28,7 @@ import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SelectableMappings; import org.hibernate.metamodel.mapping.SelectableMappings;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.query.ComparisonOperator; import org.hibernate.query.ComparisonOperator;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
@ -36,6 +38,7 @@ import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.predicate.Junction;
@ -101,6 +104,8 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
private EmbeddedForeignKeyDescriptor( private EmbeddedForeignKeyDescriptor(
EmbeddedForeignKeyDescriptor original, EmbeddedForeignKeyDescriptor original,
String keyTable, String keyTable,
ManagedMappingType keyDeclaringType,
TableGroupProducer keyDeclaringTableGroupProducer,
SelectableMappings keySelectableMappings, SelectableMappings keySelectableMappings,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
this.keyTable = keyTable; this.keyTable = keyTable;
@ -112,6 +117,8 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
Nature.KEY, Nature.KEY,
EmbeddedAttributeMapping.createInverseModelPart( EmbeddedAttributeMapping.createInverseModelPart(
original.targetSide.getModelPart(), original.targetSide.getModelPart(),
keyDeclaringType,
keyDeclaringTableGroupProducer,
keySelectableMappings, keySelectableMappings,
creationProcess creationProcess
) )
@ -158,6 +165,8 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
@Override @Override
public ForeignKeyDescriptor withKeySelectionMapping( public ForeignKeyDescriptor withKeySelectionMapping(
ManagedMappingType declaringType,
TableGroupProducer declaringTableGroupProducer,
IntFunction<SelectableMapping> selectableMappingAccess, IntFunction<SelectableMapping> selectableMappingAccess,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
SelectableMapping[] selectionMappings = new SelectableMapping[keySelectableMappings.getJdbcTypeCount()]; SelectableMapping[] selectionMappings = new SelectableMapping[keySelectableMappings.getJdbcTypeCount()];
@ -167,6 +176,8 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
return new EmbeddedForeignKeyDescriptor( return new EmbeddedForeignKeyDescriptor(
this, this,
selectionMappings[0].getContainingTableExpression(), selectionMappings[0].getContainingTableExpression(),
declaringType,
declaringTableGroupProducer,
new SelectableMappingsImpl( selectionMappings ), new SelectableMappingsImpl( selectionMappings ),
creationProcess creationProcess
); );
@ -340,11 +351,11 @@ public class EmbeddedForeignKeyDescriptor implements ForeignKeyDescriptor {
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
final TableReference lhsTableReference = targetSideTableGroup.getTableReference( final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference(
targetSideTableGroup.getNavigablePath(), targetSideTableGroup.getNavigablePath(),
targetTable targetTable
); );
final TableReference rhsTableKeyReference = keySideTableGroup.getTableReference( keyTable ); final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference( keyTable );
return generateJoinPredicate( return generateJoinPredicate(
lhsTableReference, lhsTableReference,

View File

@ -6,42 +6,66 @@
*/ */
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.Consumer; import java.util.function.Consumer;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.IndexedCollection;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value; import org.hibernate.mapping.Value;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.FromClauseAccess;
import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator; import org.hibernate.sql.ast.spi.SqlAliasBaseGenerator;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
import org.hibernate.sql.ast.tree.from.OneToManyTableGroup;
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.FetchOptions; import org.hibernate.sql.results.graph.FetchOptions;
import org.hibernate.sql.results.graph.FetchParent; import org.hibernate.sql.results.graph.FetchParent;
import org.hibernate.sql.results.graph.collection.internal.EntityCollectionPartTableGroup;
import org.hibernate.sql.results.graph.entity.EntityFetch; import org.hibernate.sql.results.graph.entity.EntityFetch;
import org.hibernate.sql.results.graph.entity.EntityValuedFetchable; import org.hibernate.sql.results.graph.entity.EntityValuedFetchable;
import org.hibernate.sql.results.graph.entity.internal.EntityFetchJoinedImpl; import org.hibernate.sql.results.graph.entity.internal.EntityFetchJoinedImpl;
import org.hibernate.tuple.IdentifierProperty;
import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type;
import org.hibernate.type.descriptor.java.JavaType; import org.hibernate.type.descriptor.java.JavaType;
/** /**
@ -53,8 +77,10 @@ public class EntityCollectionPart
private final CollectionPersister collectionDescriptor; private final CollectionPersister collectionDescriptor;
private final Nature nature; private final Nature nature;
private final EntityMappingType entityMappingType; private final EntityMappingType entityMappingType;
private final Set<String> targetKeyPropertyNames;
private ModelPart fkTargetModelPart; private ModelPart fkTargetModelPart;
private ForeignKeyDescriptor fkDescriptor;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public EntityCollectionPart( public EntityCollectionPart(
@ -67,6 +93,81 @@ public class EntityCollectionPart
this.collectionDescriptor = collectionDescriptor; this.collectionDescriptor = collectionDescriptor;
this.nature = nature; this.nature = nature;
this.entityMappingType = entityMappingType; this.entityMappingType = entityMappingType;
final String referencedPropertyName;
if ( bootModelValue instanceof OneToMany ) {
referencedPropertyName = null;
}
else {
referencedPropertyName = ( (ToOne) bootModelValue ).getReferencedPropertyName();
}
if ( referencedPropertyName == null ) {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
final IdentifierProperty identifierProperty = getEntityMappingType()
.getEntityPersister()
.getEntityMetamodel()
.getIdentifierProperty();
final Type propertyType = identifierProperty.getType();
if ( identifierProperty.getName() == null ) {
final CompositeType compositeType;
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
&& compositeType.getPropertyNames().length == 1 ) {
ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames,
compositeType.getPropertyNames()[0],
compositeType.getSubtypes()[0],
creationProcess.getCreationContext().getSessionFactory()
);
}
else {
ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames,
null,
propertyType,
creationProcess.getCreationContext().getSessionFactory()
);
}
}
else {
ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames,
identifierProperty.getName(),
propertyType,
creationProcess.getCreationContext().getSessionFactory()
);
}
this.targetKeyPropertyNames = targetKeyPropertyNames;
}
else if ( bootModelValue instanceof OneToMany ) {
this.targetKeyPropertyNames = Collections.singleton( referencedPropertyName );
}
else {
final EntityMetamodel entityMetamodel = entityMappingType.getEntityPersister().getEntityMetamodel();
final int propertyIndex = entityMetamodel.getPropertyIndex( referencedPropertyName );
final Type propertyType = entityMetamodel.getPropertyTypes()[propertyIndex];
final CompositeType compositeType;
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
&& compositeType.getPropertyNames().length == 1 ) {
this.targetKeyPropertyNames = Collections.singleton( compositeType.getPropertyNames()[0] );
}
else {
final String mapsIdAttributeName;
if ( ( mapsIdAttributeName = ToOneAttributeMapping.mapsId( entityMappingType, referencedPropertyName ) ) != null ) {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
targetKeyPropertyNames.add( referencedPropertyName );
ToOneAttributeMapping.addPrefixedPropertyNames(
targetKeyPropertyNames,
mapsIdAttributeName,
entityMappingType.getEntityPersister().getIdentifierType(),
creationProcess.getCreationContext().getSessionFactory()
);
this.targetKeyPropertyNames = targetKeyPropertyNames;
}
else {
this.targetKeyPropertyNames = Collections.singleton( referencedPropertyName );
}
}
}
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@ -81,8 +182,80 @@ public class EntityCollectionPart
else { else {
fkTargetModelPart = entityMappingType.findSubPart( fkTargetModelPartName, null ); fkTargetModelPart = entityMappingType.findSubPart( fkTargetModelPartName, null );
} }
if ( nature == Nature.ELEMENT ) {
fkDescriptor = createForeignKeyDescriptor(
bootValueMapping.getElement(),
(EntityType) collectionDescriptor.getElementType(),
creationProcess,
collectionDescriptor.getFactory().getJdbcServices().getDialect()
);
}
else {
fkDescriptor = createForeignKeyDescriptor(
( (IndexedCollection) bootValueMapping).getIndex(),
(EntityType) collectionDescriptor.getIndexType(),
creationProcess,
collectionDescriptor.getFactory().getJdbcServices().getDialect()
);
}
} }
private ForeignKeyDescriptor createForeignKeyDescriptor(
Value fkBootDescriptorSource,
EntityType entityType,
MappingModelCreationProcess creationProcess,
Dialect dialect) {
final EntityPersister associatedEntityDescriptor = creationProcess.getEntityPersister( entityType.getAssociatedEntityName() );
final ModelPart fkTargetPart = entityType.isReferenceToPrimaryKey()
? associatedEntityDescriptor.getIdentifierMapping()
: associatedEntityDescriptor.findSubPart( entityType.getRHSUniqueKeyPropertyName() );
if ( fkTargetPart instanceof BasicValuedModelPart ) {
final BasicValuedModelPart basicFkTargetPart = (BasicValuedModelPart) fkTargetPart;
final Joinable collectionDescriptorAsJoinable = (Joinable) collectionDescriptor;
final SelectableMapping keySelectableMapping = SelectableMappingImpl.from(
collectionDescriptorAsJoinable.getTableName(),
fkBootDescriptorSource.getColumnIterator().next(),
basicFkTargetPart.getJdbcMapping(),
dialect,
creationProcess.getSqmFunctionRegistry()
);
final boolean hasConstraint;
if ( fkBootDescriptorSource instanceof SimpleValue ) {
hasConstraint = ( (SimpleValue) fkBootDescriptorSource ).isConstrained();
}
else {
// We assume there is a constraint if the key is not nullable
hasConstraint = !fkBootDescriptorSource.isNullable();
}
return new SimpleForeignKeyDescriptor(
associatedEntityDescriptor,
basicFkTargetPart,
null,
keySelectableMapping,
basicFkTargetPart,
entityType.isReferenceToPrimaryKey(),
hasConstraint
);
}
else if ( fkTargetPart instanceof EmbeddableValuedModelPart ) {
return MappingModelCreationHelper.buildEmbeddableForeignKeyDescriptor(
(EmbeddableValuedModelPart) fkTargetPart,
fkBootDescriptorSource,
findContainingEntityMapping(),
collectionDescriptor.getAttributeMapping(),
false,
dialect,
creationProcess
);
}
else {
throw new NotYetImplementedFor6Exception(
"Support for composite foreign keys not yet implemented : " + collectionDescriptor
.getRole()
);
}
}
@Override @Override
public SqlAstJoinType getDefaultSqlAstJoinType(TableGroup parentTableGroup) { public SqlAstJoinType getDefaultSqlAstJoinType(TableGroup parentTableGroup) {
@ -146,22 +319,24 @@ public class EntityCollectionPart
final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess(); final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess();
creationState.registerVisitedAssociationKey( getForeignKeyDescriptor().getAssociationKey() ); creationState.registerVisitedAssociationKey( getForeignKeyDescriptor().getAssociationKey() );
TableGroup tableGroup = fromClauseAccess.resolveTableGroup( final TableGroup partTableGroup = fromClauseAccess.resolveTableGroup(
fetchablePath, fetchablePath,
np -> { np -> {
// We need to create one. The Result will be able to find it later by path final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( np.getParent() );
if ( collectionDescriptor.isOneToMany() && nature == Nature.ELEMENT ) {
return ( (OneToManyTableGroup) parentTableGroup ).getElementTableGroup();
}
for ( TableGroupJoin nestedTableGroupJoin : parentTableGroup.getNestedTableGroupJoins() ) {
if ( nestedTableGroupJoin.getNavigablePath().equals( np ) ) {
return nestedTableGroupJoin.getJoinedGroup();
}
}
// first, find the collection's TableGroup throw new IllegalStateException( "Could not find table group for: " + np );
final TableGroup collectionTableGroup = fromClauseAccess.getTableGroup( fetchParent.getNavigablePath() );
assert collectionTableGroup != null;
// create a "wrapper" around the collection TableGroup adding in the entity's table references
return new EntityCollectionPartTableGroup( fetchablePath, collectionTableGroup, this );
} }
); );
return new EntityFetchJoinedImpl( fetchParent, this, tableGroup, selected, fetchablePath, creationState ); return new EntityFetchJoinedImpl( fetchParent, this, partTableGroup, selected, fetchablePath, creationState );
} }
@Override @Override
@ -184,15 +359,20 @@ public class EntityCollectionPart
final TableGroup partTableGroup = fromClauseAccess.resolveTableGroup( final TableGroup partTableGroup = fromClauseAccess.resolveTableGroup(
navigablePath, navigablePath,
np -> { np -> {
// We need to create one. The Result will be able to find it later by path final TableGroup parentTableGroup = fromClauseAccess.getTableGroup( np.getParent() );
if ( collectionDescriptor.isOneToMany() && nature == Nature.ELEMENT ) {
// first, find the collection's TableGroup return ( (OneToManyTableGroup) parentTableGroup ).getElementTableGroup();
final TableGroup collectionTableGroup = fromClauseAccess.getTableGroup( np.getParent() ); }
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
assert collectionTableGroup != null; navigablePath,
parentTableGroup,
// create a "wrapper" around the collection TableGroup adding in the entity's table references resultVariable,
return new EntityCollectionPartTableGroup( np, collectionTableGroup, this ); SqlAstJoinType.INNER,
true,
creationState.getSqlAstCreationState()
);
parentTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup();
} }
); );
@ -247,8 +427,7 @@ public class EntityCollectionPart
@Override @Override
public ForeignKeyDescriptor getForeignKeyDescriptor() { public ForeignKeyDescriptor getForeignKeyDescriptor() {
// todo (6.0) : this will not strictly work - we'd want a new ForeignKeyDescriptor that points the other direction return fkDescriptor;
return collectionDescriptor.getAttributeMapping().getKeyDescriptor();
} }
@Override @Override
@ -269,27 +448,60 @@ public class EntityCollectionPart
@Override @Override
public TableGroupJoin createTableGroupJoin( public TableGroupJoin createTableGroupJoin(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup lhs, TableGroup collectionTableGroup,
String explicitSourceAlias, String explicitSourceAlias,
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
boolean fetched, boolean fetched,
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
return collectionDescriptor.getAttributeMapping().createTableGroupJoin( if ( collectionDescriptor.isOneToMany() && nature == Nature.ELEMENT ) {
// If this is a one-to-many, the element part is already available, so we return a TableGroupJoin "hull"
return new TableGroupJoin(
navigablePath,
sqlAstJoinType,
( (OneToManyTableGroup) collectionTableGroup ).getElementTableGroup(),
null
);
}
final LazyTableGroup lazyTableGroup = createRootTableGroupJoin(
navigablePath, navigablePath,
lhs, collectionTableGroup,
explicitSourceAlias, explicitSourceAlias,
sqlAstJoinType, sqlAstJoinType,
fetched, fetched,
null,
aliasBaseGenerator, aliasBaseGenerator,
sqlExpressionResolver, sqlExpressionResolver,
creationContext creationContext
); );
final TableGroupJoin join = new TableGroupJoin(
navigablePath,
sqlAstJoinType,
lazyTableGroup,
null
);
final TableReference keySideTableReference = collectionTableGroup.getPrimaryTableReference();
lazyTableGroup.setTableGroupInitializerCallback(
tableGroup -> join.applyPredicate(
fkDescriptor.generateJoinPredicate(
tableGroup.getPrimaryTableReference(),
keySideTableReference,
sqlAstJoinType,
sqlExpressionResolver,
creationContext
)
)
);
return join;
} }
@Override @Override
public TableGroup createRootTableGroupJoin( public LazyTableGroup createRootTableGroupJoin(
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup lhs, TableGroup lhs,
String explicitSourceAlias, String explicitSourceAlias,
@ -299,17 +511,101 @@ public class EntityCollectionPart
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
return collectionDescriptor.getAttributeMapping().createRootTableGroupJoin( final SqlAliasBase sqlAliasBase = aliasBaseGenerator.createSqlAliasBase( getSqlAliasStem() );
final boolean canUseInnerJoin = sqlAstJoinType == SqlAstJoinType.INNER || lhs.canUseInnerJoins();
final LazyTableGroup lazyTableGroup = new LazyTableGroup(
canUseInnerJoin,
navigablePath, navigablePath,
lhs,
explicitSourceAlias,
sqlAstJoinType,
fetched, fetched,
predicateConsumer, () -> createTableGroupInternal(
aliasBaseGenerator, canUseInnerJoin,
navigablePath,
fetched,
null,
sqlAliasBase,
sqlExpressionResolver,
creationContext
),
(np, tableExpression) -> {
NavigablePath path = np.getParent();
// Fast path
if ( path != null && navigablePath.equals( path ) ) {
return targetKeyPropertyNames.contains( np.getUnaliasedLocalName() )
&& fkDescriptor.getKeyTable().equals( tableExpression );
}
final StringBuilder sb = new StringBuilder( np.getFullPath().length() );
sb.append( np.getUnaliasedLocalName() );
while ( path != null && !navigablePath.equals( path ) ) {
sb.insert( 0, '.' );
sb.insert( 0, path.getUnaliasedLocalName() );
path = path.getParent();
}
return path != null && navigablePath.equals( path )
&& targetKeyPropertyNames.contains( sb.toString() )
&& fkDescriptor.getKeyTable().equals( tableExpression );
},
this,
explicitSourceAlias,
sqlAliasBase,
creationContext.getSessionFactory(),
lhs
);
if ( predicateConsumer != null ) {
final TableReference keySideTableReference = lhs.resolveTableReference(
navigablePath,
fkDescriptor.getKeyTable()
);
lazyTableGroup.setTableGroupInitializerCallback(
tableGroup -> predicateConsumer.accept(
fkDescriptor.generateJoinPredicate(
tableGroup.getPrimaryTableReference(),
keySideTableReference,
sqlAstJoinType,
sqlExpressionResolver,
creationContext
)
)
);
}
return lazyTableGroup;
}
public TableGroup createTableGroupInternal(
boolean canUseInnerJoins,
NavigablePath navigablePath,
boolean fetched,
String sourceAlias,
final SqlAliasBase sqlAliasBase,
SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) {
final TableReference primaryTableReference = getEntityMappingType().createPrimaryTableReference(
sqlAliasBase,
sqlExpressionResolver, sqlExpressionResolver,
creationContext creationContext
); );
return new StandardTableGroup(
canUseInnerJoins,
navigablePath,
this,
fetched,
sourceAlias,
primaryTableReference,
false,
sqlAliasBase,
(tableExpression) -> getEntityMappingType().containsTableReference( tableExpression ),
(tableExpression, tg) -> getEntityMappingType().createTableReferenceJoin(
tableExpression,
sqlAliasBase,
primaryTableReference,
sqlExpressionResolver,
creationContext
),
creationContext.getSessionFactory()
);
} }
@Override @Override

View File

@ -200,7 +200,6 @@ public class EntityVersionMappingImpl implements EntityVersionMapping, FetchOpti
fetchParent, fetchParent,
fetchablePath, fetchablePath,
this, this,
false,
null, null,
fetchTiming, fetchTiming,
creationState creationState

View File

@ -76,6 +76,7 @@ import org.hibernate.metamodel.model.convert.spi.BasicValueConverter;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext; import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.collection.SQLLoadableCollection; import org.hibernate.persister.collection.SQLLoadableCollection;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.Joinable;
@ -84,6 +85,7 @@ import org.hibernate.property.access.internal.ChainedPropertyAccessImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl; import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.sql.ast.spi.SqlAliasStemHelper; import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.tuple.ValueGeneration; import org.hibernate.tuple.ValueGeneration;
import org.hibernate.type.AnyType; import org.hibernate.type.AnyType;
import org.hibernate.type.AssociationType; import org.hibernate.type.AssociationType;
@ -908,6 +910,17 @@ public class MappingModelCreationHelper {
final ModelPart fkTarget; final ModelPart fkTarget;
final String lhsPropertyName = collectionDescriptor.getCollectionType().getLHSPropertyName(); final String lhsPropertyName = collectionDescriptor.getCollectionType().getLHSPropertyName();
final boolean isReferenceToPrimaryKey = lhsPropertyName == null; final boolean isReferenceToPrimaryKey = lhsPropertyName == null;
final ManagedMappingType keyDeclaringType;
if ( collectionDescriptor.getElementType().isEntityType() ) {
keyDeclaringType = ( (QueryableCollection) collectionDescriptor ).getElementPersister();
}
else {
// This is not "really correct" but it is as good as it gets.
// The key declaring type serves as declaring type for the inverse model part of a FK.
// Most of the time, there is a proper managed type, but not for basic collections.
// Since the declaring type is needed for certain operations, we use the one from the target side of the FK
keyDeclaringType = declaringType;
}
if ( isReferenceToPrimaryKey ) { if ( isReferenceToPrimaryKey ) {
fkTarget = collectionDescriptor.getOwnerEntityPersister().getIdentifierMapping(); fkTarget = collectionDescriptor.getOwnerEntityPersister().getIdentifierMapping();
} }
@ -929,6 +942,7 @@ public class MappingModelCreationHelper {
); );
attributeMapping.setForeignKeyDescriptor( attributeMapping.setForeignKeyDescriptor(
new SimpleForeignKeyDescriptor( new SimpleForeignKeyDescriptor(
keyDeclaringType,
simpleFkTarget, simpleFkTarget,
null, null,
keySelectableMapping, keySelectableMapping,
@ -943,6 +957,9 @@ public class MappingModelCreationHelper {
buildEmbeddableForeignKeyDescriptor( buildEmbeddableForeignKeyDescriptor(
(EmbeddableValuedModelPart) fkTarget, (EmbeddableValuedModelPart) fkTarget,
bootValueMapping, bootValueMapping,
keyDeclaringType,
collectionDescriptor.getAttributeMapping(),
false,
dialect, dialect,
creationProcess creationProcess
); );
@ -1025,6 +1042,8 @@ public class MappingModelCreationHelper {
final EmbeddedForeignKeyDescriptor embeddedForeignKeyDescriptor = buildEmbeddableForeignKeyDescriptor( final EmbeddedForeignKeyDescriptor embeddedForeignKeyDescriptor = buildEmbeddableForeignKeyDescriptor(
(EmbeddableValuedModelPart) modelPart, (EmbeddableValuedModelPart) modelPart,
bootValueMapping, bootValueMapping,
attributeMapping.getDeclaringType(),
attributeMapping.findContainingEntityMapping(),
true, true,
dialect, dialect,
creationProcess creationProcess
@ -1058,12 +1077,15 @@ public class MappingModelCreationHelper {
if ( inversePropertyAccess == null ) { if ( inversePropertyAccess == null ) {
// So far, OneToOne mappings are only supported based on the owner's PK // So far, OneToOne mappings are only supported based on the owner's PK
if ( bootValueMapping instanceof OneToOne ) { if ( bootValueMapping instanceof OneToOne ) {
declaringKeyPart = (BasicValuedModelPart) attributeMapping.findContainingEntityMapping().getIdentifierMapping(); declaringKeyPart = simpleFkTarget;
final EntityIdentifierMapping identifierMapping = attributeMapping.findContainingEntityMapping()
.getIdentifierMapping();
declaringKeyPropertyAccess = ( (PropertyBasedMapping) identifierMapping ).getPropertyAccess();
} }
else { else {
declaringKeyPart = simpleFkTarget; declaringKeyPart = simpleFkTarget;
declaringKeyPropertyAccess = ( (PropertyBasedMapping) declaringKeyPart ).getPropertyAccess();
} }
declaringKeyPropertyAccess = ( (PropertyBasedMapping) declaringKeyPart ).getPropertyAccess();
} }
else { else {
declaringKeyPart = simpleFkTarget; declaringKeyPart = simpleFkTarget;
@ -1094,6 +1116,7 @@ public class MappingModelCreationHelper {
} }
final ForeignKeyDescriptor foreignKeyDescriptor = new SimpleForeignKeyDescriptor( final ForeignKeyDescriptor foreignKeyDescriptor = new SimpleForeignKeyDescriptor(
attributeMapping.getDeclaringType(),
declaringKeyPart, declaringKeyPart,
declaringKeyPropertyAccess, declaringKeyPropertyAccess,
keySelectableMapping, keySelectableMapping,
@ -1108,6 +1131,8 @@ public class MappingModelCreationHelper {
final EmbeddedForeignKeyDescriptor embeddedForeignKeyDescriptor = buildEmbeddableForeignKeyDescriptor( final EmbeddedForeignKeyDescriptor embeddedForeignKeyDescriptor = buildEmbeddableForeignKeyDescriptor(
(EmbeddableValuedModelPart) fkTarget, (EmbeddableValuedModelPart) fkTarget,
bootValueMapping, bootValueMapping,
attributeMapping.getDeclaringType(),
attributeMapping.findContainingEntityMapping(),
swapDirection, swapDirection,
dialect, dialect,
creationProcess creationProcess
@ -1179,20 +1204,8 @@ public class MappingModelCreationHelper {
public static EmbeddedForeignKeyDescriptor buildEmbeddableForeignKeyDescriptor( public static EmbeddedForeignKeyDescriptor buildEmbeddableForeignKeyDescriptor(
EmbeddableValuedModelPart embeddableValuedModelPart, EmbeddableValuedModelPart embeddableValuedModelPart,
Value bootValueMapping, Value bootValueMapping,
Dialect dialect, ManagedMappingType keyDeclaringType,
MappingModelCreationProcess creationProcess) { TableGroupProducer keyDeclaringTableGroupProducer,
return buildEmbeddableForeignKeyDescriptor(
embeddableValuedModelPart,
bootValueMapping,
false,
dialect,
creationProcess
);
}
private static EmbeddedForeignKeyDescriptor buildEmbeddableForeignKeyDescriptor(
EmbeddableValuedModelPart embeddableValuedModelPart,
Value bootValueMapping,
boolean inverse, boolean inverse,
Dialect dialect, Dialect dialect,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
@ -1241,6 +1254,8 @@ public class MappingModelCreationHelper {
embeddableValuedModelPart, embeddableValuedModelPart,
EmbeddedAttributeMapping.createInverseModelPart( EmbeddedAttributeMapping.createInverseModelPart(
embeddableValuedModelPart, embeddableValuedModelPart,
keyDeclaringType,
keyDeclaringTableGroupProducer,
keySelectableMappings, keySelectableMappings,
creationProcess creationProcess
), ),
@ -1256,6 +1271,8 @@ public class MappingModelCreationHelper {
return new EmbeddedForeignKeyDescriptor( return new EmbeddedForeignKeyDescriptor(
EmbeddedAttributeMapping.createInverseModelPart( EmbeddedAttributeMapping.createInverseModelPart(
embeddableValuedModelPart, embeddableValuedModelPart,
keyDeclaringType,
keyDeclaringTableGroupProducer,
keySelectableMappings, keySelectableMappings,
creationProcess creationProcess
), ),

View File

@ -19,6 +19,7 @@ import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping; import org.hibernate.metamodel.internal.AbstractCompositeIdentifierMapping;
import org.hibernate.metamodel.mapping.EmbeddableMappingType; import org.hibernate.metamodel.mapping.EmbeddableMappingType;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.SingularAttributeMapping;
@ -175,7 +176,7 @@ public class NonAggregatedIdentifierMappingImpl extends AbstractCompositeIdentif
@Override @Override
public String getFetchableName() { public String getFetchableName() {
return "id"; return EntityIdentifierMapping.ROLE_LOCAL_NAME;
} }
@Override @Override

View File

@ -6,14 +6,11 @@
*/ */
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import java.util.ArrayList;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.NotYetImplementedFor6Exception;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.spi.CascadeStyle; import org.hibernate.engine.spi.CascadeStyle;
@ -21,31 +18,24 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Collection; import org.hibernate.mapping.Collection;
import org.hibernate.mapping.IndexedCollection;
import org.hibernate.mapping.IndexedConsumer; import org.hibernate.mapping.IndexedConsumer;
import org.hibernate.mapping.List; import org.hibernate.mapping.List;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor; import org.hibernate.metamodel.mapping.CollectionIdentifierDescriptor;
import org.hibernate.metamodel.mapping.CollectionMappingType; import org.hibernate.metamodel.mapping.CollectionMappingType;
import org.hibernate.metamodel.mapping.CollectionPart; import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EmbeddableValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType; import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping; import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess; import org.hibernate.metamodel.mapping.StateArrayContributorMetadataAccess;
import org.hibernate.metamodel.mapping.ordering.OrderByFragment; import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
import org.hibernate.metamodel.mapping.ordering.OrderByFragmentTranslator; import org.hibernate.metamodel.mapping.ordering.OrderByFragmentTranslator;
import org.hibernate.metamodel.mapping.ordering.TranslationContext; import org.hibernate.metamodel.mapping.ordering.TranslationContext;
import org.hibernate.metamodel.model.domain.NavigableRole; import org.hibernate.metamodel.model.domain.NavigableRole;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable; import org.hibernate.persister.entity.Joinable;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
@ -58,11 +48,11 @@ import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.from.StandardTableGroup; import org.hibernate.sql.ast.tree.from.CollectionTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin; import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin; import org.hibernate.sql.ast.tree.from.OneToManyTableGroup;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
@ -74,7 +64,6 @@ import org.hibernate.sql.results.graph.collection.internal.DelayedCollectionFetc
import org.hibernate.sql.results.graph.collection.internal.EagerCollectionFetch; import org.hibernate.sql.results.graph.collection.internal.EagerCollectionFetch;
import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollectionFetch; import org.hibernate.sql.results.graph.collection.internal.SelectEagerCollectionFetch;
import org.hibernate.tuple.ValueGeneration; import org.hibernate.tuple.ValueGeneration;
import org.hibernate.type.EntityType;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -113,45 +102,10 @@ public class PluralAttributeMappingImpl
private final IndexMetadata indexMetadata; private final IndexMetadata indexMetadata;
private ForeignKeyDescriptor fkDescriptor; private ForeignKeyDescriptor fkDescriptor;
private ForeignKeyDescriptor elementFkDescriptor;
private ForeignKeyDescriptor indexFkDescriptor;
private OrderByFragment orderByFragment; private OrderByFragment orderByFragment;
private OrderByFragment manyToManyOrderByFragment; private OrderByFragment manyToManyOrderByFragment;
@SuppressWarnings({"WeakerAccess", "rawtypes"})
public PluralAttributeMappingImpl(
String attributeName,
Collection bootDescriptor,
PropertyAccess propertyAccess,
StateArrayContributorMetadataAccess stateArrayContributorMetadataAccess,
CollectionMappingType collectionMappingType,
int stateArrayPosition,
CollectionPart elementDescriptor,
CollectionPart indexDescriptor,
CollectionIdentifierDescriptor identifierDescriptor,
FetchOptions fetchOptions,
CascadeStyle cascadeStyle,
ManagedMappingType declaringType,
CollectionPersister collectionDescriptor) {
this(
attributeName,
bootDescriptor,
propertyAccess,
stateArrayContributorMetadataAccess,
collectionMappingType,
stateArrayPosition,
elementDescriptor,
indexDescriptor,
identifierDescriptor,
fetchOptions.getTiming(),
fetchOptions.getStyle(),
cascadeStyle,
declaringType,
collectionDescriptor
);
}
@SuppressWarnings({"WeakerAccess", "rawtypes"}) @SuppressWarnings({"WeakerAccess", "rawtypes"})
public PluralAttributeMappingImpl( public PluralAttributeMappingImpl(
String attributeName, String attributeName,
@ -243,40 +197,6 @@ public class PluralAttributeMappingImpl
Property bootProperty, Property bootProperty,
Collection bootDescriptor, Collection bootDescriptor,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
final Dialect dialect = creationProcess.getCreationContext()
.getSessionFactory()
.getJdbcServices()
.getDialect();
if ( collectionDescriptor.getElementType() instanceof EntityType ) {
creationProcess.registerForeignKeyPostInitCallbacks(
"To-many key - " + getNavigableRole(),
() -> {
elementFkDescriptor = createForeignKeyDescriptor(
bootDescriptor.getElement(),
(EntityType) collectionDescriptor.getElementType(),
creationProcess,
dialect
);
return true;
}
);
}
if ( collectionDescriptor.getIndexType() instanceof EntityType ) {
creationProcess.registerForeignKeyPostInitCallbacks(
"To-many index - " + getNavigableRole(),
() -> {
indexFkDescriptor = createForeignKeyDescriptor(
( (IndexedCollection) bootDescriptor ).getIndex(),
(EntityType) collectionDescriptor.getIndexType(),
creationProcess,
dialect
);
return true;
}
);
}
final boolean hasOrder = bootDescriptor.getOrderBy() != null; final boolean hasOrder = bootDescriptor.getOrderBy() != null;
final boolean hasManyToManyOrder = bootDescriptor.getManyToManyOrdering() != null; final boolean hasManyToManyOrder = bootDescriptor.getManyToManyOrdering() != null;
@ -315,59 +235,6 @@ public class PluralAttributeMappingImpl
} }
} }
private ForeignKeyDescriptor createForeignKeyDescriptor(
Value fkBootDescriptorSource,
EntityType entityType,
MappingModelCreationProcess creationProcess,
Dialect dialect) {
final EntityPersister associatedEntityDescriptor = creationProcess.getEntityPersister( entityType.getAssociatedEntityName() );
final ModelPart fkTargetPart = entityType.isReferenceToPrimaryKey()
? associatedEntityDescriptor.getIdentifierMapping()
: associatedEntityDescriptor.findSubPart( entityType.getRHSUniqueKeyPropertyName() );
if ( fkTargetPart instanceof BasicValuedModelPart ) {
final BasicValuedModelPart basicFkTargetPart = (BasicValuedModelPart) fkTargetPart;
final Joinable collectionDescriptorAsJoinable = (Joinable) collectionDescriptor;
final SelectableMapping keySelectableMapping = SelectableMappingImpl.from(
collectionDescriptorAsJoinable.getTableName(),
fkBootDescriptorSource.getColumnIterator().next(),
basicFkTargetPart.getJdbcMapping(),
dialect,
creationProcess.getSqmFunctionRegistry()
);
final boolean hasConstraint;
if ( fkBootDescriptorSource instanceof SimpleValue ) {
hasConstraint = ( (SimpleValue) fkBootDescriptorSource ).isConstrained();
}
else {
// We assume there is a constraint if the key is not nullable
hasConstraint = !fkBootDescriptorSource.isNullable();
}
return new SimpleForeignKeyDescriptor(
basicFkTargetPart,
null,
keySelectableMapping,
basicFkTargetPart,
entityType.isReferenceToPrimaryKey(),
hasConstraint
);
}
else if ( fkTargetPart instanceof EmbeddableValuedModelPart ) {
return MappingModelCreationHelper.buildEmbeddableForeignKeyDescriptor(
(EmbeddableValuedModelPart) fkTargetPart,
fkBootDescriptorSource,
dialect,
creationProcess
);
}
else {
throw new NotYetImplementedFor6Exception(
"Support for composite foreign keys not yet implemented : " + collectionDescriptor
.getRole()
);
}
}
@Override @Override
public NavigableRole getNavigableRole() { public NavigableRole getNavigableRole() {
return getCollectionDescriptor().getNavigableRole(); return getCollectionDescriptor().getNavigableRole();
@ -482,6 +349,10 @@ public class PluralAttributeMappingImpl
assert collectionTableGroup != null; assert collectionTableGroup != null;
// This is only used for collection initialization where we know the owner is available, so we mark it as visited
// which will cause bidirectional to-one associations to be treated as such and avoid a join
creationState.registerVisitedAssociationKey( fkDescriptor.getAssociationKey() );
//noinspection unchecked //noinspection unchecked
return new CollectionDomainResult( navigablePath, this, resultVariable, tableGroup, creationState ); return new CollectionDomainResult( navigablePath, this, resultVariable, tableGroup, creationState );
} }
@ -498,7 +369,7 @@ public class PluralAttributeMappingImpl
creationState.registerVisitedAssociationKey( fkDescriptor.getAssociationKey() ); creationState.registerVisitedAssociationKey( fkDescriptor.getAssociationKey() );
if ( fetchTiming == FetchTiming.IMMEDIATE) { if ( fetchTiming == FetchTiming.IMMEDIATE ) {
if ( selected ) { if ( selected ) {
final TableGroup collectionTableGroup = resolveCollectionTableGroup( final TableGroup collectionTableGroup = resolveCollectionTableGroup(
fetchParent, fetchParent,
@ -628,29 +499,26 @@ public class PluralAttributeMappingImpl
SqlAliasBaseGenerator aliasBaseGenerator, SqlAliasBaseGenerator aliasBaseGenerator,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
final java.util.List<Predicate> predicates = new ArrayList<>( 2 );
final TableGroup tableGroup = createRootTableGroupJoin( final TableGroup tableGroup = createRootTableGroupJoin(
navigablePath, navigablePath,
lhs, lhs,
explicitSourceAlias, explicitSourceAlias,
sqlAstJoinType, sqlAstJoinType,
fetched, fetched,
null, predicates::add,
aliasBaseGenerator, aliasBaseGenerator,
sqlExpressionResolver, sqlExpressionResolver,
creationContext creationContext
); );
return new TableGroupJoin( final TableGroupJoin tableGroupJoin = new TableGroupJoin(
navigablePath, navigablePath,
sqlAstJoinType, sqlAstJoinType,
tableGroup, tableGroup,
getKeyDescriptor().generateJoinPredicate( null
lhs,
tableGroup,
sqlAstJoinType,
sqlExpressionResolver,
creationContext
)
); );
predicates.forEach( tableGroupJoin::applyPredicate );
return tableGroupJoin;
} }
@Override @Override
@ -715,125 +583,36 @@ public class PluralAttributeMappingImpl
SqlAliasBase sqlAliasBase, SqlAliasBase sqlAliasBase,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
final EntityMappingType elementDescriptorEntityMappingType; final TableGroup elementTableGroup = ( (EntityCollectionPart) elementDescriptor ).createTableGroupInternal(
if ( elementDescriptor instanceof EntityCollectionPart ) { canUseInnerJoins,
elementDescriptorEntityMappingType = ( (EntityCollectionPart) elementDescriptor ).getEntityMappingType(); navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
} fetched,
else { sourceAlias,
assert indexDescriptor instanceof EntityCollectionPart;
elementDescriptorEntityMappingType = null;
}
final EntityMappingType indexDescriptorEntityMappingType;
if ( indexDescriptor instanceof EntityCollectionPart ) {
indexDescriptorEntityMappingType = ( (EntityCollectionPart) indexDescriptor ).getEntityMappingType();
}
else {
indexDescriptorEntityMappingType = null;
}
if ( indexDescriptorEntityMappingType == null || elementDescriptorEntityMappingType == null ) {
final EntityMappingType entityMappingType;
if ( indexDescriptorEntityMappingType == null ) {
entityMappingType = elementDescriptorEntityMappingType.getEntityMappingType();
}
else {
entityMappingType = indexDescriptorEntityMappingType.getEntityMappingType();
}
final TableReference primaryTableReference = entityMappingType
.createPrimaryTableReference(
sqlAliasBase,
sqlExpressionResolver,
creationContext
);
return new StandardTableGroup(
canUseInnerJoins,
navigablePath,
this,
fetched,
sourceAlias,
primaryTableReference,
true,
sqlAliasBase,
entityMappingType::containsTableReference,
(tableExpression, tg) -> entityMappingType.createTableReferenceJoin(
tableExpression,
sqlAliasBase,
primaryTableReference,
sqlExpressionResolver,
creationContext
),
creationContext.getSessionFactory()
);
}
final TableReference primaryTableReference = elementDescriptorEntityMappingType
.createPrimaryTableReference(
sqlAliasBase,
sqlExpressionResolver,
creationContext
);
final BiFunction<String, TableGroup, TableReferenceJoin> tableReferenceJoinCreator;
final java.util.function.Predicate<String> tableReferenceJoinNameChecker = createTableReferenceJoinNameChecker(
elementDescriptorEntityMappingType,
indexDescriptorEntityMappingType
);
final TableReference indexAssociatedPrimaryTable = indexDescriptorEntityMappingType.createPrimaryTableReference(
sqlAliasBase, sqlAliasBase,
sqlExpressionResolver, sqlExpressionResolver,
creationContext creationContext
); );
final OneToManyTableGroup tableGroup = new OneToManyTableGroup(
final Function<TableGroup, TableReferenceJoin> indexTableGroupFinalizer = createTableGroupFinalizer(
sqlExpressionResolver,
creationContext,
primaryTableReference,
indexAssociatedPrimaryTable,
SqlAstJoinType.INNER,
indexFkDescriptor
);
tableReferenceJoinCreator = (tableExpression, tableGroup) -> {
if ( elementDescriptorEntityMappingType.containsTableReference( tableExpression ) ) {
return elementDescriptorEntityMappingType.createTableReferenceJoin(
tableExpression,
sqlAliasBase,
primaryTableReference,
sqlExpressionResolver,
creationContext
);
}
else if ( indexDescriptorEntityMappingType.containsTableReference( tableExpression ) ) {
return createTableReferenceJoin(
sqlExpressionResolver,
creationContext,
sqlAliasBase,
indexDescriptorEntityMappingType,
indexAssociatedPrimaryTable,
indexTableGroupFinalizer,
tableExpression,
tableGroup
);
}
throw new IllegalStateException( "could not create join for table `" + tableExpression + "`" );
};
return new StandardTableGroup(
canUseInnerJoins,
navigablePath,
this, this,
fetched, elementTableGroup,
sourceAlias,
primaryTableReference,
true,
sqlAliasBase,
tableReferenceJoinNameChecker,
tableReferenceJoinCreator,
creationContext.getSessionFactory() creationContext.getSessionFactory()
); );
if ( indexDescriptor instanceof EntityCollectionPart ) {
final TableGroupJoin tableGroupJoin = ( (EntityCollectionPart) indexDescriptor ).createTableGroupJoin(
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
elementTableGroup,
null,
SqlAstJoinType.INNER,
fetched,
stem -> sqlAliasBase,
sqlExpressionResolver,
creationContext
);
tableGroup.registerIndexTableGroup( tableGroupJoin );
}
return tableGroup;
} }
private TableGroup createCollectionTableGroup( private TableGroup createCollectionTableGroup(
@ -854,116 +633,7 @@ public class PluralAttributeMappingImpl
creationContext.getSessionFactory() creationContext.getSessionFactory()
); );
final EntityMappingType elementDescriptorEntityMappingType; final CollectionTableGroup tableGroup = new CollectionTableGroup(
if ( elementDescriptor instanceof EntityCollectionPart ) {
elementDescriptorEntityMappingType = ( (EntityCollectionPart) elementDescriptor ).getEntityMappingType();
}
else {
elementDescriptorEntityMappingType = null;
}
final EntityMappingType indexDescriptorEntityMappingType;
if ( indexDescriptor instanceof EntityCollectionPart ) {
indexDescriptorEntityMappingType = ( (EntityCollectionPart) indexDescriptor ).getEntityMappingType();
}
else {
indexDescriptorEntityMappingType = null;
}
final BiFunction<String, TableGroup, TableReferenceJoin> tableReferenceJoinCreator;
final java.util.function.Predicate<String> tableReferenceJoinNameChecker = createTableReferenceJoinNameChecker(
elementDescriptorEntityMappingType,
indexDescriptorEntityMappingType
);
final TableReference elementAssociatedPrimaryTable;
final Function<TableGroup, TableReferenceJoin> elementTableGroupFinalizer;
// todo (6.0) : not sure it is
if ( elementDescriptorEntityMappingType != null ) {
elementAssociatedPrimaryTable = elementDescriptorEntityMappingType.createPrimaryTableReference(
sqlAliasBase,
sqlExpressionResolver,
creationContext
);
elementTableGroupFinalizer = createTableGroupFinalizer(
sqlExpressionResolver,
creationContext,
collectionTableReference,
elementAssociatedPrimaryTable,
SqlAstJoinType.INNER,
elementFkDescriptor
);
}
else {
elementAssociatedPrimaryTable = null;
elementTableGroupFinalizer = null;
}
TableReference indexAssociatedPrimaryTable;
final Function<TableGroup, TableReferenceJoin> indexTableGroupFinalizer;
if ( indexDescriptorEntityMappingType != null ) {
indexAssociatedPrimaryTable = indexDescriptorEntityMappingType.createPrimaryTableReference(
sqlAliasBase,
sqlExpressionResolver,
creationContext
);
indexTableGroupFinalizer = createTableGroupFinalizer(
sqlExpressionResolver,
creationContext,
collectionTableReference,
indexAssociatedPrimaryTable,
SqlAstJoinType.INNER,
indexFkDescriptor
);
}
else {
indexAssociatedPrimaryTable = null;
indexTableGroupFinalizer = null;
}
if ( elementDescriptorEntityMappingType != null || indexDescriptorEntityMappingType != null ) {
tableReferenceJoinCreator = (tableExpression, tableGroup) -> {
if ( elementDescriptorEntityMappingType != null
&& elementDescriptorEntityMappingType.containsTableReference( tableExpression ) ) {
return createTableReferenceJoin(
sqlExpressionResolver,
creationContext,
sqlAliasBase,
elementDescriptorEntityMappingType,
elementAssociatedPrimaryTable,
elementTableGroupFinalizer,
tableExpression,
tableGroup
);
}
else if ( indexDescriptorEntityMappingType != null
&& indexDescriptorEntityMappingType.containsTableReference( tableExpression ) ) {
return createTableReferenceJoin(
sqlExpressionResolver,
creationContext,
sqlAliasBase,
indexDescriptorEntityMappingType,
indexAssociatedPrimaryTable,
indexTableGroupFinalizer,
tableExpression,
tableGroup
);
}
throw new IllegalStateException( "could not create join for table `" + tableExpression + "`" );
};
}
else {
tableReferenceJoinCreator = (tableExpression, tableGroup) -> {
throw new UnsupportedOperationException(
"element-collection cannot contain joins : " + collectionTableReference.getTableExpression() + " -> " + tableExpression
);
};
}
final StandardTableGroup tableGroup = new StandardTableGroup(
canUseInnerJoins, canUseInnerJoins,
navigablePath, navigablePath,
this, this,
@ -972,86 +642,39 @@ public class PluralAttributeMappingImpl
collectionTableReference, collectionTableReference,
true, true,
sqlAliasBase, sqlAliasBase,
tableReferenceJoinNameChecker, s -> false,
tableReferenceJoinCreator, null,
creationContext.getSessionFactory() creationContext.getSessionFactory()
); );
return tableGroup; if ( indexDescriptor instanceof EntityCollectionPart ) {
} final TableGroupJoin tableGroupJoin = ( (EntityCollectionPart) indexDescriptor ).createTableGroupJoin(
navigablePath.append( CollectionPart.Nature.INDEX.getName() ),
private TableReferenceJoin createTableReferenceJoin( tableGroup,
SqlExpressionResolver sqlExpressionResolver, null,
SqlAstCreationContext creationContext, SqlAstJoinType.INNER,
SqlAliasBase sqlAliasBase, fetched,
EntityMappingType elementDescriptorEntityMappingType, stem -> sqlAliasBase,
TableReference elementAssociatedPrimaryTable, sqlExpressionResolver,
Function<TableGroup, TableReferenceJoin> elementTableGroupFinalizer, creationContext
String tableExpression, TableGroup tableGroup) {
TableReferenceJoin elementTableReferenceJoin;
if ( elementAssociatedPrimaryTable.getTableExpression().equals( tableExpression ) ) {
return elementTableGroupFinalizer.apply( tableGroup );
}
else {
StandardTableGroup standardTableGroup = (StandardTableGroup) tableGroup;
if ( standardTableGroup.getTableReferenceJoins().isEmpty() ) {
elementTableReferenceJoin = elementTableGroupFinalizer.apply( tableGroup );
}
else {
elementTableReferenceJoin = null;
}
}
final TableReferenceJoin tableReferenceJoin = elementDescriptorEntityMappingType.createTableReferenceJoin(
tableExpression,
sqlAliasBase,
elementAssociatedPrimaryTable,
sqlExpressionResolver,
creationContext
);
if ( tableReferenceJoin != null && elementTableReferenceJoin != null ) {
( (StandardTableGroup) tableGroup ).addTableReferenceJoin( elementTableReferenceJoin );
return tableReferenceJoin;
}
return elementTableReferenceJoin == null ? tableReferenceJoin : elementTableReferenceJoin;
}
private Function<TableGroup, TableReferenceJoin> createTableGroupFinalizer(
SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext,
TableReference collectionTableReference,
TableReference elementAssociatedPrimaryTable,
SqlAstJoinType joinType,
ForeignKeyDescriptor elementFkDescriptor) {
return tableGroup -> {
final TableReferenceJoin associationJoin = new TableReferenceJoin(
joinType,
elementAssociatedPrimaryTable,
elementFkDescriptor.generateJoinPredicate(
elementAssociatedPrimaryTable,
collectionTableReference,
joinType,
sqlExpressionResolver,
creationContext
)
); );
return associationJoin; tableGroup.registerIndexTableGroup( tableGroupJoin );
}; }
} if ( elementDescriptor instanceof EntityCollectionPart ) {
final TableGroupJoin tableGroupJoin = ( (EntityCollectionPart) elementDescriptor ).createTableGroupJoin(
navigablePath.append( CollectionPart.Nature.ELEMENT.getName() ),
tableGroup,
null,
SqlAstJoinType.INNER,
fetched,
stem -> sqlAliasBase,
sqlExpressionResolver,
creationContext
);
tableGroup.registerElementTableGroup( tableGroupJoin );
}
private java.util.function.Predicate<String> createTableReferenceJoinNameChecker( return tableGroup;
EntityMappingType elementDescriptorEntityMappingType,
EntityMappingType indexDescriptorEntityMappingType) {
return tableExpression -> {
if ( elementDescriptorEntityMappingType != null
&& elementDescriptorEntityMappingType.containsTableReference( tableExpression ) ) {
return true;
}
if ( indexDescriptorEntityMappingType != null
&& indexDescriptorEntityMappingType.containsTableReference( tableExpression ) ) {
return true;
}
return false;
};
} }
@Override @Override
@ -1068,7 +691,7 @@ public class PluralAttributeMappingImpl
explicitSourceAlias, explicitSourceAlias,
additionalPredicateCollectorAccess, additionalPredicateCollectorAccess,
creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ), creationState.getSqlAliasBaseGenerator().createSqlAliasBase( getSqlAliasStem() ),
creationState, creationState.getSqlExpressionResolver(),
creationContext creationContext
); );
} }
@ -1080,7 +703,7 @@ public class PluralAttributeMappingImpl
String explicitSourceAlias, String explicitSourceAlias,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess, Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAliasBase sqlAliasBase, SqlAliasBase sqlAliasBase,
SqlAstCreationState creationState, SqlExpressionResolver expressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
if ( getCollectionDescriptor().isOneToMany() ) { if ( getCollectionDescriptor().isOneToMany() ) {
return createOneToManyTableGroup( return createOneToManyTableGroup(
@ -1089,7 +712,7 @@ public class PluralAttributeMappingImpl
false, false,
explicitSourceAlias, explicitSourceAlias,
sqlAliasBase, sqlAliasBase,
creationState.getSqlExpressionResolver(), expressionResolver,
creationContext creationContext
); );
} }
@ -1100,7 +723,7 @@ public class PluralAttributeMappingImpl
false, false,
explicitSourceAlias, explicitSourceAlias,
sqlAliasBase, sqlAliasBase,
creationState.getSqlExpressionResolver(), expressionResolver,
creationContext creationContext
); );
} }

View File

@ -19,6 +19,7 @@ import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.JdbcMapping; import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.mapping.ManagedMappingType;
import org.hibernate.metamodel.mapping.MappingType; import org.hibernate.metamodel.mapping.MappingType;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PropertyBasedMapping; import org.hibernate.metamodel.mapping.PropertyBasedMapping;
@ -37,8 +38,8 @@ import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupProducer;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -61,16 +62,18 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
private AssociationKey associationKey; private AssociationKey associationKey;
public SimpleForeignKeyDescriptor( public SimpleForeignKeyDescriptor(
ManagedMappingType keyDeclaringType,
BasicValuedModelPart keyModelPart, BasicValuedModelPart keyModelPart,
PropertyAccess keyPropertyAccess, PropertyAccess keyPropertyAccess,
SelectableMapping keySelectableMapping, SelectableMapping keySelectableMapping,
BasicValuedModelPart targetModelPart, BasicValuedModelPart targetModelPart,
boolean refersToPrimaryKey, boolean refersToPrimaryKey,
boolean hasConstraint) { boolean hasConstraint) {
this( keyModelPart, keyPropertyAccess, keySelectableMapping, targetModelPart, refersToPrimaryKey, hasConstraint, false ); this( keyDeclaringType, keyModelPart, keyPropertyAccess, keySelectableMapping, targetModelPart, refersToPrimaryKey, hasConstraint, false );
} }
public SimpleForeignKeyDescriptor( public SimpleForeignKeyDescriptor(
ManagedMappingType keyDeclaringType,
BasicValuedModelPart keyModelPart, BasicValuedModelPart keyModelPart,
PropertyAccess keyPropertyAccess, PropertyAccess keyPropertyAccess,
SelectableMapping keySelectableMapping, SelectableMapping keySelectableMapping,
@ -82,6 +85,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
assert targetModelPart != null; assert targetModelPart != null;
keyModelPart = BasicAttributeMapping.withSelectableMapping( keyModelPart = BasicAttributeMapping.withSelectableMapping(
keyDeclaringType,
keyModelPart, keyModelPart,
keyPropertyAccess, keyPropertyAccess,
NoValueGeneration.INSTANCE, NoValueGeneration.INSTANCE,
@ -131,9 +135,12 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override @Override
public ForeignKeyDescriptor withKeySelectionMapping( public ForeignKeyDescriptor withKeySelectionMapping(
ManagedMappingType declaringType,
TableGroupProducer declaringTableGroupProducer,
IntFunction<SelectableMapping> selectableMappingAccess, IntFunction<SelectableMapping> selectableMappingAccess,
MappingModelCreationProcess creationProcess) { MappingModelCreationProcess creationProcess) {
return new SimpleForeignKeyDescriptor( return new SimpleForeignKeyDescriptor(
declaringType,
keySide.getModelPart(), keySide.getModelPart(),
( (PropertyBasedMapping) keySide.getModelPart() ).getPropertyAccess(), ( (PropertyBasedMapping) keySide.getModelPart() ).getPropertyAccess(),
selectableMappingAccess.apply( 0 ), selectableMappingAccess.apply( 0 ),
@ -148,8 +155,6 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
NavigablePath navigablePath, NavigablePath navigablePath,
TableGroup tableGroup, TableGroup tableGroup,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
assert tableGroup.getTableReference( navigablePath, keySide.getModelPart().getContainingTableExpression() ) != null;
return createDomainResult( return createDomainResult(
navigablePath, navigablePath,
tableGroup, tableGroup,
@ -160,8 +165,6 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
@Override @Override
public DomainResult<?> createTargetDomainResult(NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) { public DomainResult<?> createTargetDomainResult(NavigablePath navigablePath, TableGroup tableGroup, DomainResultCreationState creationState) {
assert tableGroup.getTableReference( navigablePath, targetSide.getModelPart().getContainingTableExpression() ) != null;
return createDomainResult( return createDomainResult(
navigablePath, navigablePath,
tableGroup, tableGroup,
@ -210,7 +213,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver(); final SqlExpressionResolver sqlExpressionResolver = sqlAstCreationState.getSqlExpressionResolver();
final TableReference tableReference = tableGroup.resolveTableReference( final TableReference tableReference = tableGroup.resolveTableReference(
navigablePath.append( getNavigableRole().getNavigableName() ), navigablePath.append( getTargetPart().getFetchableName() ),
selectableMapping.getContainingTableExpression() selectableMapping.getContainingTableExpression()
); );
final String identificationVariable = tableReference.getIdentificationVariable(); final String identificationVariable = tableReference.getIdentificationVariable();
@ -269,11 +272,11 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
SqlAstJoinType sqlAstJoinType, SqlAstJoinType sqlAstJoinType,
SqlExpressionResolver sqlExpressionResolver, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
final TableReference lhsTableReference = targetSideTableGroup.getTableReference( final TableReference lhsTableReference = targetSideTableGroup.resolveTableReference(
targetSideTableGroup.getNavigablePath(), targetSideTableGroup.getNavigablePath(),
targetSide.getModelPart().getContainingTableExpression() targetSide.getModelPart().getContainingTableExpression()
); );
final TableReference rhsTableKeyReference = keySideTableGroup.getTableReference( final TableReference rhsTableKeyReference = keySideTableGroup.resolveTableReference(
keySide.getModelPart().getContainingTableExpression() keySide.getModelPart().getContainingTableExpression()
); );
@ -287,7 +290,7 @@ public class SimpleForeignKeyDescriptor implements ForeignKeyDescriptor, BasicVa
} }
protected TableReference getTableReference(TableGroup lhs, TableGroup tableGroup, String table) { protected TableReference getTableReference(TableGroup lhs, TableGroup tableGroup, String table) {
final NavigablePath navigablePath = lhs.getNavigablePath().append( getNavigableRole().getNavigableName() ); final NavigablePath navigablePath = lhs.getNavigablePath().append( getTargetPart().getFetchableName() );
if ( lhs.getPrimaryTableReference().getTableReference( navigablePath, table ) != null ) { if ( lhs.getPrimaryTableReference().getTableReference( navigablePath, table ) != null ) {
return lhs.getPrimaryTableReference(); return lhs.getPrimaryTableReference();
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.metamodel.mapping.internal; package org.hibernate.metamodel.mapping.internal;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -17,6 +18,7 @@ import org.hibernate.LockMode;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
import org.hibernate.engine.FetchTiming; import org.hibernate.engine.FetchTiming;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
@ -48,6 +50,7 @@ import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.property.access.spi.PropertyAccess; import org.hibernate.property.access.spi.PropertyAccess;
import org.hibernate.query.EntityIdentifierNavigablePath; import org.hibernate.query.EntityIdentifierNavigablePath;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.query.TreatedNavigablePath;
import org.hibernate.sql.ast.Clause; import org.hibernate.sql.ast.Clause;
import org.hibernate.sql.ast.SqlAstJoinType; import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.spi.FromClauseAccess; import org.hibernate.sql.ast.spi.FromClauseAccess;
@ -86,6 +89,7 @@ import org.hibernate.tuple.IdentifierProperty;
import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityMetamodel;
import org.hibernate.type.ComponentType; import org.hibernate.type.ComponentType;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
import org.hibernate.type.EntityType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
/** /**
@ -360,22 +364,50 @@ public class ToOneAttributeMapping
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded() if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
&& compositeType.getPropertyNames().length == 1 ) { && compositeType.getPropertyNames().length == 1 ) {
this.targetKeyPropertyName = compositeType.getPropertyNames()[0]; this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
addPrefixedPropertyNames( targetKeyPropertyNames, targetKeyPropertyName, compositeType.getSubtypes()[0] ); addPrefixedPropertyNames(
targetKeyPropertyNames,
targetKeyPropertyName,
compositeType.getSubtypes()[0],
declaringEntityPersister.getFactory()
);
} }
else { else {
this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME; this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME;
addPrefixedPropertyNames( targetKeyPropertyNames, null, propertyType ); addPrefixedPropertyNames(
targetKeyPropertyNames,
null,
propertyType,
declaringEntityPersister.getFactory()
);
addPrefixedPropertyNames(
targetKeyPropertyNames,
targetKeyPropertyName,
propertyType,
declaringEntityPersister.getFactory()
);
} }
} }
else { else {
this.targetKeyPropertyName = identifierProperty.getName(); this.targetKeyPropertyName = identifierProperty.getName();
addPrefixedPropertyNames( targetKeyPropertyNames, targetKeyPropertyName, propertyType ); addPrefixedPropertyNames(
targetKeyPropertyNames,
targetKeyPropertyName,
propertyType,
declaringEntityPersister.getFactory()
);
} }
this.targetKeyPropertyNames = targetKeyPropertyNames; this.targetKeyPropertyNames = targetKeyPropertyNames;
} }
else if ( bootValue.isReferenceToPrimaryKey() ) { else if ( bootValue.isReferenceToPrimaryKey() ) {
this.targetKeyPropertyName = referencedPropertyName; this.targetKeyPropertyName = referencedPropertyName;
this.targetKeyPropertyNames = Collections.singleton( targetKeyPropertyName ); final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
addPrefixedPropertyNames(
targetKeyPropertyNames,
targetKeyPropertyName,
bootValue.getType(),
declaringEntityPersister.getFactory()
);
this.targetKeyPropertyNames = targetKeyPropertyNames;
} }
else { else {
final EntityMetamodel entityMetamodel = entityMappingType.getEntityPersister().getEntityMetamodel(); final EntityMetamodel entityMetamodel = entityMappingType.getEntityPersister().getEntityMetamodel();
@ -389,18 +421,35 @@ public class ToOneAttributeMapping
} }
else { else {
this.targetKeyPropertyName = referencedPropertyName; this.targetKeyPropertyName = referencedPropertyName;
this.targetKeyPropertyNames = Collections.singleton( targetKeyPropertyName ); final String mapsIdAttributeName;
if ( ( mapsIdAttributeName = mapsId( entityMappingType, referencedPropertyName ) ) != null ) {
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
targetKeyPropertyNames.add( targetKeyPropertyName );
addPrefixedPropertyNames(
targetKeyPropertyNames,
mapsIdAttributeName,
entityMappingType.getEntityPersister().getIdentifierType(),
declaringEntityPersister.getFactory()
);
this.targetKeyPropertyNames = targetKeyPropertyNames;
}
else {
this.targetKeyPropertyNames = Collections.singleton( targetKeyPropertyName );
}
} }
} }
} }
private ToOneAttributeMapping(ToOneAttributeMapping original) { private ToOneAttributeMapping(
ToOneAttributeMapping original,
ManagedMappingType declaringType,
TableGroupProducer declaringTableGroupProducer) {
super( super(
original.getAttributeName(), original.getAttributeName(),
original.getStateArrayPosition(), original.getStateArrayPosition(),
original.getAttributeMetadataAccess(), original.getAttributeMetadataAccess(),
original, original,
original.getDeclaringType(), declaringType,
original.getPropertyAccess(), original.getPropertyAccess(),
original.getValueGeneration() original.getValueGeneration()
); );
@ -417,7 +466,7 @@ public class ToOneAttributeMapping
this.targetKeyPropertyNames = original.targetKeyPropertyNames; this.targetKeyPropertyNames = original.targetKeyPropertyNames;
this.cardinality = original.cardinality; this.cardinality = original.cardinality;
this.bidirectionalAttributeName = original.bidirectionalAttributeName; this.bidirectionalAttributeName = original.bidirectionalAttributeName;
this.declaringTableGroupProducer = original.declaringTableGroupProducer; this.declaringTableGroupProducer = declaringTableGroupProducer;
this.isConstrained = original.isConstrained; this.isConstrained = original.isConstrained;
} }
@ -438,10 +487,19 @@ public class ToOneAttributeMapping
return true; return true;
} }
private static void addPrefixedPropertyNames( static String mapsId(EntityMappingType entityMappingType, String referencedPropertyName) {
final AbstractEntityPersister persister = (AbstractEntityPersister) entityMappingType.getEntityPersister();
if ( Arrays.equals( persister.getKeyColumnNames(), persister.getPropertyColumnNames( referencedPropertyName ) ) ) {
return persister.getIdentifierPropertyName();
}
return null;
}
static void addPrefixedPropertyNames(
Set<String> targetKeyPropertyNames, Set<String> targetKeyPropertyNames,
String prefix, String prefix,
Type type) { Type type,
SessionFactoryImplementor factory) {
if ( prefix != null ) { if ( prefix != null ) {
targetKeyPropertyNames.add( prefix ); targetKeyPropertyNames.add( prefix );
} }
@ -457,13 +515,32 @@ public class ToOneAttributeMapping
else { else {
newPrefix = prefix + "." + propertyNames[i]; newPrefix = prefix + "." + propertyNames[i];
} }
addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, componentTypeSubtypes[i] ); addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, componentTypeSubtypes[i], factory );
} }
} }
else if ( type.isEntityType() ) {
final EntityType entityType = (EntityType) type;
final Type identifierOrUniqueKeyType = entityType.getIdentifierOrUniqueKeyType( factory );
final String propertyName;
if ( entityType.isReferenceToPrimaryKey() ) {
propertyName = entityType.getAssociatedEntityPersister( factory ).getIdentifierPropertyName();
}
else {
propertyName = entityType.getRHSUniqueKeyPropertyName();
}
final String newPrefix;
if ( prefix == null ) {
newPrefix = propertyName;
}
else {
newPrefix = prefix + "." + propertyName;
}
addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, identifierOrUniqueKeyType, factory );
}
} }
public ToOneAttributeMapping copy() { public ToOneAttributeMapping copy(ManagedMappingType declaringType, TableGroupProducer declaringTableGroupProducer) {
return new ToOneAttributeMapping( this ); return new ToOneAttributeMapping( this, declaringType, declaringTableGroupProducer );
} }
@Override @Override
@ -538,7 +615,7 @@ public class ToOneAttributeMapping
public ModelPart findSubPart(String name, EntityMappingType targetType) { public ModelPart findSubPart(String name, EntityMappingType targetType) {
// Prefer resolving the key part of the foreign key rather than the target part if possible // Prefer resolving the key part of the foreign key rather than the target part if possible
// This way, we don't have to register table groups the target entity type // This way, we don't have to register table groups the target entity type
if ( canUseParentTableGroup && name.equals( targetKeyPropertyName ) ) { if ( canUseParentTableGroup && targetKeyPropertyNames.contains( name ) ) {
return foreignKeyDescriptor.getKeyPart(); return foreignKeyDescriptor.getKeyPart();
} }
return EntityValuedFetchable.super.findSubPart( name, targetType ); return EntityValuedFetchable.super.findSubPart( name, targetType );
@ -606,7 +683,7 @@ public class ToOneAttributeMapping
fetchablePath, fetchablePath,
fetchParent, fetchParent,
parentNavigablePath, parentNavigablePath,
LockMode.READ creationState
); );
} }
@ -695,14 +772,17 @@ public class ToOneAttributeMapping
} }
} }
else if ( parentModelPart instanceof PluralAttributeMapping ) { else if ( parentModelPart instanceof PluralAttributeMapping ) {
return ( (PluralAttributeMapping) parentModelPart ).isBidirectionalAttributeName( fetchablePath, this ); // The parent must be non-null. If it is null, the root is a CollectionResult
return parentNavigablePath.getParent() != null
&& ( (PluralAttributeMapping) parentModelPart ).isBidirectionalAttributeName( fetchablePath, this );
} }
else if ( parentModelPart instanceof EntityCollectionPart ) { else if ( parentModelPart instanceof EntityCollectionPart ) {
NavigablePath parentOfParent = parentNavigablePath.getParent(); NavigablePath parentOfParent = parentNavigablePath.getParent();
if ( parentOfParent instanceof EntityIdentifierNavigablePath ) { if ( parentOfParent instanceof EntityIdentifierNavigablePath ) {
parentOfParent = parentOfParent.getParent(); parentOfParent = parentOfParent.getParent();
} }
return ( (PluralAttributeMapping) creationState.resolveModelPart( parentOfParent ) ) // The parent must be non-null. If it is null, the root is a CollectionResult
return parentOfParent.getParent() != null && ( (PluralAttributeMapping) creationState.resolveModelPart( parentOfParent ) )
.isBidirectionalAttributeName( fetchablePath, this ); .isBidirectionalAttributeName( fetchablePath, this );
} }
return false; return false;
@ -723,11 +803,18 @@ public class ToOneAttributeMapping
this.mappedBy = "biologicalChild" this.mappedBy = "biologicalChild"
parent.getFullPath() = "Mother.biologicalChild" parent.getFullPath() = "Mother.biologicalChild"
*/ */
final String fullPath = parentNavigablePath.getFullPath(); final NavigablePath grandparentNavigablePath = parentNavigablePath.getParent();
if ( fullPath.endsWith( bidirectionalAttributeName + "." + CollectionPart.Nature.ELEMENT.getName() ) ) { if ( parentNavigablePath.getUnaliasedLocalName().equals( CollectionPart.Nature.ELEMENT.getName() )
final NavigablePath parentPath = parentNavigablePath.getParent().getParent(); && grandparentNavigablePath != null
&& grandparentNavigablePath.getUnaliasedLocalName().equals( bidirectionalAttributeName ) ) {
final NavigablePath parentPath = grandparentNavigablePath.getParent();
// This can be null for a collection loader // This can be null for a collection loader
if ( parentPath != null ) { if ( parentPath == null ) {
return grandparentNavigablePath.getFullPath().equals(
entityMappingType.findSubPart( bidirectionalAttributeName ).getNavigableRole().getFullPath()
);
}
else {
// If the parent is null, this is a simple collection fetch of a root, in which case the types must match // If the parent is null, this is a simple collection fetch of a root, in which case the types must match
if ( parentPath.getParent() == null ) { if ( parentPath.getParent() == null ) {
final String entityName = entityMappingType.getPartName(); final String entityName = entityMappingType.getPartName();
@ -756,22 +843,83 @@ public class ToOneAttributeMapping
NavigablePath fetchablePath, NavigablePath fetchablePath,
FetchParent fetchParent, FetchParent fetchParent,
NavigablePath parentNavigablePath, NavigablePath parentNavigablePath,
LockMode lockMode) { DomainResultCreationState creationState) {
NavigablePath referencedNavigablePath; final NavigablePath referencedNavigablePath;
final boolean hasBidirectionalFetchParent;
FetchParent realFetchParent = fetchParent;
// Traverse up the embeddable fetches
while ( realFetchParent.getNavigablePath() != parentNavigablePath ) {
realFetchParent = ( (Fetch) fetchParent ).getFetchParent();
}
if ( parentNavigablePath.getParent() == null ) { if ( parentNavigablePath.getParent() == null ) {
referencedNavigablePath = parentNavigablePath; referencedNavigablePath = parentNavigablePath;
hasBidirectionalFetchParent = true;
}
else if ( CollectionPart.Nature.fromNameExact( parentNavigablePath.getUnaliasedLocalName() ) != null ) {
referencedNavigablePath = parentNavigablePath.getParent().getParent();
hasBidirectionalFetchParent = fetchParent instanceof Fetch
&& ( (Fetch) fetchParent ).getFetchParent() instanceof Fetch;
} }
else { else {
referencedNavigablePath = parentNavigablePath.getParent(); referencedNavigablePath = parentNavigablePath.getParent();
hasBidirectionalFetchParent = fetchParent instanceof Fetch;
}
// The referencedNavigablePath can be null if this is a collection initialization
if ( referencedNavigablePath != null ) {
if ( hasBidirectionalFetchParent ) {
return new CircularBiDirectionalFetchImpl(
FetchTiming.IMMEDIATE,
fetchablePath,
fetchParent,
this,
LockMode.READ,
referencedNavigablePath
);
}
else {
// A query like `select ch from Phone p join p.callHistory ch` returns collection element domain results
// but detects that Call#phone is bidirectional in the query.
// The problem with a bidirectional fetch though is that we can't find an initializer
// because there is none, as we don't fetch the data of the parent node.
// To avoid creating another join, we create a special join fetch that uses the existing joined data
final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess();
final TableGroup tableGroup = fromClauseAccess.getTableGroup( referencedNavigablePath );
fromClauseAccess.registerTableGroup( fetchablePath, tableGroup );
return new EntityFetchJoinedImpl(
fetchParent,
this,
tableGroup,
false,
fetchablePath,
creationState
);
}
}
else {
// We get here is this is a lazy collection initialization for which we know the owner is in the PC
// So we create a delayed fetch, as we are sure to find the entity in the PC
final FromClauseAccess fromClauseAccess = creationState.getSqlAstCreationState().getFromClauseAccess();
final NavigablePath realParent;
if ( CollectionPart.Nature.fromNameExact( parentNavigablePath.getUnaliasedLocalName() ) != null ) {
realParent = parentNavigablePath.getParent();
}
else {
realParent = parentNavigablePath;
}
final TableGroup tableGroup = fromClauseAccess.getTableGroup( realParent );
return new EntityDelayedFetchImpl(
fetchParent,
this,
fetchablePath,
foreignKeyDescriptor.createDomainResult(
fetchablePath,
tableGroup,
sideNature,
creationState
),
isSelectByUniqueKey( sideNature )
);
} }
return new CircularBiDirectionalFetchImpl(
FetchTiming.IMMEDIATE,
fetchablePath,
fetchParent,
this,
lockMode,
referencedNavigablePath
);
} }
@Override @Override
@ -791,7 +939,9 @@ public class ToOneAttributeMapping
); );
final NavigablePath parentNavigablePath = fetchablePath.getParent(); final NavigablePath parentNavigablePath = fetchablePath.getParent();
assert parentNavigablePath.equals( fetchParent.getNavigablePath() ); assert parentNavigablePath.equals( fetchParent.getNavigablePath() )
|| fetchParent.getNavigablePath() instanceof TreatedNavigablePath
&& parentNavigablePath.equals( fetchParent.getNavigablePath().getRealParent() );
if ( fetchTiming == FetchTiming.IMMEDIATE && selected ) { if ( fetchTiming == FetchTiming.IMMEDIATE && selected ) {
final TableGroup tableGroup; final TableGroup tableGroup;
@ -905,7 +1055,8 @@ public class ToOneAttributeMapping
fetchParent, fetchParent,
this, this,
fetchablePath, fetchablePath,
keyResult keyResult,
selectByUniqueKey
); );
} }
@ -917,7 +1068,10 @@ public class ToOneAttributeMapping
} }
else { else {
// case 1.1 // case 1.1
return bidirectionalAttributeName != null; // Make sure the entity identifier is not a target key property i.e. this really is a unique key mapping
return bidirectionalAttributeName != null && !targetKeyPropertyNames.contains(
entityMappingType.getEntityPersister().getEntityMetamodel().getIdentifierProperty().getName()
);
} }
} }
@ -930,19 +1084,20 @@ public class ToOneAttributeMapping
// We only need a join if the key is on the referring side i.e. this is an inverse to-one // We only need a join if the key is on the referring side i.e. this is an inverse to-one
// and if the FK refers to a non-PK, in which case we must load the whole entity // and if the FK refers to a non-PK, in which case we must load the whole entity
if ( sideNature == ForeignKeyDescriptor.Nature.TARGET || referencedPropertyName != null ) { if ( sideNature == ForeignKeyDescriptor.Nature.TARGET || referencedPropertyName != null ) {
final TableGroupJoin tableGroupJoin = createTableGroupJoin( creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
navigablePath, navigablePath,
tableGroup, np -> {
null, final TableGroupJoin tableGroupJoin = createTableGroupJoin(
getDefaultSqlAstJoinType( tableGroup ), navigablePath,
true, tableGroup,
creationState.getSqlAstCreationState() null,
); getDefaultSqlAstJoinType( tableGroup ),
tableGroup.addTableGroupJoin( tableGroupJoin ); true,
creationState.getSqlAstCreationState()
creationState.getSqlAstCreationState().getFromClauseAccess().registerTableGroup( );
navigablePath, tableGroup.addTableGroupJoin( tableGroupJoin );
tableGroupJoin.getJoinedGroup() return tableGroupJoin.getJoinedGroup();
}
); );
} }
if ( referencedPropertyName == null ) { if ( referencedPropertyName == null ) {
@ -955,12 +1110,15 @@ public class ToOneAttributeMapping
} }
else { else {
// We don't support proxies based on a non-PK yet, so we must fetch the whole entity // We don't support proxies based on a non-PK yet, so we must fetch the whole entity
return new EntityResultImpl( final EntityResultImpl entityResult = new EntityResultImpl(
navigablePath, navigablePath,
this, this,
tableGroup, null, tableGroup, null,
creationState creationState
); );
entityResult.afterInitialize( entityResult, creationState );
//noinspection unchecked
return entityResult;
} }
} }
@ -980,25 +1138,6 @@ public class ToOneAttributeMapping
} }
} }
private TableGroup createTableGroupJoin(
NavigablePath fetchablePath,
boolean fetched,
SqlAstJoinType sqlAstJoinType,
String sourceAlias,
DomainResultCreationState creationState,
TableGroup parentTableGroup) {
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
fetchablePath,
parentTableGroup,
sourceAlias,
sqlAstJoinType,
fetched,
creationState.getSqlAstCreationState()
);
return tableGroupJoin.getJoinedGroup();
}
@Override @Override
public int getNumberOfFetchables() { public int getNumberOfFetchables() {
return getEntityMappingType().getNumberOfFetchables(); return getEntityMappingType().getNumberOfFetchables();

View File

@ -401,7 +401,7 @@ public abstract class AbstractIdentifiableType<J>
else if ( nonAggregatedIdAttributes != null && ! nonAggregatedIdAttributes.isEmpty() ) { else if ( nonAggregatedIdAttributes != null && ! nonAggregatedIdAttributes.isEmpty() ) {
// non-aggregate composite id // non-aggregate composite id
if ( idClassType == null ) { if ( idClassType == null ) {
return new NonAggregatedCompositeSqmPathSource( return new NonAggregatedCompositeSqmPathSource<>(
EntityIdentifierMapping.ROLE_LOCAL_NAME, EntityIdentifierMapping.ROLE_LOCAL_NAME,
Bindable.BindableType.SINGULAR_ATTRIBUTE, Bindable.BindableType.SINGULAR_ATTRIBUTE,
this this

View File

@ -592,7 +592,7 @@ public class JpaMetamodelImpl implements JpaMetamodel, Serializable {
else { else {
javaTypeDescriptor = context.getTypeConfiguration() javaTypeDescriptor = context.getTypeConfiguration()
.getJavaTypeDescriptorRegistry() .getJavaTypeDescriptorRegistry()
.resolveManagedTypeDescriptor( javaType ); .resolveEntityTypeDescriptor( javaType );
} }
final EntityTypeImpl<?> entityType = new EntityTypeImpl( final EntityTypeImpl<?> entityType = new EntityTypeImpl(

View File

@ -16,11 +16,11 @@ import org.hibernate.query.sqm.tree.domain.SqmPath;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class NonAggregatedCompositeSqmPathSource extends AbstractSqmPathSource implements CompositeSqmPathSource { public class NonAggregatedCompositeSqmPathSource<J> extends AbstractSqmPathSource<J> implements CompositeSqmPathSource<J> {
public NonAggregatedCompositeSqmPathSource( public NonAggregatedCompositeSqmPathSource(
String localName, String localName,
BindableType bindableType, BindableType bindableType,
ManagedDomainType container) { ManagedDomainType<J> container) {
super( localName, container, bindableType ); super( localName, container, bindableType );
} }
@ -35,8 +35,8 @@ public class NonAggregatedCompositeSqmPathSource extends AbstractSqmPathSource i
} }
@Override @Override
public SqmPath createSqmPath(SqmPath lhs) { public SqmPath<J> createSqmPath(SqmPath<?> lhs) {
return new NonAggregatedCompositeSimplePath( return new NonAggregatedCompositeSimplePath<>(
lhs.getNavigablePath().append( getPathName() ), lhs.getNavigablePath().append( getPathName() ),
this, this,
lhs, lhs,

View File

@ -1171,7 +1171,7 @@ public abstract class AbstractCollectionPersister
null, null,
() -> p -> {}, () -> p -> {},
new SqlAliasBaseConstant( alias ), new SqlAliasBaseConstant( alias ),
sqlAstCreationState, sqlAstCreationState.getSqlExpressionResolver(),
getFactory() getFactory()
); );
@ -1828,7 +1828,7 @@ public abstract class AbstractCollectionPersister
buffer.append( " and " ); buffer.append( " and " );
} }
assert elementPersister instanceof Joinable; assert elementPersister instanceof Joinable;
final TableReference tableReference = tableGroup.getTableReference( ( (Joinable) elementPersister ).getTableName() ); final TableReference tableReference = tableGroup.resolveTableReference( ( (Joinable) elementPersister ).getTableName() );
buffer.append( StringHelper.replace( manyToManyWhereTemplate, Template.TEMPLATE, tableReference.getIdentificationVariable() ) ); buffer.append( StringHelper.replace( manyToManyWhereTemplate, Template.TEMPLATE, tableReference.getIdentificationVariable() ) );
} }

View File

@ -216,7 +216,6 @@ import org.hibernate.sql.ast.spi.SqlAliasBaseConstant;
import org.hibernate.sql.ast.spi.SqlAliasBaseManager; import org.hibernate.sql.ast.spi.SqlAliasBaseManager;
import org.hibernate.sql.ast.spi.SqlAliasStemHelper; import org.hibernate.sql.ast.spi.SqlAliasStemHelper;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.spi.SqlSelection; import org.hibernate.sql.ast.spi.SqlSelection;
import org.hibernate.sql.ast.tree.expression.AliasedExpression; import org.hibernate.sql.ast.tree.expression.AliasedExpression;
@ -1266,8 +1265,16 @@ public abstract class AbstractEntityPersister
TableGroup tableGroup, TableGroup tableGroup,
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
final EntityResultImpl entityResult = new EntityResultImpl(
navigablePath,
this,
tableGroup,
resultVariable,
creationState
);
entityResult.afterInitialize( entityResult, creationState );
//noinspection unchecked //noinspection unchecked
return new EntityResultImpl( navigablePath, this, tableGroup, resultVariable, creationState ); return entityResult;
} }
@Override @Override
@ -1313,10 +1320,8 @@ public abstract class AbstractEntityPersister
String explicitSourceAlias, String explicitSourceAlias,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess, Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAliasBase sqlAliasBase, SqlAliasBase sqlAliasBase,
SqlAstCreationState creationState, SqlExpressionResolver sqlExpressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver();
final TableReference primaryTableReference = createPrimaryTableReference( final TableReference primaryTableReference = createPrimaryTableReference(
sqlAliasBase, sqlAliasBase,
sqlExpressionResolver, sqlExpressionResolver,
@ -1905,7 +1910,7 @@ public abstract class AbstractEntityPersister
null, null,
() -> p -> {}, () -> p -> {},
new SqlAliasBaseConstant( alias ), new SqlAliasBaseConstant( alias ),
sqlAstCreationState, sqlAstCreationState.getSqlExpressionResolver(),
getFactory() getFactory()
); );
@ -6399,6 +6404,10 @@ public abstract class AbstractEntityPersister
} }
} }
protected EntityMappingType getSubclassMappingType(String subclassName) {
return subclassMappingTypes != null ? subclassMappingTypes.get( subclassName ) : null;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// EntityDefinition impl (walking model - deprecated) // EntityDefinition impl (walking model - deprecated)

View File

@ -8,6 +8,8 @@ package org.hibernate.persister.entity;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -54,12 +56,14 @@ import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.InFragment; import org.hibernate.sql.InFragment;
import org.hibernate.sql.Insert; import org.hibernate.sql.Insert;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression; import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate; import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate; import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult; import org.hibernate.sql.results.graph.DomainResult;
@ -1293,14 +1297,79 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
String resultVariable, String resultVariable,
DomainResultCreationState creationState) { DomainResultCreationState creationState) {
if ( hasSubclasses() ) { if ( hasSubclasses() ) {
final EntityResultJoinedSubclassImpl entityResultJoinedSubclass = new EntityResultJoinedSubclassImpl(
navigablePath,
this,
tableGroup,
resultVariable,
creationState
);
entityResultJoinedSubclass.afterInitialize( entityResultJoinedSubclass, creationState );
//noinspection unchecked //noinspection unchecked
return new EntityResultJoinedSubclassImpl( navigablePath, this, tableGroup, resultVariable, creationState ); return entityResultJoinedSubclass;
} }
else { else {
return super.createDomainResult( navigablePath, tableGroup, resultVariable, creationState ); return super.createDomainResult( navigablePath, tableGroup, resultVariable, creationState );
} }
} }
@Override
public void pruneForSubclasses(TableGroup tableGroup, Set<String> treatedEntityNames) {
if ( treatedEntityNames.contains( getEntityName() ) ) {
return;
}
final Set<TableReference> retainedTableReferences = new HashSet<>( treatedEntityNames.size() );
final Set<String> sharedSuperclassTables = new HashSet<>();
for ( String treatedEntityName : treatedEntityNames ) {
final JoinedSubclassEntityPersister subPersister = (JoinedSubclassEntityPersister) getSubclassMappingType( treatedEntityName );
final String[] subclassTableNames = subPersister.getSubclassTableNames();
if ( tableGroup.canUseInnerJoins() ) {
if ( sharedSuperclassTables.isEmpty() ) {
for ( int i = 0; i < subclassTableNames.length; i++ ) {
if ( subPersister.isClassOrSuperclassTable[i] ) {
sharedSuperclassTables.add( subclassTableNames[i] );
}
}
}
else {
sharedSuperclassTables.retainAll( Arrays.asList( subclassTableNames ) );
}
}
// todo (6.0): no need to resolve all table references, only the ones needed for cardinality
for ( int i = 0; i < subclassTableNames.length; i++ ) {
retainedTableReferences.add( tableGroup.resolveTableReference( null, subclassTableNames[i], false ) );
}
}
final List<TableReferenceJoin> tableReferenceJoins = tableGroup.getTableReferenceJoins();
if ( sharedSuperclassTables.isEmpty() ) {
tableReferenceJoins
.removeIf( join -> !retainedTableReferences.contains( join.getJoinedTableReference() ) );
}
else {
final TableReferenceJoin[] oldJoins = tableReferenceJoins.toArray( new TableReferenceJoin[0] );
tableReferenceJoins.clear();
for ( TableReferenceJoin oldJoin : oldJoins ) {
final TableReference joinedTableReference = oldJoin.getJoinedTableReference();
if ( retainedTableReferences.contains( joinedTableReference ) ) {
if ( oldJoin.getJoinType() != SqlAstJoinType.INNER
&& sharedSuperclassTables.contains( joinedTableReference.getTableExpression() ) ) {
tableReferenceJoins.add(
new TableReferenceJoin(
SqlAstJoinType.INNER,
joinedTableReference,
oldJoin.getPredicate()
)
);
}
else {
tableReferenceJoins.add( oldJoin );
}
}
}
}
}
@Override @Override
public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) { public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) {
for ( int i = 0; i < constraintOrderedTableNames.length; i++ ) { for ( int i = 0; i < constraintOrderedTableNames.length; i++ ) {

View File

@ -47,12 +47,12 @@ import org.hibernate.sql.InFragment;
import org.hibernate.sql.Insert; import org.hibernate.sql.Insert;
import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.QueryLiteral; import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate; import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.InListPredicate; import org.hibernate.sql.ast.tree.predicate.InListPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction; import org.hibernate.sql.ast.tree.predicate.Junction;
@ -654,7 +654,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
if ( !queryable.isAbstract() ) { if ( !queryable.isAbstract() ) {
values.add( queryable.getDiscriminatorSQLValue() ); values.add( queryable.getDiscriminatorSQLValue() );
} }
else if ( queryable.hasSubclasses() ) { if ( queryable.hasSubclasses() ) {
// if the treat is an abstract class, add the concrete implementations to values if any // if the treat is an abstract class, add the concrete implementations to values if any
Set<String> actualSubClasses = queryable.getEntityMetamodel().getSubclassEntityNames(); Set<String> actualSubClasses = queryable.getEntityMetamodel().getSubclassEntityNames();
@ -843,7 +843,7 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
String explicitSourceAlias, String explicitSourceAlias,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess, Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAliasBase sqlAliasBase, SqlAliasBase sqlAliasBase,
SqlAstCreationState creationState, SqlExpressionResolver expressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
final TableGroup tableGroup = super.createRootTableGroup( final TableGroup tableGroup = super.createRootTableGroup(
canUseInnerJoins, canUseInnerJoins,
@ -851,14 +851,14 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
explicitSourceAlias, explicitSourceAlias,
additionalPredicateCollectorAccess, additionalPredicateCollectorAccess,
sqlAliasBase, sqlAliasBase,
creationState, expressionResolver,
creationContext creationContext
); );
if ( additionalPredicateCollectorAccess != null && needsDiscriminator() ) { if ( additionalPredicateCollectorAccess != null && needsDiscriminator() ) {
final Predicate discriminatorPredicate = createDiscriminatorPredicate( final Predicate discriminatorPredicate = createDiscriminatorPredicate(
tableGroup, tableGroup,
creationState.getSqlExpressionResolver(), expressionResolver,
creationContext creationContext
); );
additionalPredicateCollectorAccess.get().accept( discriminatorPredicate ); additionalPredicateCollectorAccess.get().accept( discriminatorPredicate );
@ -952,6 +952,17 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
); );
} }
@Override
public void pruneForSubclasses(TableGroup tableGroup, Set<String> treatedEntityNames) {
if ( treatedEntityNames.contains( getEntityName() ) ) {
return;
}
final TableReference tableReference = tableGroup.getPrimaryTableReference();
tableReference.setPrunedTableExpression(
"(select * from " + getTableName() + " t where " + discriminatorFilterFragment( "t", treatedEntityNames ) + ")"
);
}
@Override @Override
public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) { public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) {
for ( int i = 0; i < constraintOrderedTableNames.length; i++ ) { for ( int i = 0; i < constraintOrderedTableNames.length; i++ ) {

View File

@ -12,6 +12,7 @@ import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -42,12 +43,14 @@ import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Subclass; import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess; import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.persister.spi.PersisterCreationContext; import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAliasBase; import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.spi.SqlAstCreationContext; import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.UnionTableGroup; import org.hibernate.sql.ast.tree.from.UnionTableGroup;
@ -251,7 +254,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
String explicitSourceAlias, String explicitSourceAlias,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess, Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAliasBase sqlAliasBase, SqlAliasBase sqlAliasBase,
SqlAstCreationState creationState, SqlExpressionResolver expressionResolver,
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
final TableReference tableReference = resolvePrimaryTableReference( sqlAliasBase ); final TableReference tableReference = resolvePrimaryTableReference( sqlAliasBase );
@ -390,6 +393,14 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return isAbstract() || hasSubclasses(); return isAbstract() || hasSubclasses();
} }
@Override
public void pruneForSubclasses(TableGroup tableGroup, Set<String> treatedEntityNames) {
if ( treatedEntityNames.contains( getEntityName() ) ) {
return;
}
final TableReference tableReference = tableGroup.resolveTableReference( getRootTableName() );
tableReference.setPrunedTableExpression( generateSubquery( treatedEntityNames ) );
}
@Override @Override
public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) { public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) {
@ -509,6 +520,68 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
return buf.append( " )" ).toString(); return buf.append( " )" ).toString();
} }
protected String generateSubquery(Set<String> treated) {
if ( !hasSubclasses() ) {
return getTableName();
}
final Dialect dialect = getFactory().getJdbcServices().getDialect();
final LinkedHashMap<String, Map<String, SelectableMapping>> selectables = new LinkedHashMap<>();
visitSubTypeAttributeMappings(
attributeMapping -> attributeMapping.forEachSelectable(
(i, selectable) -> selectables.computeIfAbsent( selectable.getSelectionExpression(), k -> new HashMap<>() )
.put( selectable.getContainingTableExpression(), selectable )
)
);
final Set<String> treatedTableNames = new HashSet<>( treated.size() );
for ( String subclassName : treated ) {
final UnionSubclassEntityPersister subPersister = (UnionSubclassEntityPersister) getSubclassMappingType( subclassName );
for ( String subclassTableName : subPersister.getSubclassTableNames() ) {
if ( ArrayHelper.indexOf( subclassSpaces, subclassTableName ) != -1 ) {
treatedTableNames.add( subclassTableName );
}
}
}
final StringBuilder buf = new StringBuilder( subquery.length() )
.append( "( " );
for ( int i = 0; i < subclassTableNames.length; i++ ) {
final String subclassTableName = subclassTableNames[i];
if ( treatedTableNames.contains( subclassTableName ) ) {
buf.append( "select " );
for ( Map<String, SelectableMapping> selectableMappings : selectables.values() ) {
SelectableMapping selectableMapping = selectableMappings.get( subclassTableName );
if ( selectableMapping == null ) {
selectableMapping = selectableMappings.values().iterator().next();
final int sqlType = selectableMapping.getJdbcMapping().getJdbcTypeDescriptor()
.getDefaultSqlTypeCode();
buf.append( dialect.getSelectClauseNullString( sqlType ) )
.append( " as " );
}
buf.append(
new ColumnReference( (String) null, selectableMapping, getFactory() ).getExpressionText()
);
buf.append( ", " );
}
buf.append( i ).append( " as clazz_" );
buf.append( " from " ).append( subclassTableName );
buf.append( " union " );
if ( dialect.supportsUnionAll() ) {
buf.append( "all " );
}
}
}
if ( buf.length() > 2 ) {
//chop the last union (all)
buf.setLength( buf.length() - ( dialect.supportsUnionAll() ? 11 : 7 ) );
}
return buf.append( " )" ).toString();
}
@Override @Override
protected String[] getSubclassTableKeyColumns(int j) { protected String[] getSubclassTableKeyColumns(int j) {
if ( j != 0 ) { if ( j != 0 ) {

View File

@ -88,6 +88,7 @@ public class ProcedureParameterImpl<T> extends AbstractQueryParameter<T> impleme
return javaType; return javaType;
} }
@Override
public NamedCallableQueryMemento.ParameterMemento toMemento() { public NamedCallableQueryMemento.ParameterMemento toMemento() {
return session -> { return session -> {
if ( getName() != null ) { if ( getName() != null ) {

View File

@ -101,6 +101,25 @@ public class NavigablePath implements DotIdentifierSequence, Serializable {
this( "" ); this( "" );
} }
NavigablePath(
NavigablePath parent,
String fullPath,
String unaliasedLocalName,
String identifierForTableGroup) {
this.parent = parent;
this.fullPath = fullPath;
this.unaliasedLocalName = unaliasedLocalName;
this.identifierForTableGroup = identifierForTableGroup;
}
public NavigablePath treatAs(String entityName) {
return new TreatedNavigablePath( this, entityName );
}
public NavigablePath treatAs(String entityName, String alias) {
return new TreatedNavigablePath( this, entityName, alias );
}
public NavigablePath append(String property) { public NavigablePath append(String property) {
return new NavigablePath( this, property ); return new NavigablePath( this, property );
} }

View File

@ -11,20 +11,39 @@ package org.hibernate.query;
*/ */
public class TreatedNavigablePath extends NavigablePath { public class TreatedNavigablePath extends NavigablePath {
public static final String ROLE_LOCAL_NAME = "{treated}";
public TreatedNavigablePath(NavigablePath parent, String entityTypeName) { public TreatedNavigablePath(NavigablePath parent, String entityTypeName) {
super( parent, ROLE_LOCAL_NAME, entityTypeName ); this( parent, entityTypeName, null );
}
public TreatedNavigablePath(NavigablePath parent, String entityTypeName, String alias) {
super(
parent,
alias == null ? "treat(" + parent.getFullPath() + " as " + entityTypeName + ")"
: "treat(" + parent.getFullPath() + " as " + entityTypeName + ")(" + alias + ")",
entityTypeName,
"treat(" + parent.getFullPath() + " as " + entityTypeName + ")"
);
assert !( parent instanceof TreatedNavigablePath );
}
@Override
public NavigablePath treatAs(String entityName) {
return new TreatedNavigablePath( getRealParent(), entityName );
}
@Override
public NavigablePath treatAs(String entityName, String alias) {
return new TreatedNavigablePath( getRealParent(), entityName, alias );
} }
@Override @Override
public String getLocalName() { public String getLocalName() {
return ROLE_LOCAL_NAME; return getUnaliasedLocalName();
} }
@Override @Override
public int hashCode() { public int hashCode() {
return getParent().getFullPath().hashCode(); return getFullPath().hashCode();
} }
@Override @Override

View File

@ -6,6 +6,10 @@
*/ */
package org.hibernate.query.criteria; package org.hibernate.query.criteria;
import java.util.Collection;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import jakarta.persistence.criteria.CollectionJoin; import jakarta.persistence.criteria.CollectionJoin;
import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
@ -15,7 +19,7 @@ import jakarta.persistence.criteria.Predicate;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface JpaCollectionJoin<O, T> extends JpaJoin<O, T>, CollectionJoin<O, T> { public interface JpaCollectionJoin<O, T> extends JpaPluralJoin<O, Collection<T>, T>, CollectionJoin<O, T> {
@Override @Override
JpaCollectionJoin<O, T> on(JpaExpression<Boolean> restriction); JpaCollectionJoin<O, T> on(JpaExpression<Boolean> restriction);
@ -31,4 +35,7 @@ public interface JpaCollectionJoin<O, T> extends JpaJoin<O, T>, CollectionJoin<O
@Override @Override
<S extends T> JpaCollectionJoin<O, S> treatAs(Class<S> treatAsType); <S extends T> JpaCollectionJoin<O, S> treatAs(Class<S> treatAsType);
@Override
<S extends T> JpaCollectionJoin<O, S> treatAs(EntityDomainType<S> treatAsType);
} }

View File

@ -11,6 +11,7 @@ import jakarta.persistence.criteria.Fetch;
import jakarta.persistence.criteria.Join; import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute; import org.hibernate.metamodel.model.domain.PersistentAttribute;
/** /**
@ -32,4 +33,10 @@ public interface JpaJoin<O, T> extends JpaJoinedFrom<O, T>, Join<O, T> {
@Override @Override
JpaJoin<O, T> on(Predicate... restrictions); JpaJoin<O, T> on(Predicate... restrictions);
@Override
<S extends T> JpaJoin<O, S> treatAs(Class<S> treatAsType);
@Override
<S extends T> JpaJoin<O, S> treatAs(EntityDomainType<S> treatAsType);
} }

View File

@ -6,6 +6,10 @@
*/ */
package org.hibernate.query.criteria; package org.hibernate.query.criteria;
import java.util.List;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.ListJoin; import jakarta.persistence.criteria.ListJoin;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
@ -15,7 +19,7 @@ import jakarta.persistence.criteria.Predicate;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface JpaListJoin<O, T> extends JpaJoin<O, T>, ListJoin<O, T> { public interface JpaListJoin<O, T> extends JpaPluralJoin<O, List<T>, T>, ListJoin<O, T> {
@Override @Override
JpaListJoin<O, T> on(JpaExpression<Boolean> restriction); JpaListJoin<O, T> on(JpaExpression<Boolean> restriction);
@ -30,4 +34,7 @@ public interface JpaListJoin<O, T> extends JpaJoin<O, T>, ListJoin<O, T> {
@Override @Override
<S extends T> JpaListJoin<O, S> treatAs(Class<S> treatAsType); <S extends T> JpaListJoin<O, S> treatAs(Class<S> treatAsType);
@Override
<S extends T> JpaListJoin<O, S> treatAs(EntityDomainType<S> treatAsType);
} }

View File

@ -6,6 +6,11 @@
*/ */
package org.hibernate.query.criteria; package org.hibernate.query.criteria;
import java.util.Map;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.PathException;
import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.MapJoin; import jakarta.persistence.criteria.MapJoin;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
@ -15,7 +20,7 @@ import jakarta.persistence.criteria.Predicate;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface JpaMapJoin<O,K,V> extends JpaJoin<O,V>, MapJoin<O,K,V> { public interface JpaMapJoin<O,K,V> extends JpaPluralJoin<O, Map<K, V>, V>, MapJoin<O,K,V> {
@Override @Override
JpaMapJoin<O, K, V> on(JpaExpression<Boolean> restriction); JpaMapJoin<O, K, V> on(JpaExpression<Boolean> restriction);
@ -29,4 +34,7 @@ public interface JpaMapJoin<O,K,V> extends JpaJoin<O,V>, MapJoin<O,K,V> {
JpaMapJoin<O, K, V> on(Predicate... restrictions); JpaMapJoin<O, K, V> on(Predicate... restrictions);
<S extends V> JpaMapJoin<O, K, S> treatAs(Class<S> treatAsType); <S extends V> JpaMapJoin<O, K, S> treatAs(Class<S> treatAsType);
@Override
<S extends V> JpaMapJoin<O, K, S> treatAs(EntityDomainType<S> treatJavaType);
} }

View File

@ -37,12 +37,12 @@ public interface JpaPath<T> extends JpaExpression<T>, Path<T> {
/** /**
* Support for JPA's explicit (TREAT) down-casting. * Support for JPA's explicit (TREAT) down-casting.
*/ */
<S extends T> JpaPath<S> treatAs(Class<S> treatJavaType) throws PathException; <S extends T> JpaPath<S> treatAs(Class<S> treatJavaType);
/** /**
* Support for JPA's explicit (TREAT) down-casting. * Support for JPA's explicit (TREAT) down-casting.
*/ */
<S extends T> JpaPath<S> treatAs(EntityDomainType<S> treatJavaType) throws PathException; <S extends T> JpaPath<S> treatAs(EntityDomainType<S> treatJavaType);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,36 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query.criteria;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.PluralJoin;
import jakarta.persistence.criteria.Predicate;
/**
* Specialization of {@link JpaJoin} for {@link java.util.Set} typed attribute joins
*
* @author Steve Ebersole
*/
public interface JpaPluralJoin<O, C, E> extends JpaJoin<O, E>, PluralJoin<O, C, E> {
@Override
PluralPersistentAttribute<? super O, C, E> getAttribute();
JpaPluralJoin<O, ? extends C, E> on(JpaExpression<Boolean> restriction);
JpaPluralJoin<O, ? extends C, E> on(Expression<Boolean> restriction);
JpaPluralJoin<O, ? extends C, E> on(JpaPredicate... restrictions);
JpaPluralJoin<O, ? extends C, E> on(Predicate... restrictions);
<S extends E> JpaPluralJoin<O, ?, S> treatAs(Class<S> treatAsType);
<S extends E> JpaPluralJoin<O, ?, S> treatAs(EntityDomainType<S> treatAsType);
}

View File

@ -6,6 +6,11 @@
*/ */
package org.hibernate.query.criteria; package org.hibernate.query.criteria;
import java.util.Set;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import jakarta.persistence.criteria.Expression; import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.SetJoin; import jakarta.persistence.criteria.SetJoin;
@ -15,7 +20,7 @@ import jakarta.persistence.criteria.SetJoin;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface JpaSetJoin<O, T> extends JpaJoin<O, T>, SetJoin<O, T> { public interface JpaSetJoin<O, T> extends JpaPluralJoin<O, Set<T>, T>, SetJoin<O, T> {
JpaSetJoin<O, T> on(JpaExpression<Boolean> restriction); JpaSetJoin<O, T> on(JpaExpression<Boolean> restriction);
@ -26,4 +31,6 @@ public interface JpaSetJoin<O, T> extends JpaJoin<O, T>, SetJoin<O, T> {
JpaSetJoin<O, T> on(Predicate... restrictions); JpaSetJoin<O, T> on(Predicate... restrictions);
<S extends T> JpaSetJoin<O, S> treatAs(Class<S> treatAsType); <S extends T> JpaSetJoin<O, S> treatAs(Class<S> treatAsType);
<S extends T> JpaSetJoin<O, S> treatAs(EntityDomainType<S> treatAsType);
} }

View File

@ -10,6 +10,7 @@ import java.lang.reflect.Field;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.criteria.JpaPath;
import org.hibernate.query.hql.HqlLogging; import org.hibernate.query.hql.HqlLogging;
import org.hibernate.query.hql.spi.DotIdentifierConsumer; import org.hibernate.query.hql.spi.DotIdentifierConsumer;
import org.hibernate.query.hql.spi.SemanticPathPart; import org.hibernate.query.hql.spi.SemanticPathPart;
@ -93,6 +94,13 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
currentPart = currentPart.resolvePathPart( identifier, isTerminal, creationState ); currentPart = currentPart.resolvePathPart( identifier, isTerminal, creationState );
} }
@Override
public void consumeTreat(String entityName, boolean isTerminal) {
final EntityDomainType<?> entityDomainType = creationState.getCreationContext().getJpaMetamodel()
.entity( entityName );
currentPart = ( (SqmPath) currentPart ).treatAs( entityDomainType );
}
protected void reset() { protected void reset() {
pathSoFar = null; pathSoFar = null;
currentPart = createBasePart(); currentPart = createBasePart();
@ -249,13 +257,13 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
throw new ParsingException( "Could not interpret dot-ident : " + pathSoFar ); throw new ParsingException( "Could not interpret dot-ident : " + pathSoFar );
} }
protected void validateAsRoot(SqmFrom pathRoot) { protected void validateAsRoot(SqmFrom<?, ?> pathRoot) {
} }
@Override @Override
public SqmPath resolveIndexedAccess( public SqmPath<?> resolveIndexedAccess(
SqmExpression selector, SqmExpression<?> selector,
boolean isTerminal, boolean isTerminal,
SqmCreationState processingState) { SqmCreationState processingState) {
return currentPart.resolveIndexedAccess( selector, isTerminal, processingState ); return currentPart.resolveIndexedAccess( selector, isTerminal, processingState );

View File

@ -144,8 +144,8 @@ public class DomainPathPart implements SemanticPathPart {
} }
@Override @Override
public SqmPath resolveIndexedAccess( public SqmPath<?> resolveIndexedAccess(
SqmExpression selector, SqmExpression<?> selector,
boolean isTerminal, boolean isTerminal,
SqmCreationState creationState) { SqmCreationState creationState) {
throw new NotYetImplementedFor6Exception( getClass() ); throw new NotYetImplementedFor6Exception( getClass() );

View File

@ -41,7 +41,7 @@ public class FullyQualifiedReflectivePath implements SemanticPathPart, FullyQual
@Override @Override
public SqmPath<?> resolveIndexedAccess( public SqmPath<?> resolveIndexedAccess(
SqmExpression selector, SqmExpression<?> selector,
boolean isTerminal, boolean isTerminal,
SqmCreationState creationState) { SqmCreationState creationState) {
throw new UnsupportedOperationException( "Fully qualified reflective paths cannot contain indexed access" ); throw new UnsupportedOperationException( "Fully qualified reflective paths cannot contain indexed access" );

View File

@ -44,6 +44,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
private final String alias; private final String alias;
private ConsumerDelegate delegate; private ConsumerDelegate delegate;
private boolean treated;
public QualifiedJoinPathConsumer( public QualifiedJoinPathConsumer(
SqmRoot<?> sqmRoot, SqmRoot<?> sqmRoot,
@ -78,6 +79,10 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
); );
} }
public void setTreated(boolean treated) {
this.treated = treated;
}
@Override @Override
public SemanticPathPart getConsumedPart() { public SemanticPathPart getConsumedPart() {
return delegate.getConsumedPart(); return delegate.getConsumedPart();
@ -87,14 +92,20 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
public void consumeIdentifier(String identifier, boolean isBase, boolean isTerminal) { public void consumeIdentifier(String identifier, boolean isBase, boolean isTerminal) {
if ( isBase ) { if ( isBase ) {
assert delegate == null; assert delegate == null;
delegate = resolveBase( identifier, isTerminal ); delegate = resolveBase( identifier, !treated && isTerminal );
} }
else { else {
assert delegate != null; assert delegate != null;
delegate.consumeIdentifier( identifier, isTerminal ); delegate.consumeIdentifier( identifier, !treated && isTerminal );
} }
} }
@Override
public void consumeTreat(String entityName, boolean isTerminal) {
assert delegate != null;
delegate.consumeTreat( entityName, isTerminal );
}
private ConsumerDelegate resolveBase(String identifier, boolean isTerminal) { private ConsumerDelegate resolveBase(String identifier, boolean isTerminal) {
final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState(); final SqmCreationProcessingState processingState = creationState.getCurrentProcessingState();
final SqmPathRegistry pathRegistry = processingState.getPathRegistry(); final SqmPathRegistry pathRegistry = processingState.getPathRegistry();
@ -187,6 +198,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
private interface ConsumerDelegate { private interface ConsumerDelegate {
void consumeIdentifier(String identifier, boolean isTerminal); void consumeIdentifier(String identifier, boolean isTerminal);
void consumeTreat(String entityName, boolean isTerminal);
SemanticPathPart getConsumedPart(); SemanticPathPart getConsumedPart();
} }
@ -226,6 +238,14 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
); );
} }
@Override
public void consumeTreat(String entityName, boolean isTerminal) {
final EntityDomainType<?> entityDomainType = creationState.getCreationContext().getJpaMetamodel()
.entity( entityName );
currentPath = currentPath.treatAs( entityDomainType, isTerminal ? alias : null );
creationState.getCurrentProcessingState().getPathRegistry().register( currentPath );
}
@Override @Override
public SemanticPathPart getConsumedPart() { public SemanticPathPart getConsumedPart() {
return currentPath; return currentPath;
@ -240,7 +260,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
private final boolean fetch; private final boolean fetch;
private final String alias; private final String alias;
private NavigablePath path = new NavigablePath(); private final StringBuilder path = new StringBuilder();
private SqmEntityJoin<?> join; private SqmEntityJoin<?> join;
@ -263,14 +283,17 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
@Override @Override
public void consumeIdentifier(String identifier, boolean isTerminal) { public void consumeIdentifier(String identifier, boolean isTerminal) {
path = path.append( identifier ); if ( path.length() != 0 ) {
path.append( '.' );
}
path.append( identifier );
if ( isTerminal ) { if ( isTerminal ) {
final String fullPath = path.toString();
final EntityDomainType<?> joinedEntityType = creationState.getCreationContext() final EntityDomainType<?> joinedEntityType = creationState.getCreationContext()
.getJpaMetamodel() .getJpaMetamodel()
.resolveHqlEntityReference( path.getFullPath() ); .resolveHqlEntityReference( fullPath );
if ( joinedEntityType == null ) { if ( joinedEntityType == null ) {
throw new SemanticException( "Could not resolve join path - " + path.getFullPath() ); throw new SemanticException( "Could not resolve join path - " + fullPath );
} }
assert ! ( joinedEntityType instanceof SqmPolymorphicRootDescriptor ); assert ! ( joinedEntityType instanceof SqmPolymorphicRootDescriptor );
@ -284,6 +307,11 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
} }
} }
@Override
public void consumeTreat(String entityName, boolean isTerminal) {
throw new UnsupportedOperationException();
}
@Override @Override
public SemanticPathPart getConsumedPart() { public SemanticPathPart getConsumedPart() {
return join; return join;

View File

@ -11,8 +11,12 @@ import java.util.Locale;
import org.hibernate.query.SemanticException; import org.hibernate.query.SemanticException;
import org.hibernate.query.hql.spi.SemanticPathPart; import org.hibernate.query.hql.spi.SemanticPathPart;
import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.tree.SqmQuery;
import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin; import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
import org.hibernate.query.sqm.tree.select.SqmSubQuery;
/** /**
* Specialized consumer for processing domain model paths occurring as part * Specialized consumer for processing domain model paths occurring as part
@ -34,8 +38,19 @@ public class QualifiedJoinPredicatePathConsumer extends BasicDotIdentifierConsum
protected SemanticPathPart createBasePart() { protected SemanticPathPart createBasePart() {
return new BaseLocalSequencePart() { return new BaseLocalSequencePart() {
@Override @Override
protected void validateAsRoot(SqmFrom pathRoot) { protected void validateAsRoot(SqmFrom<?, ?> pathRoot) {
if ( pathRoot.findRoot() != sqmJoin.findRoot() ) { final SqmRoot<?> root = pathRoot.findRoot();
if ( root != sqmJoin.findRoot() ) {
final SqmQuery<?> processingQuery = getCreationState().getCurrentProcessingState().getProcessingQuery();
if ( processingQuery instanceof SqmSubQuery<?> ) {
final SqmQuerySpec<?> querySpec = ( (SqmSubQuery<?>) processingQuery ).getQuerySpec();
// If this "foreign" from element is used in a sub query
// This is only an error if the from element is actually part of the sub query
if ( querySpec.getFromClause() == null || !querySpec.getFromClause().getRoots().contains( root ) ) {
super.validateAsRoot( pathRoot );
return;
}
}
throw new SemanticException( throw new SemanticException(
String.format( String.format(
Locale.ROOT, Locale.ROOT,

View File

@ -272,8 +272,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
private final Stack<DotIdentifierConsumer> dotIdentifierConsumerStack; private final Stack<DotIdentifierConsumer> dotIdentifierConsumerStack;
private final Stack<TreatHandler> treatHandlerStack = new StandardStack<>( new TreatHandlerNormal() );
private final Stack<ParameterDeclarationContext> parameterDeclarationContextStack = new StandardStack<>(); private final Stack<ParameterDeclarationContext> parameterDeclarationContextStack = new StandardStack<>();
private final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>(); private final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>();
@ -282,12 +280,16 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
private final JavaType<Map<?,?>> mapJavaTypeDescriptor; private final JavaType<Map<?,?>> mapJavaTypeDescriptor;
private ParameterCollector parameterCollector; private ParameterCollector parameterCollector;
private ParameterStyle parameterStyle;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public SemanticQueryBuilder(SqmCreationOptions creationOptions, SqmCreationContext creationContext) { public SemanticQueryBuilder(SqmCreationOptions creationOptions, SqmCreationContext creationContext) {
this.creationOptions = creationOptions; this.creationOptions = creationOptions;
this.creationContext = creationContext; this.creationContext = creationContext;
this.dotIdentifierConsumerStack = new StandardStack<>( new BasicDotIdentifierConsumer( this ) ); this.dotIdentifierConsumerStack = new StandardStack<>( new BasicDotIdentifierConsumer( this ) );
this.parameterStyle = creationOptions.useStrictJpaCompliance()
? ParameterStyle.UNKNOWN
: ParameterStyle.MIXED;
this.integerDomainType = creationContext this.integerDomainType = creationContext
.getNodeBuilder() .getNodeBuilder()
@ -787,13 +789,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
} }
// visit from-clause first!!! // visit from-clause first!!!
treatHandlerStack.push( new TreatHandlerFromClause() ); sqmQuerySpec.setFromClause( visitFromClause( (HqlParser.FromClauseContext) ctx.getChild( fromIndex ) ) );
try {
sqmQuerySpec.setFromClause( visitFromClause( (HqlParser.FromClauseContext) ctx.getChild( fromIndex ) ) );
}
finally {
treatHandlerStack.pop();
}
final SqmSelectClause selectClause; final SqmSelectClause selectClause;
if ( fromIndex == 1 ) { if ( fromIndex == 1 ) {
@ -817,13 +813,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
int currentIndex = fromIndex + 1; int currentIndex = fromIndex + 1;
final SqmWhereClause whereClause = new SqmWhereClause( creationContext.getNodeBuilder() ); final SqmWhereClause whereClause = new SqmWhereClause( creationContext.getNodeBuilder() );
if ( currentIndex < ctx.getChildCount() && ctx.getChild( currentIndex ) instanceof HqlParser.WhereClauseContext ) { if ( currentIndex < ctx.getChildCount() && ctx.getChild( currentIndex ) instanceof HqlParser.WhereClauseContext ) {
treatHandlerStack.push( new TreatHandlerNormal( DowncastLocation.WHERE ) ); whereClause.setPredicate( (SqmPredicate) ctx.getChild( currentIndex++ ).accept( this ) );
try {
whereClause.setPredicate( (SqmPredicate) ctx.getChild( currentIndex++ ).accept( this ) );
}
finally {
treatHandlerStack.pop();
}
} }
sqmQuerySpec.setWhereClause( whereClause ); sqmQuerySpec.setWhereClause( whereClause );
@ -862,8 +852,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override @Override
public SqmSelectClause visitSelectClause(HqlParser.SelectClauseContext ctx) { public SqmSelectClause visitSelectClause(HqlParser.SelectClauseContext ctx) {
treatHandlerStack.push( new TreatHandlerNormal( DowncastLocation.SELECT ) );
// todo (6.0) : primer a select-clause-specific SemanticPathPart into the stack // todo (6.0) : primer a select-clause-specific SemanticPathPart into the stack
final int selectionListIndex; final int selectionListIndex;
if ( ctx.getChild( 1 ) instanceof HqlParser.SelectionListContext ) { if ( ctx.getChild( 1 ) instanceof HqlParser.SelectionListContext ) {
@ -873,24 +861,19 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
selectionListIndex = 2; selectionListIndex = 2;
} }
try { final SqmSelectClause selectClause = new SqmSelectClause(
final SqmSelectClause selectClause = new SqmSelectClause( selectionListIndex == 2,
selectionListIndex == 2, creationContext.getNodeBuilder()
creationContext.getNodeBuilder() );
); final HqlParser.SelectionListContext selectionListContext = (HqlParser.SelectionListContext) ctx.getChild(
final HqlParser.SelectionListContext selectionListContext = (HqlParser.SelectionListContext) ctx.getChild( selectionListIndex
selectionListIndex );
); for ( ParseTree subCtx : selectionListContext.children ) {
for ( ParseTree subCtx : selectionListContext.children ) { if ( subCtx instanceof HqlParser.SelectionContext ) {
if ( subCtx instanceof HqlParser.SelectionContext ) { selectClause.addSelection( visitSelection( (HqlParser.SelectionContext) subCtx ) );
selectClause.addSelection( visitSelection( (HqlParser.SelectionContext) subCtx ) );
}
} }
return selectClause;
}
finally {
treatHandlerStack.pop();
} }
return selectClause;
} }
@Override @Override
@ -1409,30 +1392,23 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override @Override
public SqmFromClause visitFromClause(HqlParser.FromClauseContext parserFromClause) { public SqmFromClause visitFromClause(HqlParser.FromClauseContext parserFromClause) {
treatHandlerStack.push( new TreatHandlerFromClause() ); final SqmFromClause fromClause;
if ( parserFromClause == null ) {
try { fromClause = new SqmFromClause();
final SqmFromClause fromClause; }
if ( parserFromClause == null ) { else {
fromClause = new SqmFromClause(); final int size = parserFromClause.getChildCount();
} // Shift 1 bit instead of division by 2
else { final int estimatedSize = size >> 1;
final int size = parserFromClause.getChildCount(); fromClause = new SqmFromClause( estimatedSize );
// Shift 1 bit instead of division by 2 for ( int i = 0; i < size; i++ ) {
final int estimatedSize = size >> 1; final ParseTree parseTree = parserFromClause.getChild( i );
fromClause = new SqmFromClause( estimatedSize ); if ( parseTree instanceof HqlParser.FromClauseSpaceContext ) {
for ( int i = 0; i < size; i++ ) { fromClause.addRoot( visitFromClauseSpace( (HqlParser.FromClauseSpaceContext) parseTree ) );
final ParseTree parseTree = parserFromClause.getChild( i );
if ( parseTree instanceof HqlParser.FromClauseSpaceContext ) {
fromClause.addRoot( visitFromClauseSpace( (HqlParser.FromClauseSpaceContext) parseTree ) );
}
} }
} }
return fromClause;
}
finally {
treatHandlerStack.pop();
} }
return fromClause;
} }
@Override @Override
@ -1679,10 +1655,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
//noinspection unchecked //noinspection unchecked
final SqmQualifiedJoin<X, ?> join = (SqmQualifiedJoin<X, ?>) qualifiedJoinRhsContext.getChild( 0 ).accept( this ); final SqmQualifiedJoin<X, ?> join = (SqmQualifiedJoin<X, ?>) qualifiedJoinRhsContext.getChild( 0 ).accept( this );
// we need to set the alias here because the path could be treated - the treat operator is
// not consumed by the identifierConsumer
join.setExplicitAlias( alias );
final HqlParser.QualifiedJoinPredicateContext qualifiedJoinPredicateContext = parserJoin.qualifiedJoinPredicate(); final HqlParser.QualifiedJoinPredicateContext qualifiedJoinPredicateContext = parserJoin.qualifiedJoinPredicate();
if ( join instanceof SqmEntityJoin<?> ) { if ( join instanceof SqmEntityJoin<?> ) {
sqmRoot.addSqmJoin( join ); sqmRoot.addSqmJoin( join );
@ -3129,6 +3101,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override @Override
public SqmNamedParameter<?> visitNamedParameter(HqlParser.NamedParameterContext ctx) { public SqmNamedParameter<?> visitNamedParameter(HqlParser.NamedParameterContext ctx) {
parameterStyle = parameterStyle.withNamed();
final SqmNamedParameter<?> param = new SqmNamedParameter<>( final SqmNamedParameter<?> param = new SqmNamedParameter<>(
ctx.getChild( 1 ).getText(), ctx.getChild( 1 ).getText(),
parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(), parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(),
@ -3143,6 +3116,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
if ( ctx.getChildCount() == 1 ) { if ( ctx.getChildCount() == 1 ) {
throw new SemanticException( "Encountered positional parameter which did not declare position (? instead of, e.g., ?1)" ); throw new SemanticException( "Encountered positional parameter which did not declare position (? instead of, e.g., ?1)" );
} }
parameterStyle = parameterStyle.withPositional();
final SqmPositionalParameter<?> param = new SqmPositionalParameter<>( final SqmPositionalParameter<?> param = new SqmPositionalParameter<>(
Integer.parseInt( ctx.getChild( 1 ).getText() ), Integer.parseInt( ctx.getChild( 1 ).getText() ),
parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(), parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(),
@ -4128,15 +4102,20 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override @Override
public SqmPath<?> visitTreatedNavigablePath(HqlParser.TreatedNavigablePathContext ctx) { public SqmPath<?> visitTreatedNavigablePath(HqlParser.TreatedNavigablePathContext ctx) {
final SqmPath<?> sqmPath = consumeManagedTypeReference( (HqlParser.PathContext) ctx.getChild( 2 ) ); final DotIdentifierConsumer consumer = dotIdentifierConsumerStack.getCurrent();
if ( consumer instanceof QualifiedJoinPathConsumer ) {
( (QualifiedJoinPathConsumer) consumer ).setTreated( true );
}
consumeManagedTypeReference( (HqlParser.PathContext) ctx.getChild( 2 ) );
final String treatTargetName = ctx.getChild( 4 ).getText(); final String treatTargetName = ctx.getChild( 4 ).getText();
final String treatTargetEntityName = getCreationContext().getJpaMetamodel().qualifyImportableName( treatTargetName ); final String treatTargetEntityName = getCreationContext().getJpaMetamodel().qualifyImportableName( treatTargetName );
final EntityDomainType<?> treatTarget = getCreationContext().getJpaMetamodel().entity( treatTargetEntityName );
SqmPath<?> result = resolveTreatedPath( sqmPath, treatTarget ); final boolean hasContinuation = ctx.getChildCount() == 7;
consumer.consumeTreat( treatTargetEntityName, !hasContinuation );
SqmPath<?> result = (SqmPath<?>) consumer.getConsumedPart();
if ( ctx.getChildCount() == 7 ) { if ( hasContinuation ) {
dotIdentifierConsumerStack.push( dotIdentifierConsumerStack.push(
new BasicDotIdentifierConsumer( result, this ) { new BasicDotIdentifierConsumer( result, this ) {
@Override @Override
@ -4155,11 +4134,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
return result; return result;
} }
@SuppressWarnings({ "unchecked", "rawtypes" })
private SqmTreatedPath<?, ?> resolveTreatedPath(SqmPath<?> sqmPath, EntityDomainType<?> treatTarget) {
return sqmPath.treatAs( (EntityDomainType) treatTarget );
}
@Override @Override
public SqmPath<?> visitCollectionElementNavigablePath(HqlParser.CollectionElementNavigablePathContext ctx) { public SqmPath<?> visitCollectionElementNavigablePath(HqlParser.CollectionElementNavigablePathContext ctx) {
final SqmPath<?> pluralAttributePath = consumeDomainPath( (HqlParser.PathContext) ctx.getChild( 2 ) ); final SqmPath<?> pluralAttributePath = consumeDomainPath( (HqlParser.PathContext) ctx.getChild( 2 ) );
@ -4257,15 +4231,10 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
final SqmPath<?> sqmPath = consumeDomainPath( parserPath ); final SqmPath<?> sqmPath = consumeDomainPath( parserPath );
final SqmPathSource<?> pathSource = sqmPath.getReferencedPathSource(); final SqmPathSource<?> pathSource = sqmPath.getReferencedPathSource();
if ( pathSource.getSqmPathType() instanceof ManagedDomainType<?> ) {
try {
// use the `#sqmAs` call to validate the path is a ManagedType
pathSource.sqmAs( ManagedDomainType.class );
return sqmPath; return sqmPath;
} }
catch (Exception e) { throw new SemanticException( "Expecting ManagedType valued path [" + sqmPath.getNavigablePath() + "], but found : " + pathSource.getSqmPathType() );
throw new SemanticException( "Expecting ManagedType valued path [" + sqmPath.getNavigablePath() + "], but found : " + pathSource.getSqmPathType() );
}
} }
private SqmPath<?> consumePluralAttributeReference(HqlParser.PathContext parserPath) { private SqmPath<?> consumePluralAttributeReference(HqlParser.PathContext parserPath) {
@ -4278,40 +4247,6 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
throw new SemanticException( "Expecting plural attribute valued path [" + sqmPath.getNavigablePath() + "], but found : " + sqmPath.getReferencedPathSource().getSqmPathType() ); throw new SemanticException( "Expecting plural attribute valued path [" + sqmPath.getNavigablePath() + "], but found : " + sqmPath.getReferencedPathSource().getSqmPathType() );
} }
private interface TreatHandler {
void addDowncast(SqmFrom<?, ?> sqmFrom, IdentifiableDomainType<?> downcastTarget);
}
private static class TreatHandlerNormal implements TreatHandler {
private final DowncastLocation downcastLocation;
public TreatHandlerNormal() {
this( DowncastLocation.OTHER );
}
public TreatHandlerNormal(DowncastLocation downcastLocation) {
this.downcastLocation = downcastLocation;
}
@Override
public void addDowncast(
SqmFrom<?, ?> sqmFrom,
IdentifiableDomainType<?> downcastTarget) {
// ( (MutableUsageDetails) sqmFrom.getUsageDetails() ).addDownCast( false, downcastTarget, downcastLocation );
throw new NotYetImplementedFor6Exception();
}
}
private static class TreatHandlerFromClause implements TreatHandler {
@Override
public void addDowncast(
SqmFrom<?, ?> sqmFrom,
IdentifiableDomainType<?> downcastTarget) {
// ( (MutableUsageDetails) sqmFrom.getUsageDetails() ).addDownCast( true, downcastTarget, DowncastLocation.FROM );
throw new NotYetImplementedFor6Exception();
}
}
private void checkFQNEntityNameJpaComplianceViolationIfNeeded(String name, EntityDomainType<?> entityDescriptor) { private void checkFQNEntityNameJpaComplianceViolationIfNeeded(String name, EntityDomainType<?> entityDescriptor) {
if ( getCreationOptions().useStrictJpaCompliance() && ! name.equals( entityDescriptor.getName() ) ) { if ( getCreationOptions().useStrictJpaCompliance() && ! name.equals( entityDescriptor.getName() ) ) {
// FQN is the only possible reason // FQN is the only possible reason
@ -4321,4 +4256,59 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
); );
} }
} }
private enum ParameterStyle {
UNKNOWN {
@Override
ParameterStyle withNamed() {
return NAMED;
}
@Override
ParameterStyle withPositional() {
return POSITIONAL;
}
},
NAMED {
@Override
ParameterStyle withNamed() {
return NAMED;
}
@Override
ParameterStyle withPositional() {
throw new StrictJpaComplianceViolation(
"Cannot mix positional and named parameters",
StrictJpaComplianceViolation.Type.MIXED_POSITIONAL_NAMED_PARAMETERS
);
}
},
POSITIONAL {
@Override
ParameterStyle withNamed() {
throw new StrictJpaComplianceViolation(
"Cannot mix positional and named parameters",
StrictJpaComplianceViolation.Type.MIXED_POSITIONAL_NAMED_PARAMETERS
);
}
@Override
ParameterStyle withPositional() {
return POSITIONAL;
}
},
MIXED {
@Override
ParameterStyle withNamed() {
return MIXED;
}
@Override
ParameterStyle withPositional() {
return MIXED;
}
};
abstract ParameterStyle withNamed();
abstract ParameterStyle withPositional();
}
} }

View File

@ -22,6 +22,15 @@ public interface DotIdentifierConsumer {
*/ */
void consumeIdentifier(String identifier, boolean isBase, boolean isTerminal); void consumeIdentifier(String identifier, boolean isBase, boolean isTerminal);
/**
* Responsible for consuming each part of the path. Called sequentially for
* each part.
*
* @param entityName The treat target entity name
* @param isTerminal Is this the terminus of the path (last token)?
*/
void consumeTreat(String entityName, boolean isTerminal);
/** /**
* Get the currently consumed part. Generally called after the whole path * Get the currently consumed part. Generally called after the whole path
* has been processed at which point this will return the final outcome of the * has been processed at which point this will return the final outcome of the

View File

@ -46,7 +46,7 @@ public class QueryInterpretationCacheDisabledImpl implements QueryInterpretation
} }
@Override @Override
public SelectQueryPlan resolveSelectQueryPlan(Key key, Supplier<SelectQueryPlan> creator) { public <R> SelectQueryPlan<R> resolveSelectQueryPlan(Key key, Supplier<SelectQueryPlan<R>> creator) {
return null; return null;
} }

View File

@ -76,17 +76,18 @@ public class QueryInterpretationCacheStandardImpl implements QueryInterpretation
} }
@Override @Override
public SelectQueryPlan resolveSelectQueryPlan( public <R> SelectQueryPlan<R> resolveSelectQueryPlan(
Key key, Key key,
Supplier<SelectQueryPlan> creator) { Supplier<SelectQueryPlan<R>> creator) {
log.tracef( "QueryPlan#getSelectQueryPlan(%s)", key ); log.tracef( "QueryPlan#getSelectQueryPlan(%s)", key );
final SelectQueryPlan cached = (SelectQueryPlan) queryPlanCache.get( key ); @SuppressWarnings("unchecked")
final SelectQueryPlan<R> cached = (SelectQueryPlan<R>) queryPlanCache.get( key );
if ( cached != null ) { if ( cached != null ) {
return cached; return cached;
} }
final SelectQueryPlan plan = creator.get(); final SelectQueryPlan<R> plan = creator.get();
queryPlanCache.put( key.prepareForStore(), plan ); queryPlanCache.put( key.prepareForStore(), plan );
return plan; return plan;
} }

View File

@ -240,8 +240,8 @@ public class QueryParameterBindingsImpl implements QueryParameterBindings {
final TypeConfiguration typeConfiguration = session.getFactory().getTypeConfiguration(); final TypeConfiguration typeConfiguration = session.getFactory().getTypeConfiguration();
if ( binding.getBindType() instanceof JavaTypedExpressable ) { if ( binding.getBindType() instanceof JavaTypedExpressable ) {
final JavaTypedExpressable javaTypedExpressable = (JavaTypedExpressable) binding.getBindType(); final JavaTypedExpressable<?> javaTypedExpressable = (JavaTypedExpressable<?>) binding.getBindType();
final JavaType jtd = javaTypedExpressable.getExpressableJavaTypeDescriptor(); final JavaType<?> jtd = javaTypedExpressable.getExpressableJavaTypeDescriptor();
if ( jtd.getJavaTypeClass() != null ) { if ( jtd.getJavaTypeClass() != null ) {
// avoid dynamic models // avoid dynamic models
return typeConfiguration.getBasicTypeForJavaType( jtd.getJavaTypeClass() ); return typeConfiguration.getBasicTypeForJavaType( jtd.getJavaTypeClass() );

View File

@ -131,7 +131,7 @@ public class TableGroupImpl implements TableGroup {
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization) { boolean allowFkOptimization) {
final TableReference tableReference = getTableReference( navigablePath, tableExpression, allowFkOptimization ); final TableReference tableReference = getTableReference( navigablePath, tableExpression, allowFkOptimization, true );
if ( tableReference == null ) { if ( tableReference == null ) {
throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" ); throw new IllegalStateException( "Could not resolve binding for table `" + tableExpression + "`" );
} }
@ -143,14 +143,15 @@ public class TableGroupImpl implements TableGroup {
public TableReference getTableReference( public TableReference getTableReference(
NavigablePath navigablePath, NavigablePath navigablePath,
String tableExpression, String tableExpression,
boolean allowFkOptimization) { boolean allowFkOptimization,
if ( primaryTableReference.getTableReference( navigablePath , tableExpression, allowFkOptimization ) != null ) { boolean resolve) {
if ( primaryTableReference.getTableReference( navigablePath , tableExpression, allowFkOptimization, resolve ) != null ) {
return primaryTableReference; return primaryTableReference;
} }
for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) { for ( TableGroupJoin tableGroupJoin : getTableGroupJoins() ) {
final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference(); final TableReference primaryTableReference = tableGroupJoin.getJoinedGroup().getPrimaryTableReference();
if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization ) != null ) { if ( primaryTableReference.getTableReference( navigablePath, tableExpression, allowFkOptimization, resolve ) != null ) {
return primaryTableReference; return primaryTableReference;
} }
} }

View File

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

View File

@ -69,7 +69,7 @@ public class CompleteFetchBuilderEntityValuedModelPart
final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() ); final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().getTableGroup( navigablePath.getParent() );
modelPart.forEachSelectable( modelPart.forEachSelectable(
(selectionIndex, selectableMapping) -> { (selectionIndex, selectableMapping) -> {
final TableReference tableReference = tableGroup.getTableReference( navigablePath, selectableMapping.getContainingTableExpression() ); final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, selectableMapping.getContainingTableExpression() );
final String mappedColumn = selectableMapping.getSelectionExpression(); final String mappedColumn = selectableMapping.getSelectionExpression();
final String columnAlias = columnAliases.get( selectionIndex ); final String columnAlias = columnAliases.get( selectionIndex );
creationStateImpl.resolveSqlSelection( creationStateImpl.resolveSqlSelection(

View File

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

View File

@ -146,7 +146,7 @@ public class CompleteResultBuilderCollectionStandard implements CompleteResultBu
creationStateImpl.resolveSqlSelection( creationStateImpl.resolveSqlSelection(
creationStateImpl.resolveSqlExpression( creationStateImpl.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey( SqlExpressionResolver.createColumnReferenceKey(
tableGroup.getTableReference( selectableMapping.getContainingTableExpression() ), tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ),
selectableMapping.getSelectionExpression() selectableMapping.getSelectionExpression()
), ),
processingState -> { processingState -> {

View File

@ -58,7 +58,6 @@ public class DelayedFetchBuilderBasicPart
parent, parent,
fetchPath, fetchPath,
referencedModelPart, referencedModelPart,
true,
null, null,
FetchTiming.DELAYED, FetchTiming.DELAYED,
isEnhancedForLazyLoading, isEnhancedForLazyLoading,

View File

@ -10,6 +10,7 @@ import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.engine.FetchTiming;
import org.hibernate.internal.util.MutableObject; import org.hibernate.internal.util.MutableObject;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityValuedModelPart;
@ -19,7 +20,6 @@ import org.hibernate.query.NavigablePath;
import org.hibernate.query.results.ResultsHelper; import org.hibernate.query.results.ResultsHelper;
import org.hibernate.sql.ast.spi.SqlAstCreationState; import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.results.graph.AssemblerCreationState; import org.hibernate.sql.results.graph.AssemblerCreationState;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultAssembler; import org.hibernate.sql.results.graph.DomainResultAssembler;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import org.hibernate.sql.results.graph.Fetch; import org.hibernate.sql.results.graph.Fetch;
@ -37,7 +37,7 @@ public class EntityResultImpl implements EntityResult {
private final NavigablePath navigablePath; private final NavigablePath navigablePath;
private final EntityValuedModelPart entityValuedModelPart; private final EntityValuedModelPart entityValuedModelPart;
private final DomainResult<?> identifierResult; private final Fetch identifierFetch;
private final BasicFetch<?> discriminatorFetch; private final BasicFetch<?> discriminatorFetch;
private final List<Fetch> fetches; private final List<Fetch> fetches;
@ -102,17 +102,20 @@ public class EntityResultImpl implements EntityResult {
} }
if ( idFetchRef.isNotSet() ) { if ( idFetchRef.isNotSet() ) {
identifierResult = ResultsHelper.implicitIdentifierResult( identifierFetch = ( (Fetchable) identifierMapping ).generateFetch(
identifierMapping, this,
new EntityIdentifierNavigablePath( new EntityIdentifierNavigablePath(
navigablePath, navigablePath,
ResultsHelper.attributeName( identifierMapping ) ResultsHelper.attributeName( identifierMapping )
), ),
FetchTiming.IMMEDIATE,
true,
null,
creationState creationState
); );
} }
else { else {
this.identifierResult = idFetchRef.get().asResult( creationState ); this.identifierFetch = idFetchRef.get();
} }
} }
@ -161,7 +164,7 @@ public class EntityResultImpl implements EntityResult {
this, this,
getNavigablePath(), getNavigablePath(),
lockMode, lockMode,
identifierResult, identifierFetch,
discriminatorFetch, discriminatorFetch,
null, null,
creationState creationState

View File

@ -148,7 +148,7 @@ public class DynamicFetchBuilderLegacy implements DynamicFetchBuilder, NativeQue
resolveSqlSelection( resolveSqlSelection(
columnNames.get( selectionIndex ), columnNames.get( selectionIndex ),
createColumnReferenceKey( createColumnReferenceKey(
tableGroup.getTableReference( selectableMapping.getContainingTableExpression() ), tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ),
selectableMapping.getSelectionExpression() selectableMapping.getSelectionExpression()
), ),
selectableMapping.getJdbcMapping(), selectableMapping.getJdbcMapping(),

View File

@ -65,7 +65,7 @@ public class DynamicFetchBuilderStandard
final SqlExpressionResolver sqlExpressionResolver = domainResultCreationState.getSqlAstCreationState().getSqlExpressionResolver(); final SqlExpressionResolver sqlExpressionResolver = domainResultCreationState.getSqlAstCreationState().getSqlExpressionResolver();
final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> { final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> {
final TableReference tableReference = ownerTableGroup.getTableReference( final TableReference tableReference = ownerTableGroup.resolveTableReference(
fetchPath, fetchPath,
selectableMapping.getContainingTableExpression() selectableMapping.getContainingTableExpression()
); );

View File

@ -67,7 +67,7 @@ public class ImplicitFetchBuilderBasic implements ImplicitFetchBuilder, BasicVal
final Expression expression = creationStateImpl.resolveSqlExpression( final Expression expression = creationStateImpl.resolveSqlExpression(
createColumnReferenceKey( createColumnReferenceKey(
parentTableGroup.getTableReference( fetchPath, table ), parentTableGroup.resolveTableReference( fetchPath, table ),
fetchable.getSelectionExpression() fetchable.getSelectionExpression()
), ),
processingState -> { processingState -> {
@ -99,8 +99,6 @@ public class ImplicitFetchBuilderBasic implements ImplicitFetchBuilder, BasicVal
parent, parent,
fetchPath, fetchPath,
fetchable, fetchable,
// todo (6.0) - we don't know
true,
valueConverter, valueConverter,
FetchTiming.IMMEDIATE, FetchTiming.IMMEDIATE,
domainResultCreationState domainResultCreationState

View File

@ -101,7 +101,7 @@ import static org.hibernate.jpa.QueryHints.SPEC_HINT_TIMEOUT;
*/ */
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public abstract class AbstractQuery<R> implements QueryImplementor<R> { public abstract class AbstractQuery<R> implements QueryImplementor<R> {
private static final EntityManagerMessageLogger log = HEMLogging.messageLogger( AbstractQuery.class ); protected static final EntityManagerMessageLogger log = HEMLogging.messageLogger( AbstractQuery.class );
private final SharedSessionContractImplementor session; private final SharedSessionContractImplementor session;

View File

@ -35,7 +35,7 @@ public interface QueryInterpretationCache {
HqlInterpretation resolveHqlInterpretation(String queryString, Function<String, SqmStatement<?>> creator); HqlInterpretation resolveHqlInterpretation(String queryString, Function<String, SqmStatement<?>> creator);
SelectQueryPlan resolveSelectQueryPlan(Key key, Supplier<SelectQueryPlan> creator); <R> SelectQueryPlan<R> resolveSelectQueryPlan(Key key, Supplier<SelectQueryPlan<R>> creator);
NonSelectQueryPlan getNonSelectQueryPlan(Key key); NonSelectQueryPlan getNonSelectQueryPlan(Key key);
void cacheNonSelectQueryPlan(Key key, NonSelectQueryPlan plan); void cacheNonSelectQueryPlan(Key key, NonSelectQueryPlan plan);

View File

@ -13,18 +13,12 @@ import java.util.Set;
import org.hibernate.action.internal.BulkOperationCleanupAction; import org.hibernate.action.internal.BulkOperationCleanupAction;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.model.domain.AllowableParameterType;
import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.spi.NonSelectQueryPlan; import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.sql.spi.ParameterOccurrence;
import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter;
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
import org.hibernate.sql.exec.internal.StandardJdbcMutationExecutor; import org.hibernate.sql.exec.internal.StandardJdbcMutationExecutor;
import org.hibernate.sql.exec.spi.JdbcMutation; import org.hibernate.sql.exec.spi.JdbcMutation;
import org.hibernate.sql.exec.spi.JdbcMutationExecutor; import org.hibernate.sql.exec.spi.JdbcMutationExecutor;
@ -39,12 +33,12 @@ public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan {
private final String sql; private final String sql;
private final Set<String> affectedTableNames; private final Set<String> affectedTableNames;
private final List<QueryParameterImplementor<?>> parameterList; private final List<ParameterOccurrence> parameterList;
public NativeNonSelectQueryPlanImpl( public NativeNonSelectQueryPlanImpl(
String sql, String sql,
Set<String> affectedTableNames, Set<String> affectedTableNames,
List<QueryParameterImplementor<?>> parameterList) { List<ParameterOccurrence> parameterList) {
this.sql = sql; this.sql = sql;
this.affectedTableNames = affectedTableNames; this.affectedTableNames = affectedTableNames;
this.parameterList = parameterList; this.parameterList = parameterList;
@ -66,26 +60,12 @@ public class NativeNonSelectQueryPlanImpl implements NonSelectQueryPlan {
jdbcParameterBinders = new ArrayList<>( parameterList.size() ); jdbcParameterBinders = new ArrayList<>( parameterList.size() );
jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() );
for ( QueryParameterImplementor<?> param : parameterList ) { jdbcParameterBindings.registerNativeQueryParameters(
QueryParameterBinding<?> binding = queryParameterBindings.getBinding( param ); queryParameterBindings,
AllowableParameterType<?> type = binding.getBindType(); parameterList,
if ( type == null ) { jdbcParameterBinders,
type = param.getHibernateType(); executionContext.getSession().getFactory()
} );
if ( type == null ) {
type = executionContext.getSession().getTypeConfiguration().getBasicTypeForJavaType( Object.class );
}
final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping();
final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping );
jdbcParameterBinders.add( jdbcParameter );
jdbcParameterBindings.addBinding(
jdbcParameter,
new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() )
);
}
} }
final JdbcMutation jdbcMutation = new NativeJdbcMutation( final JdbcMutation jdbcMutation = new NativeJdbcMutation(

View File

@ -11,11 +11,13 @@ import java.time.Instant;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -30,6 +32,8 @@ import org.hibernate.LockOptions;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.QueryException; import org.hibernate.QueryException;
import org.hibernate.ScrollMode; import org.hibernate.ScrollMode;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.query.spi.NativeQueryInterpreter; import org.hibernate.engine.query.spi.NativeQueryInterpreter;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
@ -37,6 +41,7 @@ import org.hibernate.graph.GraphSemantic;
import org.hibernate.graph.RootGraph; import org.hibernate.graph.RootGraph;
import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.internal.AbstractSharedSessionContract; import org.hibernate.internal.AbstractSharedSessionContract;
import org.hibernate.internal.util.MathHelper;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jpa.internal.util.LockModeTypeHelper; import org.hibernate.jpa.internal.util.LockModeTypeHelper;
@ -69,6 +74,7 @@ import org.hibernate.query.spi.NonSelectQueryPlan;
import org.hibernate.query.spi.ParameterMetadataImplementor; import org.hibernate.query.spi.ParameterMetadataImplementor;
import org.hibernate.query.spi.QueryEngine; import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryInterpretationCache; import org.hibernate.query.spi.QueryInterpretationCache;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor;
@ -79,6 +85,7 @@ import org.hibernate.query.sql.spi.NativeSelectQueryDefinition;
import org.hibernate.query.sql.spi.NativeSelectQueryPlan; import org.hibernate.query.sql.spi.NativeSelectQueryPlan;
import org.hibernate.query.sql.spi.NonSelectInterpretationsKey; import org.hibernate.query.sql.spi.NonSelectInterpretationsKey;
import org.hibernate.query.sql.spi.ParameterInterpretation; import org.hibernate.query.sql.spi.ParameterInterpretation;
import org.hibernate.query.sql.spi.ParameterOccurrence;
import org.hibernate.query.sql.spi.SelectInterpretationsKey; import org.hibernate.query.sql.spi.SelectInterpretationsKey;
import org.hibernate.sql.exec.internal.CallbackImpl; import org.hibernate.sql.exec.internal.CallbackImpl;
import org.hibernate.sql.exec.spi.Callback; import org.hibernate.sql.exec.spi.Callback;
@ -110,7 +117,7 @@ public class NativeQueryImpl<R>
private final String sqlString; private final String sqlString;
private final ParameterMetadataImplementor parameterMetadata; private final ParameterMetadataImplementor parameterMetadata;
private final List<QueryParameterImplementor<?>> occurrenceOrderedParamList; private final List<ParameterOccurrence> parameterOccurrences;
private final QueryParameterBindings parameterBindings; private final QueryParameterBindings parameterBindings;
private final ResultSetMappingImpl resultSetMapping; private final ResultSetMappingImpl resultSetMapping;
@ -190,7 +197,7 @@ public class NativeQueryImpl<R>
this.sqlString = parameterInterpretation.getAdjustedSqlString(); this.sqlString = parameterInterpretation.getAdjustedSqlString();
this.parameterMetadata = parameterInterpretation.toParameterMetadata( session ); this.parameterMetadata = parameterInterpretation.toParameterMetadata( session );
this.occurrenceOrderedParamList = parameterInterpretation.getOccurrenceOrderedParameters(); this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences();
this.parameterBindings = QueryParameterBindingsImpl.from( this.parameterBindings = QueryParameterBindingsImpl.from(
parameterMetadata, parameterMetadata,
session.getFactory(), session.getFactory(),
@ -327,7 +334,7 @@ public class NativeQueryImpl<R>
this.sqlString = parameterInterpretation.getAdjustedSqlString(); this.sqlString = parameterInterpretation.getAdjustedSqlString();
this.parameterMetadata = parameterInterpretation.toParameterMetadata( session ); this.parameterMetadata = parameterInterpretation.toParameterMetadata( session );
this.occurrenceOrderedParamList = parameterInterpretation.getOccurrenceOrderedParameters(); this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences();
this.parameterBindings = QueryParameterBindingsImpl.from( this.parameterBindings = QueryParameterBindingsImpl.from(
parameterMetadata, parameterMetadata,
session.getFactory(), session.getFactory(),
@ -386,7 +393,7 @@ public class NativeQueryImpl<R>
this.sqlString = parameterInterpretation.getAdjustedSqlString(); this.sqlString = parameterInterpretation.getAdjustedSqlString();
this.parameterMetadata = parameterInterpretation.toParameterMetadata( session ); this.parameterMetadata = parameterInterpretation.toParameterMetadata( session );
this.occurrenceOrderedParamList = parameterInterpretation.getOccurrenceOrderedParameters(); this.parameterOccurrences = parameterInterpretation.getOrderedParameterOccurrences();
this.parameterBindings = QueryParameterBindingsImpl.from( this.parameterBindings = QueryParameterBindingsImpl.from(
parameterMetadata, parameterMetadata,
session.getFactory(), session.getFactory(),
@ -571,11 +578,9 @@ public class NativeQueryImpl<R>
@Override @Override
protected List<R> doList() { protected List<R> doList() {
//noinspection unchecked
return resolveSelectQueryPlan().performList( this ); return resolveSelectQueryPlan().performList( this );
} }
@SuppressWarnings("unchecked")
private SelectQueryPlan<R> resolveSelectQueryPlan() { private SelectQueryPlan<R> resolveSelectQueryPlan() {
final QueryInterpretationCache.Key cacheKey = generateSelectInterpretationsKey( resultSetMapping ); final QueryInterpretationCache.Key cacheKey = generateSelectInterpretationsKey( resultSetMapping );
if ( cacheKey != null ) { if ( cacheKey != null ) {
@ -590,10 +595,11 @@ public class NativeQueryImpl<R>
} }
private NativeSelectQueryPlan<R> createQueryPlan(ResultSetMapping resultSetMapping) { private NativeSelectQueryPlan<R> createQueryPlan(ResultSetMapping resultSetMapping) {
final NativeSelectQueryDefinition queryDefinition = new NativeSelectQueryDefinition() { final String sqlString = expandParameterLists();
final NativeSelectQueryDefinition<R> queryDefinition = new NativeSelectQueryDefinition<R>() {
@Override @Override
public String getSqlString() { public String getSqlString() {
return NativeQueryImpl.this.getQueryString(); return sqlString;
} }
@Override @Override
@ -602,8 +608,8 @@ public class NativeQueryImpl<R>
} }
@Override @Override
public List<QueryParameterImplementor<?>> getQueryParameterList() { public List<ParameterOccurrence> getQueryParameterOccurrences() {
return NativeQueryImpl.this.occurrenceOrderedParamList; return NativeQueryImpl.this.parameterOccurrences;
} }
@Override @Override
@ -622,6 +628,142 @@ public class NativeQueryImpl<R>
.createQueryPlan( queryDefinition, getSessionFactory() ); .createQueryPlan( queryDefinition, getSessionFactory() );
} }
private String expandParameterLists() {
if ( parameterOccurrences == null || parameterOccurrences.isEmpty() ) {
return sqlString;
}
// HHH-1123
// Some DBs limit number of IN expressions. For now, warn...
final Dialect dialect = getSessionFactory().getServiceRegistry().getService( JdbcServices.class ).getJdbcEnvironment().getDialect();
final boolean paddingEnabled = getSessionFactory().getSessionFactoryOptions().inClauseParameterPaddingEnabled();
final int inExprLimit = dialect.getInExpressionCountLimit();
StringBuilder sb = null;
// Handle parameter lists
int offset = 0;
for ( ParameterOccurrence occurrence : parameterOccurrences ) {
final QueryParameterImplementor<?> queryParameter = occurrence.getParameter();
final QueryParameterBinding<?> binding = parameterBindings.getBinding( queryParameter );
if ( !binding.isMultiValued() ) {
continue;
}
final Collection<?> bindValues = binding.getBindValues();
int bindValueCount = bindValues.size();
int bindValueMaxCount = determineBindValueMaxCount( paddingEnabled, inExprLimit, bindValueCount );
if ( inExprLimit > 0 && bindValueCount > inExprLimit ) {
log.tooManyInExpressions(
dialect.getClass().getName(),
inExprLimit,
queryParameter.getName() == null
? queryParameter.getPosition().toString()
: queryParameter.getName(),
bindValueCount
);
}
final int sourcePosition = occurrence.getSourcePosition();
if ( sourcePosition < 0 ) {
continue;
}
// check if placeholder is already immediately enclosed in parentheses
// (ignoring whitespace)
boolean isEnclosedInParens = true;
for ( int i = sourcePosition - 1; i >= 0; i-- ) {
final char ch = sqlString.charAt( i );
if ( !Character.isWhitespace( ch ) ) {
isEnclosedInParens = ch == '(';
break;
}
}
if ( isEnclosedInParens ) {
for ( int i = sourcePosition + 1; i < sqlString.length(); i++ ) {
final char ch = sqlString.charAt( i );
if ( !Character.isWhitespace( ch ) ) {
isEnclosedInParens = ch == ')';
break;
}
}
}
if ( bindValueCount == 1 && isEnclosedInParens ) {
// short-circuit for performance when only 1 value and the
// placeholder is already enclosed in parentheses...
continue;
}
if ( sb == null ) {
sb = new StringBuilder( sqlString.length() + 20 );
sb.append( sqlString );
}
final String expansionListAsString;
// HHH-8901
if ( bindValueMaxCount == 0 ) {
if ( isEnclosedInParens ) {
expansionListAsString = "null";
}
else {
expansionListAsString = "(null)";
}
}
else {
// Shift 1 bit instead of multiplication by 2
char[] chars;
if ( isEnclosedInParens ) {
chars = new char[( bindValueMaxCount << 1 ) - 1];
chars[0] = '?';
for ( int i = 1; i < bindValueMaxCount; i++ ) {
final int index = i << 1;
chars[index - 1] = ',';
chars[index] = '?';
}
}
else {
chars = new char[( bindValueMaxCount << 1 ) + 1];
chars[0] = '(';
chars[1] = '?';
for ( int i = 1; i < bindValueMaxCount; i++ ) {
final int index = i << 1;
chars[index] = ',';
chars[index + 1] = '?';
}
chars[chars.length - 1] = ')';
}
expansionListAsString = new String(chars);
}
final int start = sourcePosition + offset;
final int end = start + 1;
sb.replace( start, end, expansionListAsString );
offset += expansionListAsString.length() - 1;
}
return sb == null ? sqlString : sb.toString();
}
public static int determineBindValueMaxCount(boolean paddingEnabled, int inExprLimit, int bindValueCount) {
int bindValueMaxCount = bindValueCount;
final boolean inClauseParameterPaddingEnabled = paddingEnabled && bindValueCount > 2;
if ( inClauseParameterPaddingEnabled ) {
int bindValuePaddingCount = MathHelper.ceilingPowerOfTwo( bindValueCount );
if ( inExprLimit > 0 && bindValuePaddingCount > inExprLimit ) {
bindValuePaddingCount = inExprLimit;
}
if ( bindValueCount < bindValuePaddingCount ) {
bindValueMaxCount = bindValuePaddingCount;
}
}
return bindValueMaxCount;
}
private SelectInterpretationsKey generateSelectInterpretationsKey(JdbcValuesMappingProducer resultSetMapping) { private SelectInterpretationsKey generateSelectInterpretationsKey(JdbcValuesMappingProducer resultSetMapping) {
if ( !isCacheable( this ) ) { if ( !isCacheable( this ) ) {
return null; return null;
@ -635,8 +777,7 @@ public class NativeQueryImpl<R>
); );
} }
@SuppressWarnings("RedundantIfStatement") private static boolean isCacheable(NativeQueryImpl<?> query) {
private static boolean isCacheable(NativeQueryImpl query) {
// todo (6.0): unless we move the limit rendering from DeferredResultSetAccess to NativeSelectQueryPlanImpl // todo (6.0): unless we move the limit rendering from DeferredResultSetAccess to NativeSelectQueryPlanImpl
// we don't need to consider the limit here at all because that is applied on demand. // we don't need to consider the limit here at all because that is applied on demand.
// It certainly is better for performance to include the limit early, but then we might trash the cache // It certainly is better for performance to include the limit early, but then we might trash the cache
@ -644,7 +785,8 @@ public class NativeQueryImpl<R>
// return false; // return false;
// } // }
return true; // For now, don't cache plans that have parameter lists
return !query.parameterBindings.hasAnyMultiValuedBindings();
} }
private static boolean hasLimit(Limit limit) { private static boolean hasLimit(Limit limit) {
@ -669,7 +811,8 @@ public class NativeQueryImpl<R>
} }
if ( queryPlan == null ) { if ( queryPlan == null ) {
queryPlan = new NativeNonSelectQueryPlanImpl( sqlString, querySpaces, occurrenceOrderedParamList ); final String sqlString = expandParameterLists();
queryPlan = new NativeNonSelectQueryPlanImpl( sqlString, querySpaces, parameterOccurrences );
if ( cacheKey != null ) { if ( cacheKey != null ) {
getSession().getFactory().getQueryEngine().getInterpretationCache().cacheNonSelectQueryPlan( cacheKey, queryPlan ); getSession().getFactory().getQueryEngine().getInterpretationCache().cacheNonSelectQueryPlan( cacheKey, queryPlan );
} }
@ -680,6 +823,10 @@ public class NativeQueryImpl<R>
protected NonSelectInterpretationsKey generateNonSelectInterpretationsKey() { protected NonSelectInterpretationsKey generateNonSelectInterpretationsKey() {
if ( !isCacheable( this ) ) {
return null;
}
// todo (6.0) - should this account for query-spaces in determining "cacheable"? // todo (6.0) - should this account for query-spaces in determining "cacheable"?
return new NonSelectInterpretationsKey( return new NonSelectInterpretationsKey(
getQueryString(), getQueryString(),
@ -1412,7 +1559,7 @@ public class NativeQueryImpl<R>
private static class ParameterInterpretationImpl implements ParameterInterpretation { private static class ParameterInterpretationImpl implements ParameterInterpretation {
private final String sqlString; private final String sqlString;
private final List<QueryParameterImplementor<?>> parameterList; private final List<ParameterOccurrence> parameterList;
private final Map<Integer, QueryParameterImplementor<?>> positionalParameters; private final Map<Integer, QueryParameterImplementor<?>> positionalParameters;
private final Map<String, QueryParameterImplementor<?>> namedParameters; private final Map<String, QueryParameterImplementor<?>> namedParameters;
@ -1424,7 +1571,7 @@ public class NativeQueryImpl<R>
} }
@Override @Override
public List<QueryParameterImplementor<?>> getOccurrenceOrderedParameters() { public List<ParameterOccurrence> getOrderedParameterOccurrences() {
return parameterList; return parameterList;
} }

View File

@ -15,20 +15,14 @@ import java.util.Set;
import org.hibernate.ScrollMode; import org.hibernate.ScrollMode;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.EmptyScrollableResults; import org.hibernate.internal.EmptyScrollableResults;
import org.hibernate.metamodel.mapping.BasicValuedMapping;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.metamodel.model.domain.AllowableParameterType;
import org.hibernate.query.results.ResultSetMapping; import org.hibernate.query.results.ResultSetMapping;
import org.hibernate.query.spi.DomainQueryExecutionContext; import org.hibernate.query.spi.DomainQueryExecutionContext;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.spi.ScrollableResultsImplementor; import org.hibernate.query.spi.ScrollableResultsImplementor;
import org.hibernate.query.sql.spi.NativeSelectQueryPlan; import org.hibernate.query.sql.spi.NativeSelectQueryPlan;
import org.hibernate.query.sql.spi.ParameterOccurrence;
import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter; import org.hibernate.query.sqm.internal.SqmJdbcExecutionContextAdapter;
import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl; import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
import org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl; import org.hibernate.sql.exec.internal.JdbcSelectExecutorStandardImpl;
import org.hibernate.sql.exec.spi.JdbcParameterBinder; import org.hibernate.sql.exec.spi.JdbcParameterBinder;
import org.hibernate.sql.exec.spi.JdbcParameterBindings; import org.hibernate.sql.exec.spi.JdbcParameterBindings;
@ -44,14 +38,14 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
private final String sql; private final String sql;
private final Set<String> affectedTableNames; private final Set<String> affectedTableNames;
private final List<QueryParameterImplementor<?>> parameterList; private final List<ParameterOccurrence> parameterList;
private final JdbcValuesMappingProducer resultSetMapping; private final JdbcValuesMappingProducer resultSetMapping;
public NativeSelectQueryPlanImpl( public NativeSelectQueryPlanImpl(
String sql, String sql,
Set<String> affectedTableNames, Set<String> affectedTableNames,
List<QueryParameterImplementor<?>> parameterList, List<ParameterOccurrence> parameterList,
ResultSetMapping resultSetMapping, ResultSetMapping resultSetMapping,
SessionFactoryImplementor sessionFactory) { SessionFactoryImplementor sessionFactory) {
final ResultSetMappingProcessor processor = new ResultSetMappingProcessor( resultSetMapping, sessionFactory ); final ResultSetMappingProcessor processor = new ResultSetMappingProcessor( resultSetMapping, sessionFactory );
@ -85,26 +79,12 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
jdbcParameterBinders = new ArrayList<>( parameterList.size() ); jdbcParameterBinders = new ArrayList<>( parameterList.size() );
jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() );
for ( QueryParameterImplementor<?> param : parameterList ) { jdbcParameterBindings.registerNativeQueryParameters(
QueryParameterBinding<?> binding = queryParameterBindings.getBinding( param ); queryParameterBindings,
AllowableParameterType<?> type = binding.getBindType(); parameterList,
if ( type == null ) { jdbcParameterBinders,
type = param.getHibernateType(); executionContext.getSession().getFactory()
} );
if ( type == null ) {
type = executionContext.getSession().getTypeConfiguration().getBasicTypeForJavaType( Object.class );
}
final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping();
final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping );
jdbcParameterBinders.add( jdbcParameter );
jdbcParameterBindings.addBinding(
jdbcParameter,
new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() )
);
}
} }
executionContext.getSession().autoFlushIfRequired( affectedTableNames ); executionContext.getSession().autoFlushIfRequired( affectedTableNames );
@ -150,26 +130,11 @@ public class NativeSelectQueryPlanImpl<R> implements NativeSelectQueryPlan<R> {
jdbcParameterBinders = new ArrayList<>( parameterList.size() ); jdbcParameterBinders = new ArrayList<>( parameterList.size() );
jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() ); jdbcParameterBindings = new JdbcParameterBindingsImpl( parameterList.size() );
queryParameterBindings.visitBindings( jdbcParameterBindings.registerNativeQueryParameters(
(param, binding) -> { queryParameterBindings,
AllowableParameterType<?> type = binding.getBindType(); parameterList,
if ( type == null ) { jdbcParameterBinders,
type = param.getHibernateType(); executionContext.getSession().getFactory()
}
if ( type == null ) {
type = executionContext.getSession().getTypeConfiguration().getBasicTypeForJavaType( Object.class );
}
final JdbcMapping jdbcMapping = ( (BasicValuedMapping) type ).getJdbcMapping();
final JdbcParameterImpl jdbcParameter = new JdbcParameterImpl( jdbcMapping );
jdbcParameterBinders.add( jdbcParameter );
jdbcParameterBindings.addBinding(
jdbcParameter,
new JdbcParameterBindingImpl( jdbcMapping, binding.getBindValue() )
);
}
); );
} }

View File

@ -18,6 +18,7 @@ import org.hibernate.query.QueryParameter;
import org.hibernate.query.internal.QueryParameterNamedImpl; import org.hibernate.query.internal.QueryParameterNamedImpl;
import org.hibernate.query.internal.QueryParameterPositionalImpl; import org.hibernate.query.internal.QueryParameterPositionalImpl;
import org.hibernate.query.spi.QueryParameterImplementor; import org.hibernate.query.spi.QueryParameterImplementor;
import org.hibernate.query.sql.spi.ParameterOccurrence;
import org.hibernate.query.sql.spi.ParameterRecognizer; import org.hibernate.query.sql.spi.ParameterRecognizer;
/** /**
@ -37,8 +38,8 @@ public class ParameterRecognizerImpl implements ParameterRecognizer {
private int ordinalParameterImplicitPosition; private int ordinalParameterImplicitPosition;
private List<QueryParameterImplementor<?>> parameterList; private List<ParameterOccurrence> parameterList;
private StringBuilder sqlStringBuffer = new StringBuilder(); private final StringBuilder sqlStringBuffer = new StringBuilder();
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
public ParameterRecognizerImpl(SessionFactoryImplementor factory) { public ParameterRecognizerImpl(SessionFactoryImplementor factory) {
@ -77,7 +78,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer {
return positionalQueryParameters; return positionalQueryParameters;
} }
public List<QueryParameterImplementor<?>> getParameterList() { public List<ParameterOccurrence> getParameterList() {
return parameterList; return parameterList;
} }
@ -117,7 +118,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer {
parameterList = new ArrayList<>(); parameterList = new ArrayList<>();
} }
parameterList.add( parameter ); parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) );
sqlStringBuffer.append( "?" ); sqlStringBuffer.append( "?" );
} }
@ -148,7 +149,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer {
parameterList = new ArrayList<>(); parameterList = new ArrayList<>();
} }
parameterList.add( parameter ); parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) );
sqlStringBuffer.append( "?" ); sqlStringBuffer.append( "?" );
} }
@ -183,7 +184,7 @@ public class ParameterRecognizerImpl implements ParameterRecognizer {
parameterList = new ArrayList<>(); parameterList = new ArrayList<>();
} }
parameterList.add( parameter ); parameterList.add( new ParameterOccurrence( parameter, sqlStringBuffer.length() ) );
sqlStringBuffer.append( "?" ); sqlStringBuffer.append( "?" );
} }

View File

@ -165,9 +165,6 @@ public class SQLQueryParser {
result.append( '{' ).append( aliasPath ).append( '}' ); result.append( '{' ).append( aliasPath ).append( '}' );
} }
} }
// Possibly handle :something parameters for the query ?
return result.toString(); return result.toString();
} }

View File

@ -26,7 +26,7 @@ public interface NativeSelectQueryDefinition<R> {
* @apiNote This returns query parameters in the order they were * @apiNote This returns query parameters in the order they were
* encountered - potentially including "duplicate references" to a single parameter * encountered - potentially including "duplicate references" to a single parameter
*/ */
List<QueryParameterImplementor<?>> getQueryParameterList(); List<ParameterOccurrence> getQueryParameterOccurrences();
ResultSetMapping getResultSetMapping(); ResultSetMapping getResultSetMapping();

View File

@ -20,7 +20,7 @@ public interface ParameterInterpretation {
* Access to the defined parameters in the order they were encountered, * Access to the defined parameters in the order they were encountered,
* potentially including "duplicate references" to a single parameter * potentially including "duplicate references" to a single parameter
*/ */
List<QueryParameterImplementor<?>> getOccurrenceOrderedParameters(); List<ParameterOccurrence> getOrderedParameterOccurrences();
/** /**
* Create the ParameterMetadata representation of this interpretation * Create the ParameterMetadata representation of this interpretation

View File

@ -0,0 +1,31 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.query.sql.spi;
import org.hibernate.query.spi.QueryParameterImplementor;
/**
* @author Christian Beikov
*/
public final class ParameterOccurrence {
private final QueryParameterImplementor<?> parameter;
private final int sourcePosition;
public ParameterOccurrence(QueryParameterImplementor<?> parameter, int sourcePosition) {
this.parameter = parameter;
this.sourcePosition = sourcePosition;
}
public QueryParameterImplementor<?> getParameter() {
return parameter;
}
public int getSourcePosition() {
return sourcePosition;
}
}

View File

@ -6,11 +6,9 @@
*/ */
package org.hibernate.query.sqm; package org.hibernate.query.sqm;
import java.util.Locale;
import jakarta.persistence.metamodel.Bindable; import jakarta.persistence.metamodel.Bindable;
import org.hibernate.metamodel.model.domain.DomainType; import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.tree.SqmExpressableAccessor; import org.hibernate.query.sqm.tree.SqmExpressableAccessor;
import org.hibernate.query.sqm.tree.domain.SqmPath; import org.hibernate.query.sqm.tree.domain.SqmPath;
@ -52,20 +50,4 @@ public interface SqmPathSource<J> extends SqmExpressable<J>, Bindable<J>, SqmExp
default SqmExpressable<J> getExpressable() { default SqmExpressable<J> getExpressable() {
return (SqmExpressable<J>) getSqmPathType(); return (SqmExpressable<J>) getSqmPathType();
} }
default <X extends DomainType> X sqmAs(Class<X> targetType) {
if ( targetType.isInstance( this ) ) {
//noinspection unchecked
return (X) this;
}
throw new IllegalArgumentException(
String.format(
Locale.ROOT,
"`%s` cannot be treated as `%s`",
getClass().getName(),
targetType.getName()
)
);
}
} }

View File

@ -31,6 +31,7 @@ public class StrictJpaComplianceViolation extends SemanticException {
IDENTIFICATION_VARIABLE_NOT_DECLARED_IN_FROM_CLAUSE( "use of an alias not declared in the FROM clause" ), IDENTIFICATION_VARIABLE_NOT_DECLARED_IN_FROM_CLAUSE( "use of an alias not declared in the FROM clause" ),
FQN_ENTITY_NAME( "use of FQN for entity name" ), FQN_ENTITY_NAME( "use of FQN for entity name" ),
IMPLICIT_TREAT( "use of implicit treat" ), IMPLICIT_TREAT( "use of implicit treat" ),
MIXED_POSITIONAL_NAMED_PARAMETERS( "mix of positional and named parameters" ),
; ;
private final String description; private final String description;

View File

@ -47,16 +47,16 @@ public class DomainParameterXref {
final Map<SqmParameter,QueryParameterImplementor<?>> xrefMap = new TreeMap<>( final Map<SqmParameter,QueryParameterImplementor<?>> xrefMap = new TreeMap<>(
(o1, o2) -> { (o1, o2) -> {
if ( o1 instanceof SqmNamedParameter ) { if ( o1 instanceof SqmNamedParameter ) {
final SqmNamedParameter one = (SqmNamedParameter) o1; final SqmNamedParameter<?> one = (SqmNamedParameter<?>) o1;
final SqmNamedParameter another = (SqmNamedParameter) o2; return o2 instanceof SqmNamedParameter<?>
? one.getName().compareTo( ((SqmNamedParameter<?>) o2).getName() )
return one.getName().compareTo( another.getName() ); : -1;
} }
else if ( o1 instanceof SqmPositionalParameter ) { else if ( o1 instanceof SqmPositionalParameter ) {
final SqmPositionalParameter one = (SqmPositionalParameter) o1; final SqmPositionalParameter<?> one = (SqmPositionalParameter<?>) o1;
final SqmPositionalParameter another = (SqmPositionalParameter) o2; return o2 instanceof SqmPositionalParameter<?>
? one.getPosition().compareTo( ( (SqmPositionalParameter<?>) o2 ).getPosition() )
return one.getPosition().compareTo( another.getPosition() ); : 1;
} }
else if ( o1 instanceof SqmJpaCriteriaParameterWrapper else if ( o1 instanceof SqmJpaCriteriaParameterWrapper
&& o2 instanceof SqmJpaCriteriaParameterWrapper ) { && o2 instanceof SqmJpaCriteriaParameterWrapper ) {
@ -101,7 +101,7 @@ public class DomainParameterXref {
sqmParameter, sqmParameter,
p -> { p -> {
if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper ) { if ( sqmParameter instanceof SqmJpaCriteriaParameterWrapper ) {
return ( (SqmJpaCriteriaParameterWrapper) sqmParameter ).getJpaCriteriaParameter(); return ( (SqmJpaCriteriaParameterWrapper<?>) sqmParameter ).getJpaCriteriaParameter();
} }
else if ( sqmParameter.getName() != null ) { else if ( sqmParameter.getName() != null ) {
return QueryParameterNamedImpl.fromSqm( sqmParameter ); return QueryParameterNamedImpl.fromSqm( sqmParameter );

View File

@ -670,7 +670,6 @@ public class QuerySqmImpl<R>
return lockMode != null && lockMode.greaterThan( LockMode.READ ); return lockMode != null && lockMode.greaterThan( LockMode.READ );
} }
@SuppressWarnings("unchecked")
private SelectQueryPlan<R> resolveSelectQueryPlan() { private SelectQueryPlan<R> resolveSelectQueryPlan() {
// resolve (or make) the QueryPlan. This QueryPlan might be an aggregation of multiple plans. // resolve (or make) the QueryPlan. This QueryPlan might be an aggregation of multiple plans.
// //

View File

@ -201,7 +201,7 @@ public class MatchingIdSelectionHelper {
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter( final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(
entityDescriptor, entityDescriptor,
sqmMutationStatement.getTarget().getExplicitAlias(), sqmMutationStatement.getTarget(),
domainParameterXref, domainParameterXref,
executionContext.getQueryOptions(), executionContext.getQueryOptions(),
executionContext.getSession().getLoadQueryInfluencers(), executionContext.getSession().getLoadQueryInfluencers(),

View File

@ -14,7 +14,6 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.Stack;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.MappingModelExpressable; import org.hibernate.metamodel.mapping.MappingModelExpressable;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.spi.QueryOptions; import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings; import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref; import org.hibernate.query.sqm.internal.DomainParameterXref;
@ -23,6 +22,7 @@ import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl; import org.hibernate.query.sqm.sql.internal.SqlAstProcessingStateImpl;
import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl; import org.hibernate.query.sqm.sql.internal.SqlAstQueryPartProcessingStateImpl;
import org.hibernate.query.sqm.tree.expression.SqmParameter; import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.predicate.SqmWhereClause; import org.hibernate.query.sqm.tree.predicate.SqmWhereClause;
import org.hibernate.query.sqm.tree.select.SqmSelectClause; import org.hibernate.query.sqm.tree.select.SqmSelectClause;
import org.hibernate.query.sqm.tree.update.SqmAssignment; import org.hibernate.query.sqm.tree.update.SqmAssignment;
@ -63,7 +63,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
public MultiTableSqmMutationConverter( public MultiTableSqmMutationConverter(
EntityMappingType mutatingEntityDescriptor, EntityMappingType mutatingEntityDescriptor,
String mutatingEntityExplicitAlias, SqmRoot<?> sqmRoot,
DomainParameterXref domainParameterXref, DomainParameterXref domainParameterXref,
QueryOptions queryOptions, QueryOptions queryOptions,
LoadQueryInfluencers loadQueryInfluencers, LoadQueryInfluencers loadQueryInfluencers,
@ -71,8 +71,8 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
SqlAstCreationContext creationContext) { SqlAstCreationContext creationContext) {
this( this(
mutatingEntityDescriptor, mutatingEntityDescriptor,
mutatingEntityExplicitAlias, sqmRoot,
mutatingEntityExplicitAlias, sqmRoot.getExplicitAlias(),
domainParameterXref, domainParameterXref,
queryOptions, queryOptions,
loadQueryInfluencers, loadQueryInfluencers,
@ -83,7 +83,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
public MultiTableSqmMutationConverter( public MultiTableSqmMutationConverter(
EntityMappingType mutatingEntityDescriptor, EntityMappingType mutatingEntityDescriptor,
String mutatingEntityExplicitAlias, SqmRoot<?> sqmRoot,
String sourceAlias, String sourceAlias,
DomainParameterXref domainParameterXref, DomainParameterXref domainParameterXref,
QueryOptions queryOptions, QueryOptions queryOptions,
@ -101,10 +101,9 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
pushProcessingState( rootProcessingState ); pushProcessingState( rootProcessingState );
final NavigablePath navigablePath = new NavigablePath( mutatingEntityDescriptor.getEntityName(), mutatingEntityExplicitAlias );
this.mutatingTableGroup = mutatingEntityDescriptor.createRootTableGroup( this.mutatingTableGroup = mutatingEntityDescriptor.createRootTableGroup(
true, true,
navigablePath, sqmRoot.getNavigablePath(),
sourceAlias, sourceAlias,
// We don't care about the discriminator predicate, // We don't care about the discriminator predicate,
// but we pass non-null to ensure table reference join predicates are generated // but we pass non-null to ensure table reference join predicates are generated
@ -112,7 +111,7 @@ public class MultiTableSqmMutationConverter extends BaseSqmToSqlAstConverter<Sta
this, this,
creationContext.getSessionFactory() ); creationContext.getSessionFactory() );
getFromClauseAccess().registerTableGroup( navigablePath, mutatingTableGroup ); getFromClauseAccess().registerTableGroup( sqmRoot.getNavigablePath(), mutatingTableGroup );
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")

View File

@ -117,7 +117,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter( final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(
entityDescriptor, entityDescriptor,
sqmMutationStatement.getTarget().getExplicitAlias(), sqmMutationStatement.getTarget(),
explicitDmlTargetAlias, explicitDmlTargetAlias,
domainParameterXref, domainParameterXref,
executionContext.getQueryOptions(), executionContext.getQueryOptions(),
@ -324,7 +324,7 @@ public abstract class AbstractCteMutationHandler extends AbstractMutationHandler
); );
} }
else { else {
return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression ); return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression, true, true );
} }
} }

View File

@ -110,7 +110,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
converter = new MultiTableSqmMutationConverter( converter = new MultiTableSqmMutationConverter(
entityDescriptor, entityDescriptor,
sqmDelete.getTarget().getExplicitAlias(), sqmDelete.getTarget(),
domainParameterXref, domainParameterXref,
queryOptions, queryOptions,
loadQueryInfluencers, loadQueryInfluencers,
@ -288,7 +288,7 @@ public class RestrictedDeleteExecutionDelegate implements TableBasedDeleteHandle
); );
} }
else { else {
return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression ); return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression, true, true );
} }
} }

View File

@ -126,7 +126,7 @@ public class TableBasedUpdateHandler
final MultiTableSqmMutationConverter converterDelegate = new MultiTableSqmMutationConverter( final MultiTableSqmMutationConverter converterDelegate = new MultiTableSqmMutationConverter(
entityDescriptor, entityDescriptor,
getSqmDeleteOrUpdateStatement().getTarget().getExplicitAlias(), getSqmDeleteOrUpdateStatement().getTarget(),
domainParameterXref, domainParameterXref,
executionContext.getQueryOptions(), executionContext.getQueryOptions(),
executionContext.getSession().getLoadQueryInfluencers(), executionContext.getSession().getLoadQueryInfluencers(),

View File

@ -230,7 +230,7 @@ public class UpdateExecutionDelegate implements TableBasedUpdateHandler.Executio
); );
} }
else { else {
return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression ); return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression, true, true );
} }
} }

View File

@ -16,13 +16,17 @@ import org.hibernate.query.sqm.tree.domain.SqmPath;
*/ */
public class SqmCreationHelper { public class SqmCreationHelper {
public static NavigablePath buildRootNavigablePath(String base, String alias) { public static NavigablePath buildRootNavigablePath(String base, String alias) {
// Make sure we always create a unique alias, otherwise we might use a wrong table group for the same join
return alias == null return alias == null
? new NavigablePath( base ) ? new NavigablePath( base, Long.toString( System.nanoTime() ) )
: new NavigablePath( base, alias ); : new NavigablePath( base, alias );
} }
public static NavigablePath buildSubNavigablePath(NavigablePath lhs, String base, String alias) { public static NavigablePath buildSubNavigablePath(NavigablePath lhs, String base, String alias) {
return lhs.append( base, alias ); // Make sure we always create a unique alias, otherwise we might use a wrong table group for the same join
return alias == null
? lhs.append( base, Long.toString( System.nanoTime() ) )
: lhs.append( base, alias );
} }
public static NavigablePath buildSubNavigablePath(SqmPath<?> lhs, String subNavigable, String alias) { public static NavigablePath buildSubNavigablePath(SqmPath<?> lhs, String subNavigable, String alias) {

View File

@ -12,6 +12,7 @@ import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
@ -88,6 +89,7 @@ import org.hibernate.query.sqm.tree.domain.SqmCorrelatedRootJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelation; import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression; import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression; import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.domain.SqmTreatedRoot;
import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification; import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
@ -597,6 +599,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final SqmStatement<?> sqmStatement = getStatement(); final SqmStatement<?> sqmStatement = getStatement();
//noinspection unchecked //noinspection unchecked
final T statement = (T) sqmStatement.accept( this ); final T statement = (T) sqmStatement.accept( this );
pruneTableGroupJoins();
return new StandardSqmTranslation<>( return new StandardSqmTranslation<>(
statement, statement,
getJdbcParamsBySqmParam(), getJdbcParamsBySqmParam(),
@ -1988,13 +1991,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
additionalRestrictions, additionalRestrictions,
new ComparisonPredicate( new ComparisonPredicate(
new ColumnReference( new ColumnReference(
parentTableGroup.getTableReference( navigablePath, selectable.getContainingTableExpression() ), parentTableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
selectable, selectable,
sessionFactory sessionFactory
), ),
ComparisonOperator.EQUAL, ComparisonOperator.EQUAL,
new ColumnReference( new ColumnReference(
tableGroup.getTableReference( navigablePath, selectable.getContainingTableExpression() ), tableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
selectable, selectable,
sessionFactory sessionFactory
) )
@ -2009,14 +2012,14 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
(index, selectable) -> { (index, selectable) -> {
lhs.add( lhs.add(
new ColumnReference( new ColumnReference(
parentTableGroup.getTableReference( navigablePath, selectable.getContainingTableExpression() ), parentTableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
selectable, selectable,
sessionFactory sessionFactory
) )
); );
rhs.add( rhs.add(
new ColumnReference( new ColumnReference(
tableGroup.getTableReference( navigablePath, selectable.getContainingTableExpression() ), tableGroup.resolveTableReference( navigablePath, selectable.getContainingTableExpression() ),
selectable, selectable,
sessionFactory sessionFactory
) )
@ -2095,13 +2098,58 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return creationContext.getDomainModel().getEntityDescriptor( entityDomainType.getHibernateEntityName() ); return creationContext.getDomainModel().getEntityDescriptor( entityDomainType.getHibernateEntityName() );
} }
private final Map<TableGroup, Set<String>> tableGroupTreatUsages = new IdentityHashMap<>();
protected void registerUsage(SqmFrom<?, ?> sqmFrom, TableGroup tableGroup) {
final EntityDomainType<?> treatedType;
if ( sqmFrom instanceof SqmTreatedPath<?, ?> ) {
treatedType = ( (SqmTreatedPath<?, ?>) sqmFrom ).getTreatTarget();
}
else if ( sqmFrom.getReferencedPathSource().getSqmPathType() instanceof EntityDomainType<?> ) {
treatedType = (EntityDomainType<?>) sqmFrom.getReferencedPathSource().getSqmPathType();
}
else {
return;
}
final Set<String> treatedEntityNames = tableGroupTreatUsages.computeIfAbsent(
tableGroup,
tg -> new HashSet<>( 1 )
);
treatedEntityNames.add( treatedType.getHibernateEntityName() );
}
protected void pruneTableGroupJoins() {
for ( Map.Entry<TableGroup, Set<String>> entry : tableGroupTreatUsages.entrySet() ) {
final TableGroup tableGroup = entry.getKey();
final Set<String> treatedEntityNames = entry.getValue();
final ModelPartContainer modelPart = tableGroup.getModelPart();
final EntityPersister tableGroupPersister;
if ( modelPart instanceof PluralAttributeMapping ) {
tableGroupPersister = (EntityPersister) ( (PluralAttributeMapping) modelPart )
.getElementDescriptor()
.getPartMappingType();
}
else {
tableGroupPersister = (EntityPersister) modelPart.getPartMappingType();
}
tableGroupPersister.pruneForSubclasses( tableGroup, treatedEntityNames );
}
}
protected void consumeExplicitJoins(SqmFrom<?, ?> sqmFrom, TableGroup lhsTableGroup) { protected void consumeExplicitJoins(SqmFrom<?, ?> sqmFrom, TableGroup lhsTableGroup) {
if ( log.isTraceEnabled() ) { if ( log.isTraceEnabled() ) {
log.tracef( "Visiting explicit joins for `%s`", sqmFrom.getNavigablePath() ); log.tracef( "Visiting explicit joins for `%s`", sqmFrom.getNavigablePath() );
} }
sqmFrom.visitSqmJoins( sqmFrom.visitSqmJoins(
sqmJoin -> consumeExplicitJoin( sqmJoin, lhsTableGroup, lhsTableGroup, true ) sqmJoin -> {
registerUsage( (SqmFrom<?, ?>) sqmJoin.getLhs(), lhsTableGroup );
consumeExplicitJoin( sqmJoin, lhsTableGroup, lhsTableGroup, true );
}
); );
for ( SqmFrom<?, ?> sqmTreat : sqmFrom.getSqmTreats() ) {
registerUsage( sqmTreat, lhsTableGroup );
consumeExplicitJoins( sqmTreat, lhsTableGroup );
}
} }
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@ -2221,7 +2269,8 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
private NavigablePath getJoinNavigablePath( private NavigablePath getJoinNavigablePath(
NavigablePath sqmJoinNavigablePath, NavigablePath sqmJoinNavigablePath,
NavigablePath parentNavigablePath, String partName) { NavigablePath parentNavigablePath,
String partName) {
if ( parentNavigablePath == null ) { if ( parentNavigablePath == null ) {
return sqmJoinNavigablePath; return sqmJoinNavigablePath;
} }
@ -2327,7 +2376,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
JpaPath<?> sqmPath, JpaPath<?> sqmPath,
boolean useInnerJoin, boolean useInnerJoin,
Consumer<TableGroup> implicitJoinChecker) { Consumer<TableGroup> implicitJoinChecker) {
final JpaPath<?> parentPath = sqmPath.getParentPath(); final JpaPath<?> parentPath;
if ( sqmPath instanceof SqmTreatedPath<?, ?> ) {
parentPath = ( (SqmTreatedPath<?, ?>) sqmPath ).getWrappedPath();
}
else {
parentPath = sqmPath.getParentPath();
}
final TableGroup tableGroup = fromClauseIndex.findTableGroup( parentPath.getNavigablePath() ); final TableGroup tableGroup = fromClauseIndex.findTableGroup( parentPath.getNavigablePath() );
if ( tableGroup == null ) { if ( tableGroup == null ) {
final TableGroup parentTableGroup = prepareReusablePath( final TableGroup parentTableGroup = prepareReusablePath(
@ -2336,17 +2391,31 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
useInnerJoin, useInnerJoin,
implicitJoinChecker implicitJoinChecker
); );
if ( parentPath instanceof SqmTreatedPath<?, ?> ) {
fromClauseIndex.register( (SqmPath<?>) parentPath, parentTableGroup );
}
final TableGroup newTableGroup = createTableGroup( parentTableGroup, (SqmPath<?>) parentPath, useInnerJoin ); final TableGroup newTableGroup = createTableGroup( parentTableGroup, (SqmPath<?>) parentPath, useInnerJoin );
if ( newTableGroup != null ) { if ( newTableGroup != null ) {
implicitJoinChecker.accept( newTableGroup ); implicitJoinChecker.accept( newTableGroup );
if ( sqmPath instanceof SqmFrom<?, ?> ) {
registerUsage( (SqmFrom<?, ?>) sqmPath, newTableGroup );
}
} }
return newTableGroup; return newTableGroup;
} }
else if ( sqmPath instanceof SqmTreatedPath<?, ?> ) {
fromClauseIndex.register( (SqmPath<?>) sqmPath, tableGroup );
if ( sqmPath instanceof SqmFrom<?, ?> ) {
registerUsage( (SqmFrom<?, ?>) sqmPath, tableGroup );
}
}
else if ( parentPath instanceof SqmFrom<?, ?> ) {
registerUsage( (SqmFrom<?, ?>) parentPath, tableGroup );
}
return tableGroup; return tableGroup;
} }
private void prepareForSelection(SqmPath<?> joinedPath) { private void prepareForSelection(SqmPath<?> joinedPath) {
final SqmPath<?> lhsPath = joinedPath.getLhs();
final FromClauseIndex fromClauseIndex = getFromClauseIndex(); final FromClauseIndex fromClauseIndex = getFromClauseIndex();
final TableGroup tableGroup = fromClauseIndex.findTableGroup( joinedPath.getNavigablePath() ); final TableGroup tableGroup = fromClauseIndex.findTableGroup( joinedPath.getNavigablePath() );
if ( tableGroup == null ) { if ( tableGroup == null ) {
@ -2354,24 +2423,29 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final NavigablePath navigablePath; final NavigablePath navigablePath;
if ( CollectionPart.Nature.fromNameExact( joinedPath.getNavigablePath().getUnaliasedLocalName() ) != null ) { if ( CollectionPart.Nature.fromNameExact( joinedPath.getNavigablePath().getUnaliasedLocalName() ) != null ) {
navigablePath = lhsPath.getLhs().getNavigablePath(); navigablePath = joinedPath.getLhs().getLhs().getNavigablePath();
}
else if ( joinedPath instanceof SqmTreatedRoot<?, ?> ) {
navigablePath = ( (SqmTreatedRoot<?, ?>) joinedPath ).getWrappedPath().getNavigablePath();
} }
else { else {
navigablePath = lhsPath.getNavigablePath(); navigablePath = joinedPath.getLhs().getNavigablePath();
} }
// todo (6.0): check again if this really is a "requirement" by the JPA spec and document the reference if it is. // todo (6.0): check again if this really is a "requirement" by the JPA spec and document the reference if it is.
// The additional join will filter rows, so there would be no way to just select the FK for a nullable association // The additional join will filter rows, so there would be no way to just select the FK for a nullable association
final boolean useInnerJoin = true; final boolean useInnerJoin = true;
// INNER join semantics are required per the JPA spec for select items. // INNER join semantics are required per the JPA spec for select items.
createTableGroup( fromClauseIndex.getTableGroup( navigablePath ), joinedPath, useInnerJoin ); final TableGroup createdTableGroup = createTableGroup(
} fromClauseIndex.getTableGroup( navigablePath ),
// When we select a treated path, we must add the type restriction as where clause predicate joinedPath,
if ( joinedPath instanceof SqmTreatedPath<?, ?> ) { useInnerJoin
final SqmTreatedPath<?, ?> treatedPath = (SqmTreatedPath<?, ?>) joinedPath;
// todo (6.0): the persister and the table group should participate so we can optimize the joins/selects
currentQuerySpec().applyPredicate(
createTreatTypeRestriction( treatedPath.getWrappedPath(), treatedPath.getTreatTarget() )
); );
if ( createdTableGroup != null && joinedPath instanceof SqmTreatedPath<?, ?> ) {
fromClauseIndex.register( joinedPath, createdTableGroup );
}
}
else if ( joinedPath instanceof SqmFrom<?, ?> ) {
registerUsage( (SqmFrom<?, ?>) joinedPath, tableGroup );
} }
} }
@ -3000,7 +3074,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
(selectionIndex, selectionMapping) -> { (selectionIndex, selectionMapping) -> {
tupleElements.add( tupleElements.add(
new ColumnReference( new ColumnReference(
tableGroup.getTableReference( tableGroup.resolveTableReference(
navigablePath, navigablePath,
selectionMapping.getContainingTableExpression() selectionMapping.getContainingTableExpression()
), ),
@ -5254,9 +5328,18 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
tableGroup tableGroup
); );
if ( manyToManyFilterPredicate != null ) { if ( manyToManyFilterPredicate != null ) {
assert tableGroup.getTableReferenceJoins() != null && TableGroupJoin elementTableGroupJoin = null;
tableGroup.getTableReferenceJoins().size() == 1; for ( TableGroupJoin nestedTableGroupJoin : tableGroup.getNestedTableGroupJoins() ) {
tableGroup.getTableReferenceJoins().get( 0 ).applyPredicate( manyToManyFilterPredicate ); final NavigablePath navigablePath = nestedTableGroupJoin.getNavigablePath();
if ( navigablePath.getParent() == tableGroup.getNavigablePath()
&& CollectionPart.Nature.ELEMENT.getName().equals( navigablePath.getUnaliasedLocalName() ) ) {
elementTableGroupJoin = nestedTableGroupJoin;
break;
}
}
assert elementTableGroupJoin != null;
elementTableGroupJoin.applyPredicate( manyToManyFilterPredicate );
} }
} }

View File

@ -12,7 +12,6 @@ import java.util.List;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping; import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.metamodel.mapping.BasicValuedModelPart; import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EntityAssociationMapping; import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping; import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping; import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
@ -20,27 +19,19 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.mapping.EntityValuedModelPart; import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor; import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ModelPart; import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer; import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor; import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
import org.hibernate.query.NavigablePath; import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter; import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath; import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.SqlAstWalker; import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.FromClauseAccess;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver; import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference; import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression; import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.SqlTuple; import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer; import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
import org.hibernate.sql.ast.tree.from.TableGroup; import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference; import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState; import org.hibernate.sql.results.graph.DomainResultCreationState;
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey; import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
@ -53,16 +44,9 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
public static <T> EntityValuedPathInterpretation<T> from( public static <T> EntityValuedPathInterpretation<T> from(
SqmEntityValuedSimplePath<T> sqmPath, SqmEntityValuedSimplePath<T> sqmPath,
SqmToSqlAstConverter sqlAstCreationState) { SqmToSqlAstConverter sqlAstCreationState) {
final SqmPath<?> realPath;
if ( CollectionPart.Nature.fromNameExact( sqmPath.getNavigablePath().getUnaliasedLocalName() ) != null ) {
realPath = sqmPath.getLhs();
}
else {
realPath = sqmPath;
}
final TableGroup tableGroup = sqlAstCreationState final TableGroup tableGroup = sqlAstCreationState
.getFromClauseAccess() .getFromClauseAccess()
.findTableGroup( realPath.getLhs().getNavigablePath() ); .findTableGroup( sqmPath.getLhs().getNavigablePath() );
final EntityValuedModelPart mapping = (EntityValuedModelPart) sqlAstCreationState final EntityValuedModelPart mapping = (EntityValuedModelPart) sqlAstCreationState
.getFromClauseAccess() .getFromClauseAccess()
@ -124,19 +108,6 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
final String lhsTable; final String lhsTable;
final ModelPart lhsPart; final ModelPart lhsPart;
boolean useKeyPart = associationMapping.getSideNature() == ForeignKeyDescriptor.Nature.KEY; boolean useKeyPart = associationMapping.getSideNature() == ForeignKeyDescriptor.Nature.KEY;
if ( mapping instanceof EntityCollectionPart ) {
// EntityCollectionPart always returns TARGET, but sometimes we still want to use the KEY (element) side.
// With a collection table, we can always use the TARGET for referring to the collection,
// but in case of a one-to-many without collection table, this would be problematic,
// as we can't use e.g. the primary key of the owner entity as substitution.
final TableGroup pluralTableGroup = sqlAstCreationState.getFromClauseAccess()
.findTableGroup( navigablePath.getParent() );
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) pluralTableGroup.getModelPart();
if ( pluralAttributeMapping.getSeparateCollectionTable() == null ) {
useKeyPart = true;
tableGroup = pluralTableGroup;
}
}
if ( useKeyPart ) { if ( useKeyPart ) {
lhsTable = fkDescriptor.getKeyTable(); lhsTable = fkDescriptor.getKeyTable();
lhsPart = fkDescriptor.getKeyPart(); lhsPart = fkDescriptor.getKeyPart();
@ -281,46 +252,4 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
return (EntityValuedModelPart) super.getExpressionType(); return (EntityValuedModelPart) super.getExpressionType();
} }
@Override
public DomainResult<T> createDomainResult(String resultVariable, DomainResultCreationState creationState) {
final EntityValuedModelPart mappingType = getExpressionType();
if ( mappingType instanceof EntityAssociationMapping ) {
final NavigablePath navigablePath = getNavigablePath();
// for a to-one or to-many we may not have yet joined to the association table,
// but we need to because the association is a root return and needs to select
// all of the entity columns
final SqlAstCreationState sqlAstCreationState = creationState.getSqlAstCreationState();
final FromClauseAccess fromClauseAccess = sqlAstCreationState.getFromClauseAccess();
final EntityAssociationMapping associationMapping = (EntityAssociationMapping) mappingType;
final TableGroup tableGroup = fromClauseAccess.resolveTableGroup(
navigablePath,
np -> {
final TableGroup parentTableGroup;
if ( getExpressionType() instanceof CollectionPart ) {
parentTableGroup = fromClauseAccess.findTableGroup( np.getParent().getParent() );
}
else {
parentTableGroup = getTableGroup();
}
final TableGroupJoin tableGroupJoin = associationMapping.createTableGroupJoin(
navigablePath,
parentTableGroup,
null,
SqlAstJoinType.INNER,
false,
sqlAstCreationState
);
parentTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup();
}
);
return associationMapping.createDomainResult( navigablePath, tableGroup, resultVariable, creationState );
}
return super.createDomainResult( resultVariable, creationState );
}
} }

View File

@ -11,6 +11,7 @@ import jakarta.persistence.criteria.JoinType;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
import org.hibernate.metamodel.model.domain.PersistentAttribute; import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaPredicate; import org.hibernate.query.criteria.JpaPredicate;
import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.NodeBuilder;
@ -48,9 +49,28 @@ public abstract class AbstractSqmAttributeJoin<O,T>
SqmJoinType joinType, SqmJoinType joinType,
boolean fetched, boolean fetched,
NodeBuilder nodeBuilder) { NodeBuilder nodeBuilder) {
this(
lhs,
SqmCreationHelper.buildSubNavigablePath( lhs, joinedNavigable.getName(), alias ),
joinedNavigable,
alias,
joinType,
fetched,
nodeBuilder
);
}
protected AbstractSqmAttributeJoin(
SqmFrom<?,O> lhs,
NavigablePath navigablePath,
SqmJoinable joinedNavigable,
String alias,
SqmJoinType joinType,
boolean fetched,
NodeBuilder nodeBuilder) {
//noinspection unchecked //noinspection unchecked
super( super(
SqmCreationHelper.buildSubNavigablePath( lhs, joinedNavigable.getName(), alias ), navigablePath,
(SqmPathSource<T>) joinedNavigable, (SqmPathSource<T>) joinedNavigable,
lhs, lhs,
alias, alias,

View File

@ -10,6 +10,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -39,7 +40,6 @@ import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.SemanticException; import org.hibernate.query.SemanticException;
import org.hibernate.query.sqm.SqmPathSource; import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.UnknownPathException; import org.hibernate.query.sqm.UnknownPathException;
import org.hibernate.query.hql.spi.SemanticPathPart;
import org.hibernate.query.hql.spi.SqmCreationState; import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.spi.SqmCreationHelper; import org.hibernate.query.sqm.spi.SqmCreationHelper;
import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.SqmJoinType;
@ -58,6 +58,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
private String alias; private String alias;
private List<SqmJoin<T, ?>> joins; private List<SqmJoin<T, ?>> joins;
private List<SqmFrom<?, ?>> treats;
protected AbstractSqmFrom( protected AbstractSqmFrom(
NavigablePath navigablePath, NavigablePath navigablePath,
@ -168,6 +169,38 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
} }
} }
@Override
public boolean hasTreats() {
return treats != null && !treats.isEmpty();
}
@Override
public List<SqmFrom<?, ?>> getSqmTreats() {
return treats == null ? Collections.emptyList() : treats;
}
protected <S, X extends SqmFrom<?, S>> X findTreat(EntityDomainType<S> targetType, String alias) {
if ( treats != null ) {
for ( SqmFrom<?, ?> treat : treats ) {
if ( treat.getModel() == targetType ) {
if ( treat.getExplicitAlias() == null && alias == null
|| Objects.equals( treat.getExplicitAlias(), alias ) ) {
//noinspection unchecked
return (X) treat;
}
}
}
}
return null;
}
protected <X extends SqmFrom<?, ?>> X addTreat(X treat) {
if ( treats == null ) {
treats = new ArrayList<>();
}
treats.add( treat );
return treat;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JPA // JPA

View File

@ -59,18 +59,6 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
return (SqmPathSource<T>) super.getNodeType(); return (SqmPathSource<T>) super.getNodeType();
} }
@SuppressWarnings("WeakerAccess")
protected AbstractSqmPath(SqmPathSource<T> referencedPathSource, SqmPath<?> lhs, NodeBuilder nodeBuilder) {
this(
lhs == null
? new NavigablePath( referencedPathSource.getPathName() )
: lhs.getNavigablePath().append( referencedPathSource.getPathName() ),
referencedPathSource,
lhs,
nodeBuilder
);
}
@Override @Override
public NavigablePath getNavigablePath() { public NavigablePath getNavigablePath() {
return navigablePath; return navigablePath;

View File

@ -9,8 +9,10 @@ package org.hibernate.query.sqm.tree.domain;
import jakarta.persistence.criteria.PluralJoin; import jakarta.persistence.criteria.PluralJoin;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute; import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.criteria.JpaJoin; import org.hibernate.query.criteria.JpaJoin;
import org.hibernate.query.sqm.NodeBuilder; import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SqmJoinable;
import org.hibernate.query.sqm.tree.SqmJoinType; import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmFrom; import org.hibernate.query.sqm.tree.from.SqmFrom;
@ -27,14 +29,18 @@ public abstract class AbstractSqmPluralJoin<O,C,E> extends AbstractSqmAttributeJ
SqmJoinType joinType, SqmJoinType joinType,
boolean fetched, boolean fetched,
NodeBuilder nodeBuilder) { NodeBuilder nodeBuilder) {
super( super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
lhs, }
joinedNavigable,
alias, protected AbstractSqmPluralJoin(
joinType, SqmFrom<?, O> lhs,
fetched, NavigablePath navigablePath,
nodeBuilder PluralPersistentAttribute<O,C,E> joinedNavigable,
); String alias,
SqmJoinType joinType,
boolean fetched,
NodeBuilder nodeBuilder) {
super( lhs, navigablePath, joinedNavigable, alias, joinType, fetched, nodeBuilder );
} }
@Override @Override

View File

@ -12,7 +12,7 @@ import jakarta.persistence.criteria.Predicate;
import org.hibernate.metamodel.model.domain.BagPersistentAttribute; import org.hibernate.metamodel.model.domain.BagPersistentAttribute;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.PathException; import org.hibernate.query.NavigablePath;
import org.hibernate.query.criteria.JpaCollectionJoin; import org.hibernate.query.criteria.JpaCollectionJoin;
import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaPredicate; import org.hibernate.query.criteria.JpaPredicate;
@ -36,6 +36,17 @@ public class SqmBagJoin<O, E> extends AbstractSqmPluralJoin<O,Collection<E>, E>
super( lhs, attribute, alias, sqmJoinType, fetched, nodeBuilder ); super( lhs, attribute, alias, sqmJoinType, fetched, nodeBuilder );
} }
protected SqmBagJoin(
SqmFrom<?, O> lhs,
NavigablePath navigablePath,
BagPersistentAttribute<O,E> attribute,
String alias,
SqmJoinType joinType,
boolean fetched,
NodeBuilder nodeBuilder) {
super( lhs, navigablePath, attribute, alias, joinType, fetched, nodeBuilder );
}
@Override @Override
public BagPersistentAttribute<O,E> getReferencedPathSource() { public BagPersistentAttribute<O,E> getReferencedPathSource() {
return (BagPersistentAttribute<O,E>) super.getReferencedPathSource(); return (BagPersistentAttribute<O,E>) super.getReferencedPathSource();
@ -85,15 +96,27 @@ public class SqmBagJoin<O, E> extends AbstractSqmPluralJoin<O,Collection<E>, E>
} }
@Override @Override
public <S extends E> SqmTreatedBagJoin<O,E,S> treatAs(EntityDomainType<S> treatTarget) throws PathException { public <S extends E> SqmTreatedBagJoin<O,E,S> treatAs(EntityDomainType<S> treatTarget) {
//noinspection unchecked return treatAs( treatTarget, null );
return new SqmTreatedBagJoin( this, treatTarget, null );
} }
@Override @Override
public SqmAttributeJoin makeCopy(SqmCreationProcessingState creationProcessingState) { public <S extends E> SqmTreatedBagJoin<O,E,S> treatAs(Class<S> treatJavaType, String alias) {
//noinspection unchecked return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias );
return new SqmBagJoin( }
@Override
public <S extends E> SqmTreatedBagJoin<O,E,S> treatAs(EntityDomainType<S> treatTarget, String alias) {
final SqmTreatedBagJoin<O,E,S> treat = findTreat( treatTarget, alias );
if ( treat == null ) {
return addTreat( new SqmTreatedBagJoin<>( this, treatTarget, alias ) );
}
return treat;
}
@Override
public SqmAttributeJoin<O, E> makeCopy(SqmCreationProcessingState creationProcessingState) {
return new SqmBagJoin<>(
creationProcessingState.getPathRegistry().findFromByPath( getLhs().getNavigablePath() ), creationProcessingState.getPathRegistry().findFromByPath( getLhs().getNavigablePath() ),
getReferencedPathSource(), getReferencedPathSource(),
getExplicitAlias(), getExplicitAlias(),

View File

@ -12,6 +12,8 @@ import jakarta.persistence.criteria.Predicate;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ListPersistentAttribute; import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.PathException; import org.hibernate.query.PathException;
import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaListJoin; import org.hibernate.query.criteria.JpaListJoin;
@ -39,6 +41,17 @@ public class SqmListJoin<O,E>
super( lhs, listAttribute, alias, sqmJoinType, fetched, nodeBuilder ); super( lhs, listAttribute, alias, sqmJoinType, fetched, nodeBuilder );
} }
protected SqmListJoin(
SqmFrom<?, O> lhs,
NavigablePath navigablePath,
ListPersistentAttribute<O, E> listAttribute,
String alias,
SqmJoinType joinType,
boolean fetched,
NodeBuilder nodeBuilder) {
super( lhs, navigablePath, listAttribute, alias, joinType, fetched, nodeBuilder );
}
@Override @Override
public ListPersistentAttribute<O, E> getReferencedPathSource() { public ListPersistentAttribute<O, E> getReferencedPathSource() {
return (ListPersistentAttribute<O, E>) super.getReferencedPathSource(); return (ListPersistentAttribute<O, E>) super.getReferencedPathSource();
@ -88,12 +101,26 @@ public class SqmListJoin<O,E>
@Override @Override
public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(Class<S> treatAsType) { public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(Class<S> treatAsType) {
return treatAs( nodeBuilder().getDomainModel().entity( treatAsType ) ); return treatAs( nodeBuilder().getDomainModel().entity( treatAsType ), null );
} }
@Override @Override
public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(EntityDomainType<S> treatTarget) throws PathException { public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(EntityDomainType<S> treatTarget) {
return new SqmTreatedListJoin<>( this, treatTarget, null ); return treatAs( treatTarget, null );
}
@Override
public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(Class<S> treatJavaType, String alias) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias );
}
@Override
public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(EntityDomainType<S> treatTarget, String alias) {
final SqmTreatedListJoin<O,E,S> treat = findTreat( treatTarget, alias );
if ( treat == null ) {
return addTreat( new SqmTreatedListJoin<>( this, treatTarget, alias ) );
}
return treat;
} }
@Override @Override

View File

@ -13,6 +13,8 @@ import jakarta.persistence.criteria.Predicate;
import org.hibernate.metamodel.model.domain.EntityDomainType; import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.MapPersistentAttribute; import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.PathException; import org.hibernate.query.PathException;
import org.hibernate.query.criteria.JpaExpression; import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaMapJoin; import org.hibernate.query.criteria.JpaMapJoin;
@ -40,6 +42,17 @@ public class SqmMapJoin<O, K, V>
super( lhs, pluralValuedNavigable, alias, sqmJoinType, fetched, nodeBuilder ); super( lhs, pluralValuedNavigable, alias, sqmJoinType, fetched, nodeBuilder );
} }
protected SqmMapJoin(
SqmFrom<?, O> lhs,
NavigablePath navigablePath,
MapPersistentAttribute<O, K, V> pluralValuedNavigable,
String alias,
SqmJoinType joinType,
boolean fetched,
NodeBuilder nodeBuilder) {
super( lhs, navigablePath, pluralValuedNavigable, alias, joinType, fetched, nodeBuilder );
}
@Override @Override
public MapPersistentAttribute<O, K, V> getReferencedPathSource() { public MapPersistentAttribute<O, K, V> getReferencedPathSource() {
return(MapPersistentAttribute<O, K, V>) super.getReferencedPathSource(); return(MapPersistentAttribute<O, K, V>) super.getReferencedPathSource();
@ -103,13 +116,27 @@ public class SqmMapJoin<O, K, V>
} }
@Override @Override
public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(Class<S> treatJavaType) throws PathException { public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(Class<S> treatJavaType) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) ); return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) );
} }
@Override @Override
public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(EntityDomainType<S> treatTarget) throws PathException { public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(EntityDomainType<S> treatTarget) {
return new SqmTreatedMapJoin<>( this, treatTarget, null ); return treatAs( treatTarget, null );
}
@Override
public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(Class<S> treatJavaType, String alias) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias );
}
@Override
public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(EntityDomainType<S> treatTarget, String alias) {
final SqmTreatedMapJoin<O, K, V, S> treat = findTreat( treatTarget, alias );
if ( treat == null ) {
return addTreat( new SqmTreatedMapJoin<>( this, treatTarget, alias ) );
}
return treat;
} }
@Override @Override

View File

@ -114,10 +114,10 @@ public interface SqmPath<T> extends SqmExpression<T>, SemanticPathPart, JpaPath<
} }
@Override @Override
<S extends T> SqmTreatedPath<T,S> treatAs(Class<S> treatJavaType) throws PathException; <S extends T> SqmPath<S> treatAs(Class<S> treatJavaType);
@Override @Override
<S extends T> SqmTreatedPath<T,S> treatAs(EntityDomainType<S> treatTarget) throws PathException; <S extends T> SqmPath<S> treatAs(EntityDomainType<S> treatTarget);
default SqmRoot<?> findRoot() { default SqmRoot<?> findRoot() {
final SqmPath<?> lhs = getLhs(); final SqmPath<?> lhs = getLhs();

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