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
| UPPER
| VALUE
| VALUES
| VERSION
| VERSIONED
| WEEK

View File

@ -35,7 +35,7 @@ public <R> NativeSelectQueryPlan<R> createQueryPlan(
return new NativeSelectQueryPlanImpl<>(
queryDefinition.getSqlString(),
queryDefinition.getAffectedTableNames(),
queryDefinition.getQueryParameterList(),
queryDefinition.getQueryParameterOccurrences(),
queryDefinition.getResultSetMapping(),
sessionFactory
);

View File

@ -44,7 +44,7 @@ default <R> NativeSelectQueryPlan<R> createQueryPlan(
return new NativeSelectQueryPlanImpl<>(
queryDefinition.getSqlString(),
queryDefinition.getAffectedTableNames(),
queryDefinition.getQueryParameterList(),
queryDefinition.getQueryParameterOccurrences(),
queryDefinition.getResultSetMapping(),
sessionFactory
);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -177,7 +177,6 @@ public BasicFetch generateFetch(
fetchParent,
fetchablePath,
this,
false,
null,
fetchTiming,
creationState

View File

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

View File

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

View File

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

View File

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

View File

@ -230,7 +230,6 @@ public Fetch generateFetch(
fetchParent,
fetchablePath,
this,
false,
valueConverter,
FetchTiming.IMMEDIATE,
creationState

View File

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

View File

@ -180,7 +180,6 @@ public Fetch generateFetch(
fetchParent,
fetchablePath,
this,
! selected,
null,
FetchTiming.IMMEDIATE,
creationState

View File

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

View File

@ -159,7 +159,6 @@ public Fetch generateFetch(
fetchParent,
FetchTiming.IMMEDIATE,
selected,
true,
creationState
);
}

View File

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

View File

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

View File

@ -200,7 +200,6 @@ public Fetch generateFetch(
fetchParent,
fetchablePath,
this,
false,
null,
fetchTiming,
creationState

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -592,7 +592,7 @@ private EntityTypeImpl<?> buildEntityType(
else {
javaTypeDescriptor = context.getTypeConfiguration()
.getJavaTypeDescriptorRegistry()
.resolveManagedTypeDescriptor( javaType );
.resolveEntityTypeDescriptor( javaType );
}
final EntityTypeImpl<?> entityType = new EntityTypeImpl(

View File

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

View File

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

View File

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

View File

@ -8,6 +8,8 @@
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -54,12 +56,14 @@
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.InFragment;
import org.hibernate.sql.Insert;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
import org.hibernate.sql.ast.tree.predicate.NullnessPredicate;
import org.hibernate.sql.ast.tree.predicate.Predicate;
import org.hibernate.sql.results.graph.DomainResult;
@ -1293,14 +1297,79 @@ public <T> DomainResult<T> createDomainResult(
String resultVariable,
DomainResultCreationState creationState) {
if ( hasSubclasses() ) {
final EntityResultJoinedSubclassImpl entityResultJoinedSubclass = new EntityResultJoinedSubclassImpl(
navigablePath,
this,
tableGroup,
resultVariable,
creationState
);
entityResultJoinedSubclass.afterInitialize( entityResultJoinedSubclass, creationState );
//noinspection unchecked
return new EntityResultJoinedSubclassImpl( navigablePath, this, tableGroup, resultVariable, creationState );
return entityResultJoinedSubclass;
}
else {
return super.createDomainResult( navigablePath, tableGroup, resultVariable, creationState );
}
}
@Override
public void pruneForSubclasses(TableGroup tableGroup, Set<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
public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) {
for ( int i = 0; i < constraintOrderedTableNames.length; i++ ) {

View File

@ -47,12 +47,12 @@
import org.hibernate.sql.Insert;
import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.InListPredicate;
import org.hibernate.sql.ast.tree.predicate.Junction;
@ -654,7 +654,7 @@ private String[] decodeTreatAsRequests(Set<String> treatAsDeclarations) {
if ( !queryable.isAbstract() ) {
values.add( queryable.getDiscriminatorSQLValue() );
}
else if ( queryable.hasSubclasses() ) {
if ( queryable.hasSubclasses() ) {
// if the treat is an abstract class, add the concrete implementations to values if any
Set<String> actualSubClasses = queryable.getEntityMetamodel().getSubclassEntityNames();
@ -843,7 +843,7 @@ public TableGroup createRootTableGroup(
String explicitSourceAlias,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAliasBase sqlAliasBase,
SqlAstCreationState creationState,
SqlExpressionResolver expressionResolver,
SqlAstCreationContext creationContext) {
final TableGroup tableGroup = super.createRootTableGroup(
canUseInnerJoins,
@ -851,14 +851,14 @@ public TableGroup createRootTableGroup(
explicitSourceAlias,
additionalPredicateCollectorAccess,
sqlAliasBase,
creationState,
expressionResolver,
creationContext
);
if ( additionalPredicateCollectorAccess != null && needsDiscriminator() ) {
final Predicate discriminatorPredicate = createDiscriminatorPredicate(
tableGroup,
creationState.getSqlExpressionResolver(),
expressionResolver,
creationContext
);
additionalPredicateCollectorAccess.get().accept( discriminatorPredicate );
@ -952,6 +952,17 @@ else if ( discriminatorValue == NOT_NULL_DISCRIMINATOR ) {
);
}
@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
public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) {
for ( int i = 0; i < constraintOrderedTableNames.length; i++ ) {

View File

@ -12,6 +12,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@ -42,12 +43,14 @@
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.Table;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.persister.spi.PersisterCreationContext;
import org.hibernate.query.NavigablePath;
import org.hibernate.sql.ast.spi.SqlAliasBase;
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.ast.tree.from.UnionTableGroup;
@ -251,7 +254,7 @@ public TableGroup createRootTableGroup(
String explicitSourceAlias,
Supplier<Consumer<Predicate>> additionalPredicateCollectorAccess,
SqlAliasBase sqlAliasBase,
SqlAstCreationState creationState,
SqlExpressionResolver expressionResolver,
SqlAstCreationContext creationContext) {
final TableReference tableReference = resolvePrimaryTableReference( sqlAliasBase );
@ -390,6 +393,14 @@ public boolean isMultiTable() {
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
public void visitConstraintOrderedTables(ConstraintOrderedTableConsumer consumer) {
@ -509,6 +520,68 @@ protected String generateSubquery(PersistentClass model, Mapping mapping) {
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
protected String[] getSubclassTableKeyColumns(int j) {
if ( j != 0 ) {

View File

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

View File

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

View File

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

View File

@ -6,6 +6,10 @@
*/
package org.hibernate.query.criteria;
import java.util.Collection;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import jakarta.persistence.criteria.CollectionJoin;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
@ -15,7 +19,7 @@
*
* @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
JpaCollectionJoin<O, T> on(JpaExpression<Boolean> restriction);
@ -31,4 +35,7 @@ public interface JpaCollectionJoin<O, T> extends JpaJoin<O, T>, CollectionJoin<O
@Override
<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.Join;
import jakarta.persistence.criteria.Predicate;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
/**
@ -32,4 +33,10 @@ public interface JpaJoin<O, T> extends JpaJoinedFrom<O, T>, Join<O, T> {
@Override
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;
import java.util.List;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.ListJoin;
import jakarta.persistence.criteria.Predicate;
@ -15,7 +19,7 @@
*
* @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
JpaListJoin<O, T> on(JpaExpression<Boolean> restriction);
@ -30,4 +34,7 @@ public interface JpaListJoin<O, T> extends JpaJoin<O, T>, ListJoin<O, T> {
@Override
<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;
import java.util.Map;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.PathException;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.MapJoin;
import jakarta.persistence.criteria.Predicate;
@ -15,7 +20,7 @@
*
* @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
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);
<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.
*/
<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.
*/
<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;
import java.util.Set;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Predicate;
import jakarta.persistence.criteria.SetJoin;
@ -15,7 +20,7 @@
*
* @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);
@ -26,4 +31,6 @@ public interface JpaSetJoin<O, T> extends JpaJoin<O, T>, SetJoin<O, T> {
JpaSetJoin<O, T> on(Predicate... restrictions);
<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 org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.criteria.JpaPath;
import org.hibernate.query.hql.HqlLogging;
import org.hibernate.query.hql.spi.DotIdentifierConsumer;
import org.hibernate.query.hql.spi.SemanticPathPart;
@ -93,6 +94,13 @@ public void consumeIdentifier(String identifier, boolean isBase, boolean isTermi
currentPart = currentPart.resolvePathPart( identifier, isTerminal, creationState );
}
@Override
public void consumeTreat(String entityName, boolean isTerminal) {
final EntityDomainType<?> entityDomainType = creationState.getCreationContext().getJpaMetamodel()
.entity( entityName );
currentPart = ( (SqmPath) currentPart ).treatAs( entityDomainType );
}
protected void reset() {
pathSoFar = null;
currentPart = createBasePart();
@ -249,13 +257,13 @@ public SemanticPathPart resolvePathPart(
throw new ParsingException( "Could not interpret dot-ident : " + pathSoFar );
}
protected void validateAsRoot(SqmFrom pathRoot) {
protected void validateAsRoot(SqmFrom<?, ?> pathRoot) {
}
@Override
public SqmPath resolveIndexedAccess(
SqmExpression selector,
public SqmPath<?> resolveIndexedAccess(
SqmExpression<?> selector,
boolean isTerminal,
SqmCreationState processingState) {
return currentPart.resolveIndexedAccess( selector, isTerminal, processingState );

View File

@ -144,8 +144,8 @@ else if ( lhs instanceof SqmEntityJoin<?> ) {
}
@Override
public SqmPath resolveIndexedAccess(
SqmExpression selector,
public SqmPath<?> resolveIndexedAccess(
SqmExpression<?> selector,
boolean isTerminal,
SqmCreationState creationState) {
throw new NotYetImplementedFor6Exception( getClass() );

View File

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

View File

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

View File

@ -272,8 +272,6 @@ public static <R> SqmStatement<R> buildSemanticModel(
private final Stack<DotIdentifierConsumer> dotIdentifierConsumerStack;
private final Stack<TreatHandler> treatHandlerStack = new StandardStack<>( new TreatHandlerNormal() );
private final Stack<ParameterDeclarationContext> parameterDeclarationContextStack = new StandardStack<>();
private final Stack<SqmCreationProcessingState> processingStateStack = new StandardStack<>();
@ -282,12 +280,16 @@ public static <R> SqmStatement<R> buildSemanticModel(
private final JavaType<Map<?,?>> mapJavaTypeDescriptor;
private ParameterCollector parameterCollector;
private ParameterStyle parameterStyle;
@SuppressWarnings("WeakerAccess")
public SemanticQueryBuilder(SqmCreationOptions creationOptions, SqmCreationContext creationContext) {
this.creationOptions = creationOptions;
this.creationContext = creationContext;
this.dotIdentifierConsumerStack = new StandardStack<>( new BasicDotIdentifierConsumer( this ) );
this.parameterStyle = creationOptions.useStrictJpaCompliance()
? ParameterStyle.UNKNOWN
: ParameterStyle.MIXED;
this.integerDomainType = creationContext
.getNodeBuilder()
@ -787,13 +789,7 @@ public SqmQuerySpec<Object> visitQuerySpec(HqlParser.QuerySpecContext ctx) {
}
// visit from-clause first!!!
treatHandlerStack.push( new TreatHandlerFromClause() );
try {
sqmQuerySpec.setFromClause( visitFromClause( (HqlParser.FromClauseContext) ctx.getChild( fromIndex ) ) );
}
finally {
treatHandlerStack.pop();
}
sqmQuerySpec.setFromClause( visitFromClause( (HqlParser.FromClauseContext) ctx.getChild( fromIndex ) ) );
final SqmSelectClause selectClause;
if ( fromIndex == 1 ) {
@ -817,13 +813,7 @@ else if ( ctx.getChild( ctx.getChildCount() - 1 ) instanceof HqlParser.SelectCla
int currentIndex = fromIndex + 1;
final SqmWhereClause whereClause = new SqmWhereClause( creationContext.getNodeBuilder() );
if ( currentIndex < ctx.getChildCount() && ctx.getChild( currentIndex ) instanceof HqlParser.WhereClauseContext ) {
treatHandlerStack.push( new TreatHandlerNormal( DowncastLocation.WHERE ) );
try {
whereClause.setPredicate( (SqmPredicate) ctx.getChild( currentIndex++ ).accept( this ) );
}
finally {
treatHandlerStack.pop();
}
whereClause.setPredicate( (SqmPredicate) ctx.getChild( currentIndex++ ).accept( this ) );
}
sqmQuerySpec.setWhereClause( whereClause );
@ -862,8 +852,6 @@ protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
@Override
public SqmSelectClause visitSelectClause(HqlParser.SelectClauseContext ctx) {
treatHandlerStack.push( new TreatHandlerNormal( DowncastLocation.SELECT ) );
// todo (6.0) : primer a select-clause-specific SemanticPathPart into the stack
final int selectionListIndex;
if ( ctx.getChild( 1 ) instanceof HqlParser.SelectionListContext ) {
@ -873,24 +861,19 @@ public SqmSelectClause visitSelectClause(HqlParser.SelectClauseContext ctx) {
selectionListIndex = 2;
}
try {
final SqmSelectClause selectClause = new SqmSelectClause(
selectionListIndex == 2,
creationContext.getNodeBuilder()
);
final HqlParser.SelectionListContext selectionListContext = (HqlParser.SelectionListContext) ctx.getChild(
selectionListIndex
);
for ( ParseTree subCtx : selectionListContext.children ) {
if ( subCtx instanceof HqlParser.SelectionContext ) {
selectClause.addSelection( visitSelection( (HqlParser.SelectionContext) subCtx ) );
}
final SqmSelectClause selectClause = new SqmSelectClause(
selectionListIndex == 2,
creationContext.getNodeBuilder()
);
final HqlParser.SelectionListContext selectionListContext = (HqlParser.SelectionListContext) ctx.getChild(
selectionListIndex
);
for ( ParseTree subCtx : selectionListContext.children ) {
if ( subCtx instanceof HqlParser.SelectionContext ) {
selectClause.addSelection( visitSelection( (HqlParser.SelectionContext) subCtx ) );
}
return selectClause;
}
finally {
treatHandlerStack.pop();
}
return selectClause;
}
@Override
@ -1409,30 +1392,23 @@ private EntityDomainType<?> resolveEntityReference(String entityName) {
@Override
public SqmFromClause visitFromClause(HqlParser.FromClauseContext parserFromClause) {
treatHandlerStack.push( new TreatHandlerFromClause() );
try {
final SqmFromClause fromClause;
if ( parserFromClause == null ) {
fromClause = new SqmFromClause();
}
else {
final int size = parserFromClause.getChildCount();
// Shift 1 bit instead of division by 2
final int estimatedSize = size >> 1;
fromClause = new SqmFromClause( estimatedSize );
for ( int i = 0; i < size; i++ ) {
final ParseTree parseTree = parserFromClause.getChild( i );
if ( parseTree instanceof HqlParser.FromClauseSpaceContext ) {
fromClause.addRoot( visitFromClauseSpace( (HqlParser.FromClauseSpaceContext) parseTree ) );
}
final SqmFromClause fromClause;
if ( parserFromClause == null ) {
fromClause = new SqmFromClause();
}
else {
final int size = parserFromClause.getChildCount();
// Shift 1 bit instead of division by 2
final int estimatedSize = size >> 1;
fromClause = new SqmFromClause( estimatedSize );
for ( int i = 0; i < size; i++ ) {
final ParseTree parseTree = parserFromClause.getChild( i );
if ( parseTree instanceof HqlParser.FromClauseSpaceContext ) {
fromClause.addRoot( visitFromClauseSpace( (HqlParser.FromClauseSpaceContext) parseTree ) );
}
}
return fromClause;
}
finally {
treatHandlerStack.pop();
}
return fromClause;
}
@Override
@ -1679,10 +1655,6 @@ protected <X> void consumeQualifiedJoin(HqlParser.QualifiedJoinContext parserJoi
//noinspection unchecked
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();
if ( join instanceof SqmEntityJoin<?> ) {
sqmRoot.addSqmJoin( join );
@ -3129,6 +3101,7 @@ public Object visitParameterExpression(HqlParser.ParameterExpressionContext ctx)
@Override
public SqmNamedParameter<?> visitNamedParameter(HqlParser.NamedParameterContext ctx) {
parameterStyle = parameterStyle.withNamed();
final SqmNamedParameter<?> param = new SqmNamedParameter<>(
ctx.getChild( 1 ).getText(),
parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(),
@ -3143,6 +3116,7 @@ public SqmPositionalParameter<?> visitPositionalParameter(HqlParser.PositionalPa
if ( ctx.getChildCount() == 1 ) {
throw new SemanticException( "Encountered positional parameter which did not declare position (? instead of, e.g., ?1)" );
}
parameterStyle = parameterStyle.withPositional();
final SqmPositionalParameter<?> param = new SqmPositionalParameter<>(
Integer.parseInt( ctx.getChild( 1 ).getText() ),
parameterDeclarationContextStack.getCurrent().isMultiValuedBindingAllowed(),
@ -4128,15 +4102,20 @@ public SemanticPathPart visitDotIdentifierSequence(HqlParser.DotIdentifierSequen
@Override
public SqmPath<?> visitTreatedNavigablePath(HqlParser.TreatedNavigablePathContext ctx) {
final SqmPath<?> sqmPath = consumeManagedTypeReference( (HqlParser.PathContext) ctx.getChild( 2 ) );
final DotIdentifierConsumer consumer = dotIdentifierConsumerStack.getCurrent();
if ( consumer instanceof QualifiedJoinPathConsumer ) {
( (QualifiedJoinPathConsumer) consumer ).setTreated( true );
}
consumeManagedTypeReference( (HqlParser.PathContext) ctx.getChild( 2 ) );
final String treatTargetName = ctx.getChild( 4 ).getText();
final String treatTargetEntityName = getCreationContext().getJpaMetamodel().qualifyImportableName( treatTargetName );
final EntityDomainType<?> treatTarget = getCreationContext().getJpaMetamodel().entity( treatTargetEntityName );
SqmPath<?> result = resolveTreatedPath( sqmPath, treatTarget );
final boolean hasContinuation = ctx.getChildCount() == 7;
consumer.consumeTreat( treatTargetEntityName, !hasContinuation );
SqmPath<?> result = (SqmPath<?>) consumer.getConsumedPart();
if ( ctx.getChildCount() == 7 ) {
if ( hasContinuation ) {
dotIdentifierConsumerStack.push(
new BasicDotIdentifierConsumer( result, this ) {
@Override
@ -4155,11 +4134,6 @@ protected void reset() {
return result;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private SqmTreatedPath<?, ?> resolveTreatedPath(SqmPath<?> sqmPath, EntityDomainType<?> treatTarget) {
return sqmPath.treatAs( (EntityDomainType) treatTarget );
}
@Override
public SqmPath<?> visitCollectionElementNavigablePath(HqlParser.CollectionElementNavigablePathContext ctx) {
final SqmPath<?> pluralAttributePath = consumeDomainPath( (HqlParser.PathContext) ctx.getChild( 2 ) );
@ -4257,15 +4231,10 @@ private SqmPath<?> consumeManagedTypeReference(HqlParser.PathContext parserPath)
final SqmPath<?> sqmPath = consumeDomainPath( parserPath );
final SqmPathSource<?> pathSource = sqmPath.getReferencedPathSource();
try {
// use the `#sqmAs` call to validate the path is a ManagedType
pathSource.sqmAs( ManagedDomainType.class );
if ( pathSource.getSqmPathType() instanceof ManagedDomainType<?> ) {
return sqmPath;
}
catch (Exception e) {
throw new SemanticException( "Expecting ManagedType valued path [" + sqmPath.getNavigablePath() + "], but found : " + pathSource.getSqmPathType() );
}
throw new SemanticException( "Expecting ManagedType valued path [" + sqmPath.getNavigablePath() + "], but found : " + pathSource.getSqmPathType() );
}
private SqmPath<?> consumePluralAttributeReference(HqlParser.PathContext parserPath) {
@ -4278,40 +4247,6 @@ private SqmPath<?> consumePluralAttributeReference(HqlParser.PathContext parserP
throw new SemanticException( "Expecting plural attribute valued path [" + sqmPath.getNavigablePath() + "], but found : " + sqmPath.getReferencedPathSource().getSqmPathType() );
}
private interface TreatHandler {
void addDowncast(SqmFrom<?, ?> sqmFrom, IdentifiableDomainType<?> downcastTarget);
}
private static class TreatHandlerNormal implements TreatHandler {
private final DowncastLocation downcastLocation;
public TreatHandlerNormal() {
this( DowncastLocation.OTHER );
}
public TreatHandlerNormal(DowncastLocation downcastLocation) {
this.downcastLocation = downcastLocation;
}
@Override
public void addDowncast(
SqmFrom<?, ?> sqmFrom,
IdentifiableDomainType<?> downcastTarget) {
// ( (MutableUsageDetails) sqmFrom.getUsageDetails() ).addDownCast( false, downcastTarget, downcastLocation );
throw new NotYetImplementedFor6Exception();
}
}
private static class TreatHandlerFromClause implements TreatHandler {
@Override
public void addDowncast(
SqmFrom<?, ?> sqmFrom,
IdentifiableDomainType<?> downcastTarget) {
// ( (MutableUsageDetails) sqmFrom.getUsageDetails() ).addDownCast( true, downcastTarget, DowncastLocation.FROM );
throw new NotYetImplementedFor6Exception();
}
}
private void checkFQNEntityNameJpaComplianceViolationIfNeeded(String name, EntityDomainType<?> entityDescriptor) {
if ( getCreationOptions().useStrictJpaCompliance() && ! name.equals( entityDescriptor.getName() ) ) {
// FQN is the only possible reason
@ -4321,4 +4256,59 @@ private void checkFQNEntityNameJpaComplianceViolationIfNeeded(String name, Entit
);
}
}
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);
/**
* Responsible for consuming each part of the path. Called sequentially for
* each part.
*
* @param entityName The treat target entity name
* @param isTerminal Is this the terminus of the path (last token)?
*/
void consumeTreat(String entityName, boolean isTerminal);
/**
* Get the currently consumed part. Generally called after the whole path
* has been processed at which point this will return the final outcome of the

View File

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

View File

@ -76,17 +76,18 @@ public int getNumberOfCachedQueryPlans() {
}
@Override
public SelectQueryPlan resolveSelectQueryPlan(
public <R> SelectQueryPlan<R> resolveSelectQueryPlan(
Key key,
Supplier<SelectQueryPlan> creator) {
Supplier<SelectQueryPlan<R>> creator) {
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 ) {
return cached;
}
final SelectQueryPlan plan = creator.get();
final SelectQueryPlan<R> plan = creator.get();
queryPlanCache.put( key.prepareForStore(), plan );
return plan;
}

View File

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

View File

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

View File

@ -68,7 +68,7 @@ public BasicFetch<?> buildFetch(
final String mappedColumn = referencedModelPart.getSelectionExpression();
final TableGroup tableGroup = creationState.getFromClauseAccess().getTableGroup( parent.getNavigablePath() );
final TableReference tableReference = tableGroup.getTableReference( navigablePath, mappedTable );
final TableReference tableReference = tableGroup.resolveTableReference( navigablePath, mappedTable );
final String selectedAlias;
final int jdbcPosition;

View File

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

View File

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

View File

@ -146,7 +146,7 @@ private void resolveSelections(
creationStateImpl.resolveSqlSelection(
creationStateImpl.resolveSqlExpression(
SqlExpressionResolver.createColumnReferenceKey(
tableGroup.getTableReference( selectableMapping.getContainingTableExpression() ),
tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ),
selectableMapping.getSelectionExpression()
),
processingState -> {

View File

@ -58,7 +58,6 @@ public BasicFetch<?> buildFetch(
parent,
fetchPath,
referencedModelPart,
true,
null,
FetchTiming.DELAYED,
isEnhancedForLazyLoading,

View File

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

View File

@ -148,7 +148,7 @@ public Fetch buildFetch(
resolveSqlSelection(
columnNames.get( selectionIndex ),
createColumnReferenceKey(
tableGroup.getTableReference( selectableMapping.getContainingTableExpression() ),
tableGroup.resolveTableReference( selectableMapping.getContainingTableExpression() ),
selectableMapping.getSelectionExpression()
),
selectableMapping.getJdbcMapping(),

View File

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

View File

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

View File

@ -101,7 +101,7 @@
*/
@SuppressWarnings("WeakerAccess")
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;

View File

@ -35,7 +35,7 @@ default Key prepareForStore() {
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);
void cacheNonSelectQueryPlan(Key key, NonSelectQueryPlan plan);

View File

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

View File

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

View File

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

View File

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

View File

@ -165,9 +165,6 @@ else if ( context.isEntityAlias( aliasName ) ) {
result.append( '{' ).append( aliasPath ).append( '}' );
}
}
// Possibly handle :something parameters for the query ?
return result.toString();
}

View File

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

View File

@ -20,7 +20,7 @@ public interface ParameterInterpretation {
* Access to the defined parameters in the order they were encountered,
* potentially including "duplicate references" to a single parameter
*/
List<QueryParameterImplementor<?>> getOccurrenceOrderedParameters();
List<ParameterOccurrence> getOrderedParameterOccurrences();
/**
* 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;
import java.util.Locale;
import jakarta.persistence.metamodel.Bindable;
import org.hibernate.metamodel.model.domain.DomainType;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.tree.SqmExpressableAccessor;
import org.hibernate.query.sqm.tree.domain.SqmPath;
@ -52,20 +50,4 @@ public interface SqmPathSource<J> extends SqmExpressable<J>, Bindable<J>, SqmExp
default SqmExpressable<J> getExpressable() {
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 enum Type {
IDENTIFICATION_VARIABLE_NOT_DECLARED_IN_FROM_CLAUSE( "use of an alias not declared in the FROM clause" ),
FQN_ENTITY_NAME( "use of FQN for entity name" ),
IMPLICIT_TREAT( "use of implicit treat" ),
MIXED_POSITIONAL_NAMED_PARAMETERS( "mix of positional and named parameters" ),
;
private final String description;

View File

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

View File

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

View File

@ -201,7 +201,7 @@ public static List<Object> selectMatchingIds(
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(
entityDescriptor,
sqmMutationStatement.getTarget().getExplicitAlias(),
sqmMutationStatement.getTarget(),
domainParameterXref,
executionContext.getQueryOptions(),
executionContext.getSession().getLoadQueryInfluencers(),

View File

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

View File

@ -117,7 +117,7 @@ public int execute(DomainQueryExecutionContext executionContext) {
final MultiTableSqmMutationConverter sqmConverter = new MultiTableSqmMutationConverter(
entityDescriptor,
sqmMutationStatement.getTarget().getExplicitAlias(),
sqmMutationStatement.getTarget(),
explicitDmlTargetAlias,
domainParameterXref,
executionContext.getQueryOptions(),
@ -324,7 +324,7 @@ protected TableReference resolveUnionTableReference(
);
}
else {
return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression );
return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression, true, true );
}
}

View File

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

View File

@ -126,7 +126,7 @@ private ExecutionDelegate resolveDelegate(DomainQueryExecutionContext executionC
final MultiTableSqmMutationConverter converterDelegate = new MultiTableSqmMutationConverter(
entityDescriptor,
getSqmDeleteOrUpdateStatement().getTarget().getExplicitAlias(),
getSqmDeleteOrUpdateStatement().getTarget(),
domainParameterXref,
executionContext.getQueryOptions(),
executionContext.getSession().getLoadQueryInfluencers(),

View File

@ -230,7 +230,7 @@ private TableReference resolveUnionTableReference(TableGroup tableGroup, String
);
}
else {
return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression );
return tableGroup.getTableReference( tableGroup.getNavigablePath(), tableExpression, true, true );
}
}

View File

@ -16,13 +16,17 @@
*/
public class SqmCreationHelper {
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
? new NavigablePath( base )
? new NavigablePath( base, Long.toString( System.nanoTime() ) )
: new NavigablePath( base, 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) {

View File

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

View File

@ -12,7 +12,6 @@
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.metamodel.mapping.EntityAssociationMapping;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
@ -20,27 +19,19 @@
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
import org.hibernate.metamodel.mapping.ModelPart;
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
import org.hibernate.metamodel.mapping.SelectableConsumer;
import org.hibernate.metamodel.mapping.internal.EntityCollectionPart;
import org.hibernate.metamodel.mapping.internal.SimpleForeignKeyDescriptor;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.sql.ast.SqlAstJoinType;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.spi.FromClauseAccess;
import org.hibernate.sql.ast.spi.SqlAstCreationState;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.SqlTupleContainer;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.results.graph.DomainResult;
import org.hibernate.sql.results.graph.DomainResultCreationState;
import static org.hibernate.sql.ast.spi.SqlExpressionResolver.createColumnReferenceKey;
@ -53,16 +44,9 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
public static <T> EntityValuedPathInterpretation<T> from(
SqmEntityValuedSimplePath<T> sqmPath,
SqmToSqlAstConverter sqlAstCreationState) {
final SqmPath<?> realPath;
if ( CollectionPart.Nature.fromNameExact( sqmPath.getNavigablePath().getUnaliasedLocalName() ) != null ) {
realPath = sqmPath.getLhs();
}
else {
realPath = sqmPath;
}
final TableGroup tableGroup = sqlAstCreationState
.getFromClauseAccess()
.findTableGroup( realPath.getLhs().getNavigablePath() );
.findTableGroup( sqmPath.getLhs().getNavigablePath() );
final EntityValuedModelPart mapping = (EntityValuedModelPart) sqlAstCreationState
.getFromClauseAccess()
@ -124,19 +108,6 @@ else if ( mapping instanceof EntityAssociationMapping ) {
final String lhsTable;
final ModelPart lhsPart;
boolean useKeyPart = associationMapping.getSideNature() == ForeignKeyDescriptor.Nature.KEY;
if ( mapping instanceof EntityCollectionPart ) {
// EntityCollectionPart always returns TARGET, but sometimes we still want to use the KEY (element) side.
// With a collection table, we can always use the TARGET for referring to the collection,
// but in case of a one-to-many without collection table, this would be problematic,
// as we can't use e.g. the primary key of the owner entity as substitution.
final TableGroup pluralTableGroup = sqlAstCreationState.getFromClauseAccess()
.findTableGroup( navigablePath.getParent() );
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) pluralTableGroup.getModelPart();
if ( pluralAttributeMapping.getSeparateCollectionTable() == null ) {
useKeyPart = true;
tableGroup = pluralTableGroup;
}
}
if ( useKeyPart ) {
lhsTable = fkDescriptor.getKeyTable();
lhsPart = fkDescriptor.getKeyPart();
@ -281,46 +252,4 @@ public EntityValuedModelPart 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.Predicate;
import org.hibernate.metamodel.model.domain.PersistentAttribute;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaPredicate;
import org.hibernate.query.sqm.NodeBuilder;
@ -48,9 +49,28 @@ public AbstractSqmAttributeJoin(
SqmJoinType joinType,
boolean fetched,
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
super(
SqmCreationHelper.buildSubNavigablePath( lhs, joinedNavigable.getName(), alias ),
navigablePath,
(SqmPathSource<T>) joinedNavigable,
lhs,
alias,

View File

@ -10,6 +10,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
@ -39,7 +40,6 @@
import org.hibernate.query.SemanticException;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.UnknownPathException;
import org.hibernate.query.hql.spi.SemanticPathPart;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.spi.SqmCreationHelper;
import org.hibernate.query.sqm.tree.SqmJoinType;
@ -58,6 +58,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
private String alias;
private List<SqmJoin<T, ?>> joins;
private List<SqmFrom<?, ?>> treats;
protected AbstractSqmFrom(
NavigablePath navigablePath,
@ -168,6 +169,38 @@ public void visitSqmJoins(Consumer<SqmJoin<T, ?>> consumer) {
}
}
@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

View File

@ -59,18 +59,6 @@ public SqmPathSource<T> getReferencedPathSource() {
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
public NavigablePath getNavigablePath() {
return navigablePath;

View File

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

View File

@ -12,7 +12,7 @@
import org.hibernate.metamodel.model.domain.BagPersistentAttribute;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.PathException;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.criteria.JpaCollectionJoin;
import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaPredicate;
@ -36,6 +36,17 @@ public SqmBagJoin(
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
public BagPersistentAttribute<O,E> getReferencedPathSource() {
return (BagPersistentAttribute<O,E>) super.getReferencedPathSource();
@ -85,15 +96,27 @@ public <S extends E> SqmTreatedBagJoin<O, E, S> treatAs(Class<S> treatAsType) {
}
@Override
public <S extends E> SqmTreatedBagJoin<O,E,S> treatAs(EntityDomainType<S> treatTarget) throws PathException {
//noinspection unchecked
return new SqmTreatedBagJoin( this, treatTarget, null );
public <S extends E> SqmTreatedBagJoin<O,E,S> treatAs(EntityDomainType<S> treatTarget) {
return treatAs( treatTarget, null );
}
@Override
public SqmAttributeJoin makeCopy(SqmCreationProcessingState creationProcessingState) {
//noinspection unchecked
return new SqmBagJoin(
public <S extends E> SqmTreatedBagJoin<O,E,S> treatAs(Class<S> treatJavaType, String alias) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias );
}
@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() ),
getReferencedPathSource(),
getExplicitAlias(),

View File

@ -12,6 +12,8 @@
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.PathException;
import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaListJoin;
@ -39,6 +41,17 @@ public SqmListJoin(
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
public ListPersistentAttribute<O, E> getReferencedPathSource() {
return (ListPersistentAttribute<O, E>) super.getReferencedPathSource();
@ -88,12 +101,26 @@ public SqmCorrelatedListJoin<O, E> createCorrelation() {
@Override
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
public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(EntityDomainType<S> treatTarget) throws PathException {
return new SqmTreatedListJoin<>( this, treatTarget, null );
public <S extends E> SqmTreatedListJoin<O,E,S> treatAs(EntityDomainType<S> treatTarget) {
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

View File

@ -13,6 +13,8 @@
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.metamodel.model.domain.MapPersistentAttribute;
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.PathException;
import org.hibernate.query.criteria.JpaExpression;
import org.hibernate.query.criteria.JpaMapJoin;
@ -40,6 +42,17 @@ public SqmMapJoin(
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
public MapPersistentAttribute<O, K, V> getReferencedPathSource() {
return(MapPersistentAttribute<O, K, V>) super.getReferencedPathSource();
@ -103,13 +116,27 @@ public SqmCorrelatedMapJoin<O, K, V> createCorrelation() {
}
@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 ) );
}
@Override
public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(EntityDomainType<S> treatTarget) throws PathException {
return new SqmTreatedMapJoin<>( this, treatTarget, null );
public <S extends V> SqmTreatedMapJoin<O, K, V, S> treatAs(EntityDomainType<S> treatTarget) {
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

View File

@ -114,10 +114,10 @@ default JavaType<T> getJavaTypeDescriptor() {
}
@Override
<S extends T> SqmTreatedPath<T,S> treatAs(Class<S> treatJavaType) throws PathException;
<S extends T> SqmPath<S> treatAs(Class<S> treatJavaType);
@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() {
final SqmPath<?> lhs = getLhs();

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