HHH-11433 Allow usage of KEY expression in a join

This commit is contained in:
Christian Beikov 2021-12-20 16:14:46 +01:00
parent 360fec82f1
commit d5d350e5e7
15 changed files with 496 additions and 40 deletions

View File

@ -8,14 +8,21 @@ package org.hibernate.metamodel.model.domain.internal;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.SqmJoinable;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
import org.hibernate.query.sqm.tree.from.SqmFrom;
/**
* @author Steve Ebersole
*/
public class EntitySqmPathSource<J> extends AbstractSqmPathSource<J> {
public class EntitySqmPathSource<J> extends AbstractSqmPathSource<J> implements SqmJoinable<Object, J> {
public EntitySqmPathSource(
String localPathName,
EntityDomainType<J> domainType,
@ -51,4 +58,25 @@ public class EntitySqmPathSource<J> extends AbstractSqmPathSource<J> {
lhs.nodeBuilder()
);
}
@Override
public SqmPluralPartJoin<Object, J> createSqmJoin(
SqmFrom<?, Object> lhs,
SqmJoinType joinType,
String alias,
boolean fetched,
SqmCreationState creationState) {
return new SqmPluralPartJoin<>(
lhs,
this,
alias,
joinType,
creationState.getCreationContext().getNodeBuilder()
);
}
@Override
public String getName() {
return getPathName();
}
}

View File

@ -21,7 +21,6 @@ import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.spi.SqmCreationHelper;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmJoin;
@ -45,7 +44,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
private final String alias;
private ConsumerDelegate delegate;
private boolean treated;
private boolean nested;
public QualifiedJoinPathConsumer(
SqmRoot<?> sqmRoot,
@ -80,8 +79,12 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
);
}
public void setTreated(boolean treated) {
this.treated = treated;
public boolean isNested() {
return nested;
}
public void setNested(boolean nested) {
this.nested = nested;
}
@Override
@ -91,13 +94,12 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
@Override
public void consumeIdentifier(String identifier, boolean isBase, boolean isTerminal) {
if ( isBase ) {
assert delegate == null;
delegate = resolveBase( identifier, !treated && isTerminal );
if ( isBase && delegate == null ) {
delegate = resolveBase( identifier, !nested && isTerminal );
}
else {
assert delegate != null;
delegate.consumeIdentifier( identifier, !treated && isTerminal );
delegate.consumeIdentifier( identifier, !nested && isTerminal );
}
}
@ -194,7 +196,7 @@ public class QualifiedJoinPathConsumer implements DotIdentifierConsumer {
}
}
@SuppressWarnings("unchecked")
final SqmAttributeJoin<Object, Object> join = ( (SqmJoinable) subPathSource ).createSqmJoin(
final SqmJoin<Object, Object> join = ( (SqmJoinable<Object, Object>) subPathSource ).createSqmJoin(
lhs,
joinType,
isTerminal ? alias : SqmCreationHelper.IMPLICIT_ALIAS,

View File

@ -33,6 +33,7 @@ import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmPolymorphicRootDescriptor;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
@ -410,6 +411,27 @@ public class QuerySplitter {
return copy;
}
@Override
public SqmPluralPartJoin<?, ?> visitPluralPartJoin(SqmPluralPartJoin<?, ?> join) {
final SqmFrom<?, ?> sqmFrom = sqmFromCopyMap.get( join );
if ( sqmFrom != null ) {
return (SqmPluralPartJoin<?, ?>) sqmFrom;
}
final SqmFrom<?, ?> newLhs = (SqmFrom<?, ?>) sqmFromCopyMap.get( join.getLhs() );
final SqmPluralPartJoin copy = new SqmPluralPartJoin<>(
newLhs,
join.getReferencedPathSource(),
join.getExplicitAlias(),
join.getSqmJoinType(),
join.nodeBuilder()
);
getProcessingStateStack().getCurrent().getPathRegistry().register( copy );
sqmFromCopyMap.put( join, copy );
sqmPathCopyMap.put( join.getNavigablePath(), copy );
newLhs.addSqmJoin( copy );
return copy;
}
@Override
public SqmEntityJoin<?> visitQualifiedEntityJoin(SqmEntityJoin<?> join) {
final SqmFrom<?, ?> sqmFrom = sqmFromCopyMap.get( join );

View File

@ -1660,7 +1660,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
if ( join instanceof SqmEntityJoin<?> ) {
sqmRoot.addSqmJoin( join );
}
else {
else if ( join instanceof SqmAttributeJoin<?, ?> ) {
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) join;
if ( getCreationOptions().useStrictJpaCompliance() ) {
if ( join.getExplicitAlias() != null ) {
@ -4106,7 +4106,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
public SqmPath<?> visitTreatedNavigablePath(HqlParser.TreatedNavigablePathContext ctx) {
final DotIdentifierConsumer consumer = dotIdentifierConsumerStack.getCurrent();
if ( consumer instanceof QualifiedJoinPathConsumer ) {
( (QualifiedJoinPathConsumer) consumer ).setTreated( true );
( (QualifiedJoinPathConsumer) consumer ).setNested( true );
}
consumeManagedTypeReference( (HqlParser.PathContext) ctx.getChild( 2 ) );
@ -4189,21 +4189,67 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
@Override
@SuppressWarnings({ "rawtypes" })
public SqmPath visitMapKeyNavigablePath(HqlParser.MapKeyNavigablePathContext ctx) {
final DotIdentifierConsumer consumer = dotIdentifierConsumerStack.getCurrent();
final boolean madeNested;
if ( consumer instanceof QualifiedJoinPathConsumer ) {
final QualifiedJoinPathConsumer qualifiedJoinPathConsumer = (QualifiedJoinPathConsumer) consumer;
madeNested = !qualifiedJoinPathConsumer.isNested();
if ( madeNested ) {
qualifiedJoinPathConsumer.setNested( true );
}
}
else {
madeNested = false;
}
final SqmPath<?> sqmPath = consumeDomainPath( (HqlParser.PathContext) ctx.getChild( 2 ) );
final boolean hasContinuation = ctx.getChildCount() == 5;
SqmPath<?> result;
if ( sqmPath instanceof SqmMapJoin ) {
final SqmMapJoin<?, ?, ?> sqmMapJoin = (SqmMapJoin<?, ?, ?>) sqmPath;
result = sqmMapJoin.key();
if ( consumer instanceof QualifiedJoinPathConsumer ) {
if ( madeNested && !hasContinuation ) {
// Reset the nested state before consuming the terminal identifier
( (QualifiedJoinPathConsumer) consumer ).setNested( false );
}
consumer.consumeIdentifier( CollectionPart.Nature.INDEX.getName(), false, !hasContinuation );
result = (SqmPath<?>) consumer.getConsumedPart();
}
else {
result = sqmMapJoin.key();
}
}
else {
assert sqmPath instanceof SqmPluralValuedSimplePath;
final SqmPluralValuedSimplePath<?> mapPath = (SqmPluralValuedSimplePath<?>) sqmPath;
result = mapPath.resolvePathPart( CollectionPart.Nature.INDEX.getName(), true, this );
result = mapPath.resolvePathPart( CollectionPart.Nature.INDEX.getName(), !hasContinuation, this );
}
if ( ctx.getChildCount() == 5 ) {
result = consumeDomainPath( (HqlParser.DotIdentifierSequenceContext) ctx.getChild( 4 ).getChild( 1 ) );
if ( hasContinuation ) {
if ( madeNested ) {
// Reset the nested state before consuming the terminal identifier
( (QualifiedJoinPathConsumer) consumer ).setNested( false );
}
final HqlParser.DotIdentifierSequenceContext identCtx = (HqlParser.DotIdentifierSequenceContext) ctx.getChild( 4 )
.getChild( 1 );
if ( consumer instanceof QualifiedJoinPathConsumer ) {
result = consumeDomainPath( identCtx );
}
else {
dotIdentifierConsumerStack.push(
new BasicDotIdentifierConsumer( result, this ) {
@Override
protected void reset() {
}
}
);
try {
result = consumeDomainPath( identCtx );
}
finally {
dotIdentifierConsumerStack.pop();
}
}
}
return result;

View File

@ -25,6 +25,7 @@ import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath;
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.expression.SqmAny;
@ -130,6 +131,8 @@ public interface SemanticQueryWalker<T> {
T visitCrossJoin(SqmCrossJoin<?> joinedFromElement);
T visitPluralPartJoin(SqmPluralPartJoin<?, ?> joinedFromElement);
T visitQualifiedEntityJoin(SqmEntityJoin<?> joinedFromElement);
T visitQualifiedAttributeJoin(SqmAttributeJoin<?, ?> joinedFromElement);

View File

@ -10,6 +10,7 @@ import org.hibernate.query.hql.spi.SqmCreationState;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmJoin;
/**
* Specialization for attributes that that can be used in creating SQM joins
@ -19,9 +20,9 @@ import org.hibernate.query.sqm.tree.from.SqmFrom;
*
* @author Steve Ebersole
*/
public interface SqmJoinable {
SqmAttributeJoin createSqmJoin(
SqmFrom lhs,
public interface SqmJoinable<O, E> {
SqmJoin<O, E> createSqmJoin(
SqmFrom<?, O> lhs,
SqmJoinType joinType,
String alias,
boolean fetched,

View File

@ -29,6 +29,7 @@ import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath;
import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
@ -505,6 +506,17 @@ public class SqmTreePrinter implements SemanticQueryWalker<Object> {
return null;
}
@Override
public Object visitPluralPartJoin(SqmPluralPartJoin<?, ?> joinedFromElement) {
processStanza(
"plural-part",
'`' + joinedFromElement.getNavigablePath().getFullPath() + '`',
() -> processJoins( joinedFromElement )
);
return null;
}
private boolean inJoinPredicate;
private void processJoinPredicate(SqmQualifiedJoin<?, ?> joinedFromElement) {

View File

@ -28,6 +28,7 @@ import org.hibernate.query.sqm.tree.domain.SqmMaxIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmMinElementPath;
import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
@ -249,6 +250,13 @@ public abstract class BaseSemanticQueryWalker implements SemanticQueryWalker<Obj
return joinedFromElement;
}
@Override
public Object visitPluralPartJoin(SqmPluralPartJoin<?, ?> joinedFromElement) {
joinedFromElement.visitReusablePaths( path -> path.accept( this ) );
joinedFromElement.visitSqmJoins( sqmJoin -> sqmJoin.accept( this ) );
return joinedFromElement;
}
@Override
public Object visitQualifiedEntityJoin(SqmEntityJoin<?> joinedFromElement) {
joinedFromElement.visitReusablePaths( path -> path.accept( this ) );

View File

@ -101,6 +101,7 @@ import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedRootJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
import org.hibernate.query.sqm.tree.domain.SqmPluralPartJoin;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
@ -114,7 +115,6 @@ import org.hibernate.sql.ast.tree.from.QueryPartTableGroup;
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
import org.hibernate.sql.ast.tree.from.TableReference;
import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.Joinable;
import org.hibernate.query.BinaryArithmeticOperator;
@ -126,8 +126,6 @@ import org.hibernate.query.QueryLogging;
import org.hibernate.query.SemanticException;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.UnaryArithmeticOperator;
import org.hibernate.query.criteria.JpaPath;
import org.hibernate.query.internal.QueryHelper;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBinding;
import org.hibernate.query.spi.QueryParameterBindings;
@ -138,10 +136,8 @@ import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.SqmQuerySource;
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
import org.hibernate.query.sqm.function.SelfRenderingAggregateFunctionSqlAstExpression;
import org.hibernate.query.sqm.function.SelfRenderingFunctionSqlAstExpression;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.internal.SqmMappingModelHelper;
import org.hibernate.query.sqm.produce.function.internal.PatternRenderer;
import org.hibernate.query.sqm.spi.BaseSemanticQueryWalker;
import org.hibernate.query.sqm.sql.internal.BasicValuedPathInterpretation;
import org.hibernate.query.sqm.sql.internal.DiscriminatedAssociationPathInterpretation;
@ -157,7 +153,6 @@ import org.hibernate.query.sqm.sql.internal.SqmMapEntryResult;
import org.hibernate.query.sqm.sql.internal.SqmParameterInterpretation;
import org.hibernate.query.sqm.sql.internal.SqmPathInterpretation;
import org.hibernate.query.sqm.sql.internal.TypeHelper;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.SqmTypedNode;
import org.hibernate.query.sqm.tree.cte.SqmCteContainer;
@ -170,8 +165,6 @@ import org.hibernate.query.sqm.tree.domain.AbstractSqmSpecificPluralPartPath;
import org.hibernate.query.sqm.tree.domain.NonAggregatedCompositeSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmAnyValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmBasicValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmCorrelatedRootJoin;
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
import org.hibernate.query.sqm.tree.domain.SqmEmbeddedValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmEntityValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
@ -183,7 +176,6 @@ import org.hibernate.query.sqm.tree.domain.SqmMinIndexPath;
import org.hibernate.query.sqm.tree.domain.SqmPath;
import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedPath;
import org.hibernate.query.sqm.tree.domain.SqmTreatedRoot;
import org.hibernate.query.sqm.tree.expression.Conversion;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmAliasedNodeRef;
@ -208,7 +200,6 @@ import org.hibernate.query.sqm.tree.expression.SqmJpaCriteriaParameterWrapper;
import org.hibernate.query.sqm.tree.expression.SqmLiteral;
import org.hibernate.query.sqm.tree.expression.SqmLiteralEntityType;
import org.hibernate.query.sqm.tree.expression.SqmLiteralNull;
import org.hibernate.query.sqm.tree.expression.SqmModifiedSubQueryExpression;
import org.hibernate.query.sqm.tree.expression.SqmNamedParameter;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.expression.SqmParameterizedEntityType;
@ -227,7 +218,6 @@ import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.from.SqmJoin;
import org.hibernate.query.sqm.tree.from.SqmRoot;
import org.hibernate.query.sqm.tree.insert.SqmInsertSelectStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertStatement;
import org.hibernate.query.sqm.tree.insert.SqmInsertValuesStatement;
import org.hibernate.query.sqm.tree.insert.SqmValues;
import org.hibernate.query.sqm.tree.predicate.SqmAndPredicate;
@ -304,21 +294,16 @@ import org.hibernate.sql.ast.tree.expression.ExtractUnit;
import org.hibernate.sql.ast.tree.expression.Format;
import org.hibernate.sql.ast.tree.expression.JdbcLiteral;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.expression.ModifiedSubQueryExpression;
import org.hibernate.sql.ast.tree.expression.Over;
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
import org.hibernate.sql.ast.tree.expression.SelfRenderingExpression;
import org.hibernate.sql.ast.tree.expression.SelfRenderingSqlFragmentExpression;
import org.hibernate.sql.ast.tree.expression.SqlSelectionExpression;
import org.hibernate.sql.ast.tree.expression.SqlTuple;
import org.hibernate.sql.ast.tree.expression.Star;
import org.hibernate.sql.ast.tree.expression.Summarization;
import org.hibernate.sql.ast.tree.expression.TrimSpecification;
import org.hibernate.sql.ast.tree.expression.UnaryOperation;
import org.hibernate.sql.ast.tree.from.CorrelatedPluralTableGroup;
import org.hibernate.sql.ast.tree.from.CorrelatedTableGroup;
import org.hibernate.sql.ast.tree.from.LazyTableGroup;
import org.hibernate.sql.ast.tree.from.PluralTableGroup;
import org.hibernate.sql.ast.tree.from.TableGroup;
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
@ -329,7 +314,6 @@ import org.hibernate.sql.ast.tree.predicate.BetweenPredicate;
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
import org.hibernate.sql.ast.tree.predicate.ExistsPredicate;
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
import org.hibernate.sql.ast.tree.predicate.GroupedPredicate;
import org.hibernate.sql.ast.tree.predicate.InListPredicate;
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
@ -351,7 +335,6 @@ import org.hibernate.sql.ast.tree.update.Assignment;
import org.hibernate.sql.ast.tree.update.UpdateStatement;
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
import org.hibernate.sql.exec.internal.JdbcParametersImpl;
import org.hibernate.sql.exec.internal.VersionTypeSeedParameterSpecification;
import org.hibernate.sql.exec.spi.ExecutionContext;
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
import org.hibernate.sql.exec.spi.JdbcParameters;
@ -2469,6 +2452,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
else if ( sqmJoin instanceof SqmEntityJoin<?> ) {
return consumeEntityJoin( ( (SqmEntityJoin<?>) sqmJoin ), lhsTableGroup, transitive );
}
else if ( sqmJoin instanceof SqmPluralPartJoin<?, ?> ) {
return consumePluralPartJoin( ( (SqmPluralPartJoin<?, ?>) sqmJoin ), ownerTableGroup, transitive );
}
else {
throw new InterpretationException( "Could not resolve SqmJoin [" + sqmJoin.getNavigablePath() + "] to TableGroupJoin" );
}
@ -2694,6 +2680,31 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
return tableGroup;
}
private TableGroup consumePluralPartJoin(SqmPluralPartJoin<?, ?> sqmJoin, TableGroup lhsTableGroup, boolean transitive) {
final PluralTableGroup pluralTableGroup = (PluralTableGroup) lhsTableGroup;
final TableGroup tableGroup = getPluralPartTableGroup( pluralTableGroup, sqmJoin.getReferencedPathSource() );
getFromClauseIndex().register( sqmJoin, tableGroup );
assert sqmJoin.getJoinPredicate() == null;
if ( transitive ) {
consumeExplicitJoins( sqmJoin, tableGroup );
}
return tableGroup;
}
private TableGroup getPluralPartTableGroup(PluralTableGroup pluralTableGroup, SqmPathSource<?> pathSource) {
final CollectionPart.Nature nature = CollectionPart.Nature.fromNameExact( pathSource.getPathName() );
if ( nature != null ) {
switch ( nature ) {
case INDEX:
return pluralTableGroup.getIndexTableGroup();
case ELEMENT:
return pluralTableGroup.getElementTableGroup();
}
}
throw new UnsupportedOperationException( "Unsupported plural part join nature: " + nature );
}
private <X> X prepareReusablePath(SqmPath<?> sqmPath, Supplier<X> supplier) {
final Consumer<TableGroup> implicitJoinChecker;
if ( getCurrentProcessingState() instanceof SqlAstQueryPartProcessingState ) {
@ -2719,6 +2730,9 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
else {
parentPath = sqmPath.getParentPath();
}
if ( parentPath == null ) {
return null;
}
final TableGroup tableGroup = fromClauseIndex.findTableGroup( parentPath.getNavigablePath() );
if ( tableGroup == null ) {
final TableGroup parentTableGroup = prepareReusablePath(
@ -2923,6 +2937,20 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
throw new InterpretationException( "SqmCrossJoin not yet resolved to TableGroup" );
}
@Override
public Object visitPluralPartJoin(SqmPluralPartJoin<?, ?> sqmJoin) {
// todo (6.0) : have this resolve to TableGroup instead?
// - trying to remove tracking of TableGroupJoin in the x-refs
final TableGroup existing = getFromClauseAccess().findTableGroup( sqmJoin.getNavigablePath() );
if ( existing != null ) {
log.tracef( "SqmPluralPartJoin [%s] resolved to existing TableGroup [%s]", sqmJoin, existing );
return visitTableGroup( existing, sqmJoin );
}
throw new InterpretationException( "SqmPluralPartJoin not yet resolved to TableGroup" );
}
@Override
public Expression visitQualifiedEntityJoin(SqmEntityJoin sqmJoin) {
// todo (6.0) : have this resolve to TableGroup instead?
@ -4165,6 +4193,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final MappingMetamodel domainModel = getCreationContext().getDomainModel();
if ( sqmExpression instanceof SqmPath ) {
log.debugf( "Determining mapping-model type for SqmPath : %s ", sqmExpression );
prepareReusablePath( (SqmPath<?>) sqmExpression, () -> null );
return SqmMappingModelHelper.resolveMappingModelExpressable(
sqmExpression,
domainModel,
@ -5547,9 +5576,27 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
}
// otherwise - no special case...
inferrableTypeAccessStack.push(
() -> {
for ( SqmExpression<?> listExpression : predicate.getListExpressions() ) {
final MappingModelExpressable<?> mapping = determineValueMapping( listExpression );
if ( mapping != null ) {
return mapping;
}
}
return null;
}
);
final Expression testExpression;
try {
testExpression = (Expression) predicate.getTestExpression().accept( this );
}
finally {
inferrableTypeAccessStack.pop();
}
final InListPredicate inPredicate = new InListPredicate(
(Expression) predicate.getTestExpression().accept( this ),
testExpression,
predicate.isNegated(),
getBooleanType()
);

View File

@ -0,0 +1,52 @@
/*
* 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.sqm.tree.domain;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmRoot;
/**
* @author Christian Beikov
*/
public class SqmCorrelatedPluralPartJoin<O, T> extends SqmPluralPartJoin<O, T> implements SqmCorrelation<O, T> {
private final SqmCorrelatedRootJoin<O> correlatedRootJoin;
private final SqmPluralPartJoin<O, T> correlationParent;
public SqmCorrelatedPluralPartJoin(SqmPluralPartJoin<O, T> correlationParent) {
super(
(SqmFrom<?, O>) correlationParent.getLhs(),
correlationParent.getReferencedPathSource(),
null,
SqmJoinType.INNER,
correlationParent.nodeBuilder()
);
this.correlatedRootJoin = SqmCorrelatedRootJoin.create( correlationParent, this );
this.correlationParent = correlationParent;
}
@Override
public SqmPluralPartJoin<O, T> getCorrelationParent() {
return correlationParent;
}
@Override
public SqmPath<T> getWrappedPath() {
return correlationParent;
}
@Override
public boolean isCorrelated() {
return true;
}
@Override
public SqmRoot<O> getCorrelatedRoot() {
return correlatedRootJoin;
}
}

View File

@ -0,0 +1,114 @@
/*
* 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.sqm.tree.domain;
import java.util.Locale;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.NavigablePath;
import org.hibernate.query.sqm.NodeBuilder;
import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.spi.SqmCreationHelper;
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
import org.hibernate.query.sqm.tree.SqmJoinType;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
import org.hibernate.query.sqm.tree.predicate.SqmPredicate;
/**
* @author Christian Beikov
*/
public class SqmPluralPartJoin<O,T> extends AbstractSqmJoin<O,T> implements DomainResultProducer<T>, SqmQualifiedJoin<O, T> {
public SqmPluralPartJoin(
SqmFrom<?,O> lhs,
SqmPathSource<T> joinedNavigable,
String alias,
SqmJoinType joinType,
NodeBuilder nodeBuilder) {
super(
SqmCreationHelper.buildSubNavigablePath( lhs, joinedNavigable.getPathName(), alias ),
joinedNavigable,
lhs,
alias == SqmCreationHelper.IMPLICIT_ALIAS ? null : alias,
joinType,
nodeBuilder
);
}
protected SqmPluralPartJoin(
SqmFrom<?, O> lhs,
NavigablePath navigablePath,
SqmPathSource<T> joinedNavigable,
String alias,
SqmJoinType joinType,
NodeBuilder nodeBuilder) {
super(
navigablePath,
joinedNavigable,
lhs,
alias == SqmCreationHelper.IMPLICIT_ALIAS ? null : alias,
joinType,
nodeBuilder
);
}
@Override
public SqmPredicate getJoinPredicate() {
return null;
}
@Override
public void setJoinPredicate(SqmPredicate predicate) {
throw new UnsupportedOperationException( "Setting a predicate for a plural part join is unsupported!" );
}
@Override
public <X> X accept(SemanticQueryWalker<X> walker) {
return walker.visitPluralPartJoin( this );
}
@Override
public <S extends T> SqmTreatedPluralPartJoin<O,T,S> treatAs(Class<S> treatJavaType) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) );
}
@Override
public <S extends T> SqmTreatedPluralPartJoin<O,T,S> treatAs(EntityDomainType<S> treatTarget) {
return treatAs( treatTarget, null );
}
@Override
public <S extends T> SqmTreatedPluralPartJoin<O,T,S> treatAs(Class<S> treatJavaType, String alias) {
return treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ), alias );
}
@Override
public <S extends T> SqmTreatedPluralPartJoin<O,T,S> treatAs(EntityDomainType<S> treatTarget, String alias) {
final SqmTreatedPluralPartJoin<O, T, S> treat = findTreat( treatTarget, alias );
if ( treat == null ) {
return addTreat( new SqmTreatedPluralPartJoin<>( this, treatTarget, alias ) );
}
return treat;
}
@Override
public SqmCorrelatedPluralPartJoin<O, T> createCorrelation() {
return new SqmCorrelatedPluralPartJoin<>( this );
}
@Override
public String toString() {
return String.format(
Locale.ROOT,
"SqmPluralPartJoin(%s : %s)",
getNavigablePath().getFullPath(),
getReferencedPathSource().getPathName()
);
}
}

View File

@ -34,6 +34,16 @@ public class SqmSingularJoin<O,T> extends AbstractSqmAttributeJoin<O,T> implemen
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
}
public SqmSingularJoin(
SqmFrom<?,O> lhs,
SqmJoinable joinedNavigable,
String alias,
SqmJoinType joinType,
boolean fetched,
NodeBuilder nodeBuilder) {
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
}
protected SqmSingularJoin(
SqmFrom<?, O> lhs,
NavigablePath navigablePath,

View File

@ -0,0 +1,72 @@
/*
* 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.sqm.tree.domain;
import org.hibernate.metamodel.model.domain.EntityDomainType;
import org.hibernate.query.sqm.SqmPathSource;
import org.hibernate.query.sqm.tree.from.SqmFrom;
import org.hibernate.query.sqm.tree.from.SqmJoin;
/**
* @author Steve Ebersole
*/
public class SqmTreatedPluralPartJoin<O,T, S extends T> extends SqmPluralPartJoin<O,S> implements SqmTreatedPath<T,S> {
private final SqmPluralPartJoin<O,T> wrappedPath;
private final EntityDomainType<S> treatTarget;
@SuppressWarnings("WeakerAccess")
public SqmTreatedPluralPartJoin(
SqmPluralPartJoin<O,T> wrappedPath,
EntityDomainType<S> treatTarget,
String alias) {
//noinspection unchecked
super(
(SqmFrom<?, O>) wrappedPath.getLhs(),
wrappedPath.getNavigablePath().treatAs(
treatTarget.getHibernateEntityName(),
alias
),
(SqmPathSource<S>) wrappedPath.getReferencedPathSource(),
alias,
wrappedPath.getSqmJoinType(),
wrappedPath.nodeBuilder()
);
this.treatTarget = treatTarget;
this.wrappedPath = wrappedPath;
}
@Override
public void addSqmJoin(SqmJoin<S, ?> join) {
super.addSqmJoin( join );
//noinspection unchecked
wrappedPath.addSqmJoin( (SqmJoin<T, ?>) join );
}
@Override
public SqmPluralPartJoin<O,T> getWrappedPath() {
return wrappedPath;
}
@Override
public EntityDomainType<S> getTreatTarget() {
return treatTarget;
}
@Override
public SqmPathSource<S> getNodeType() {
return treatTarget;
}
@Override
public void appendHqlString(StringBuilder sb) {
sb.append( "treat(" );
wrappedPath.appendHqlString( sb );
sb.append( " as " );
sb.append( treatTarget.getName() );
sb.append( ')' );
}
}

View File

@ -153,6 +153,6 @@ public abstract class AbstractSqmExpression<T> extends AbstractJpaSelection<T> i
@Override
public JavaType<T> getJavaTypeDescriptor() {
return getNodeType().getExpressableJavaTypeDescriptor();
return getNodeType() == null ? null : getNodeType().getExpressableJavaTypeDescriptor();
}
}

View File

@ -4,13 +4,15 @@
* 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.test.hql;
package org.hibernate.orm.test.hql;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.util.Arrays;
@ -55,6 +57,7 @@ public class CollectionMapWithComponentValueTest extends BaseCoreFunctionalTestC
@Override
protected void prepareTest() throws Exception {
doInHibernate( this::sessionFactory, s -> {
keyValue.base = null;
s.save( keyValue );
BaseTestEntity baseTestEntity1 = new BaseTestEntity();
@ -67,6 +70,8 @@ public class CollectionMapWithComponentValueTest extends BaseCoreFunctionalTestC
baseTestEntity1.entities.add( testEntity );
s.save( baseTestEntity1 );
keyValue.base = baseTestEntity1;
KeyValue keyValue2 = new KeyValue( "key2" );
s.save( keyValue2 );
BaseTestEntity baseTestEntity2 = new BaseTestEntity();
@ -197,6 +202,37 @@ public class CollectionMapWithComponentValueTest extends BaseCoreFunctionalTestC
} );
}
@Test
@TestForIssue(jiraKey = "HHH-11433")
public void testJoinMapKey() {
doInHibernate( this::sessionFactory, s -> {
// Assert that a left join is used for joining the map key entity table
List keyValues= s.createQuery( "select k from BaseTestEntity bte left join bte.entities te left join te.values v left join key(v) k" ).list();
System.out.println( keyValues );
assertEquals( 2, keyValues.size() );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-11433")
public void testJoinMapKeyAssociation() {
doInHibernate( this::sessionFactory, s -> {
List keyValues= s.createQuery( "select b from BaseTestEntity bte left join bte.entities te left join te.values v left join key(v) k join k.base b" ).list();
System.out.println( keyValues );
assertEquals( 1, keyValues.size() );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-11433")
public void testJoinMapKeyAssociationImplicit() {
doInHibernate( this::sessionFactory, s -> {
List keyValues= s.createQuery( "select b from BaseTestEntity bte left join bte.entities te left join te.values v join key(v).base b" ).list();
System.out.println( keyValues );
assertEquals( 1, keyValues.size() );
} );
}
@Override
protected boolean isCleanupTestDataRequired() {
return true;
@ -233,6 +269,9 @@ public class CollectionMapWithComponentValueTest extends BaseCoreFunctionalTestC
String name;
@ManyToOne(fetch = FetchType.LAZY)
BaseTestEntity base;
public KeyValue() {
}