Fix determining table groups for fetches and reuse joins for parsed paths. Fix determining correct table group for SqmFrom usages. Fix indexed access for plural paths
This commit is contained in:
parent
07f6d31d2b
commit
aa7b5529e9
|
@ -176,7 +176,8 @@ public abstract class AbstractCompositeIdentifierMapping
|
|||
final CompositeTableGroup compositeTableGroup = new CompositeTableGroup(
|
||||
navigablePath,
|
||||
this,
|
||||
lhs
|
||||
lhs,
|
||||
fetched
|
||||
);
|
||||
|
||||
final TableGroupJoin join = new TableGroupJoin( navigablePath, SqlAstJoinType.LEFT, compositeTableGroup, null );
|
||||
|
|
|
@ -275,7 +275,8 @@ public class EmbeddedAttributeMapping
|
|||
final CompositeTableGroup compositeTableGroup = new CompositeTableGroup(
|
||||
navigablePath,
|
||||
this,
|
||||
lhs
|
||||
lhs,
|
||||
fetched
|
||||
);
|
||||
|
||||
TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
||||
|
|
|
@ -206,7 +206,7 @@ public class EmbeddedCollectionPart implements CollectionPart, EmbeddableValuedF
|
|||
SqlAstCreationContext creationContext) {
|
||||
assert lhs.getModelPart() instanceof PluralAttributeMapping;
|
||||
|
||||
final TableGroup tableGroup = new CompositeTableGroup( navigablePath, this, lhs );
|
||||
final TableGroup tableGroup = new CompositeTableGroup( navigablePath, this, lhs, fetched );
|
||||
|
||||
final TableGroupJoin tableGroupJoin = new TableGroupJoin(
|
||||
navigablePath,
|
||||
|
|
|
@ -275,14 +275,29 @@ public class ToOneAttributeMapping
|
|||
) );
|
||||
}
|
||||
if ( referencedPropertyName == null ) {
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
||||
final IdentifierProperty identifierProperty = getEntityMappingType()
|
||||
.getEntityPersister()
|
||||
.getEntityMetamodel()
|
||||
.getIdentifierProperty();
|
||||
this.targetKeyPropertyName = identifierProperty.getName();
|
||||
final Set<String> targetKeyPropertyNames = new HashSet<>( 2 );
|
||||
targetKeyPropertyNames.add( EntityIdentifierMapping.ROLE_LOCAL_NAME );
|
||||
addPrefixedPropertyNames( targetKeyPropertyNames, targetKeyPropertyName, identifierProperty.getType() );
|
||||
final Type propertyType = identifierProperty.getType();
|
||||
if ( identifierProperty.getName() == null ) {
|
||||
final CompositeType compositeType;
|
||||
if ( propertyType.isComponentType() && ( compositeType = (CompositeType) propertyType ).isEmbedded()
|
||||
&& compositeType.getPropertyNames().length == 1 ) {
|
||||
this.targetKeyPropertyName = compositeType.getPropertyNames()[0];
|
||||
addPrefixedPropertyNames( targetKeyPropertyNames, targetKeyPropertyName, compositeType.getSubtypes()[0] );
|
||||
}
|
||||
else {
|
||||
this.targetKeyPropertyName = EntityIdentifierMapping.ROLE_LOCAL_NAME;
|
||||
addPrefixedPropertyNames( targetKeyPropertyNames, null, propertyType );
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.targetKeyPropertyName = identifierProperty.getName();
|
||||
addPrefixedPropertyNames( targetKeyPropertyNames, targetKeyPropertyName, propertyType );
|
||||
}
|
||||
this.targetKeyPropertyNames = targetKeyPropertyNames;
|
||||
}
|
||||
else if ( bootValue.isReferenceToPrimaryKey() ) {
|
||||
|
@ -334,22 +349,24 @@ public class ToOneAttributeMapping
|
|||
Set<String> targetKeyPropertyNames,
|
||||
String prefix,
|
||||
Type type) {
|
||||
if ( type.isComponentType() ) {
|
||||
if ( prefix != null ) {
|
||||
targetKeyPropertyNames.add( prefix );
|
||||
}
|
||||
if ( type.isComponentType() ) {
|
||||
final ComponentType componentType = (ComponentType) type;
|
||||
final String[] propertyNames = componentType.getPropertyNames();
|
||||
final Type[] componentTypeSubtypes = componentType.getSubtypes();
|
||||
for ( int i = 0, propertyNamesLength = propertyNames.length; i < propertyNamesLength; i++ ) {
|
||||
addPrefixedPropertyNames(
|
||||
targetKeyPropertyNames,
|
||||
prefix + "." + propertyNames[i],
|
||||
componentTypeSubtypes[i]
|
||||
);
|
||||
final String newPrefix;
|
||||
if ( prefix == null ) {
|
||||
newPrefix = propertyNames[i];
|
||||
}
|
||||
else {
|
||||
newPrefix = prefix + "." + propertyNames[i];
|
||||
}
|
||||
addPrefixedPropertyNames( targetKeyPropertyNames, newPrefix, componentTypeSubtypes[i] );
|
||||
}
|
||||
}
|
||||
else {
|
||||
targetKeyPropertyNames.add( prefix );
|
||||
}
|
||||
}
|
||||
|
||||
public ToOneAttributeMapping copy() {
|
||||
|
@ -849,6 +866,7 @@ public class ToOneAttributeMapping
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlAstJoinType getDefaultSqlAstJoinType(TableGroup parentTableGroup) {
|
||||
if ( isNullable ) {
|
||||
return SqlAstJoinType.LEFT;
|
||||
|
@ -903,6 +921,7 @@ public class ToOneAttributeMapping
|
|||
final LazyTableGroup lazyTableGroup = new LazyTableGroup(
|
||||
canUseInnerJoin,
|
||||
navigablePath,
|
||||
fetched,
|
||||
() -> createTableGroupJoinInternal(
|
||||
canUseInnerJoin,
|
||||
navigablePath,
|
||||
|
@ -916,10 +935,10 @@ public class ToOneAttributeMapping
|
|||
if ( !canUseParentTableGroup ) {
|
||||
return false;
|
||||
}
|
||||
// Special case for resolving the table group for entity valued paths
|
||||
if ( np == navigablePath ) {
|
||||
return true;
|
||||
}
|
||||
// // Special case for resolving the table group for entity valued paths
|
||||
// if ( np == navigablePath ) {
|
||||
// return true;
|
||||
// }
|
||||
NavigablePath path = np.getParent();
|
||||
// Fast path
|
||||
if ( path != null && navigablePath.equals( path ) ) {
|
||||
|
|
|
@ -144,7 +144,7 @@ public class BasicDotIdentifierConsumer implements DotIdentifierConsumer {
|
|||
// identifier is an "unqualified attribute reference"
|
||||
validateAsRoot( pathRootByExposedNavigable );
|
||||
|
||||
SqmPath<?> sqmPath = (SqmPath<?>) pathRootByExposedNavigable.get( identifier );
|
||||
final SqmPath<?> sqmPath = pathRootByExposedNavigable.get( identifier );
|
||||
if ( isTerminal ) {
|
||||
return sqmPath;
|
||||
}
|
||||
|
|
|
@ -7,12 +7,19 @@
|
|||
package org.hibernate.query.hql.internal;
|
||||
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.query.hql.HqlLogging;
|
||||
import org.hibernate.query.hql.spi.SemanticPathPart;
|
||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
import org.hibernate.query.sqm.tree.from.SqmAttributeJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmCrossJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmEntityJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
import org.hibernate.query.sqm.tree.from.SqmJoin;
|
||||
import org.hibernate.query.sqm.tree.from.SqmRoot;
|
||||
|
||||
/**
|
||||
* Specialized "intermediate" SemanticPathPart for processing domain model paths
|
||||
|
@ -42,22 +49,53 @@ public class DomainPathPart implements SemanticPathPart {
|
|||
currentPath,
|
||||
name
|
||||
);
|
||||
// if we want to allow re-use of matched unaliased SqmFrom nodes
|
||||
//
|
||||
// final SqmPathRegistry pathRegistry = creationState.getCurrentProcessingState().getPathRegistry();
|
||||
// final NavigablePath possibleImplicitAliasPath = lhs.getNavigablePath().append( name );
|
||||
// final SqmPath fromByImplicitAlias = pathRegistry.findPath( possibleImplicitAliasPath );
|
||||
//
|
||||
// if ( fromByImplicitAlias != null ) {
|
||||
// if ( fromByImplicitAlias instanceof SqmFrom ) {
|
||||
// final String explicitPathAlias = fromByImplicitAlias.getExplicitAlias();
|
||||
// if ( explicitPathAlias == null || Objects.equals( possibleImplicitAliasPath.getFullPath(), explicitPathAlias ) ) {
|
||||
// currentPath = fromByImplicitAlias;
|
||||
// return isTerminal ? currentPath : this;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
currentPath = (SqmPath<?>) currentPath.get( name );
|
||||
final SqmPath<?> reusablePath = currentPath.getReusablePath( name );
|
||||
if ( reusablePath != null ) {
|
||||
currentPath = reusablePath;
|
||||
}
|
||||
else {
|
||||
// Try to resolve an existing attribute join without ON clause
|
||||
SqmPath<?> resolvedPath = null;
|
||||
if ( currentPath instanceof SqmFrom<?, ?> ) {
|
||||
ModelPartContainer modelPartContainer = null;
|
||||
for ( SqmJoin<?, ?> sqmJoin : ( (SqmFrom<?, ?>) currentPath ).getSqmJoins() ) {
|
||||
if ( sqmJoin instanceof SqmAttributeJoin<?, ?>
|
||||
&& name.equals( sqmJoin.getReferencedPathSource().getPathName() ) ) {
|
||||
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmJoin;
|
||||
if ( attributeJoin.getOn() == null ) {
|
||||
// todo (6.0): to match the expectation of the JPA spec I think we also have to check
|
||||
// that the join type is INNER or the default join type for the attribute,
|
||||
// but as far as I understand, in 5.x we expect to ignore this behavior
|
||||
// if ( attributeJoin.getSqmJoinType() != SqmJoinType.INNER ) {
|
||||
// if ( attributeJoin.getAttribute().isCollection() ) {
|
||||
// continue;
|
||||
// }
|
||||
// if ( modelPartContainer == null ) {
|
||||
// modelPartContainer = findModelPartContainer( attributeJoin, creationState );
|
||||
// }
|
||||
// final TableGroupJoinProducer joinProducer = (TableGroupJoinProducer) modelPartContainer.findSubPart(
|
||||
// name,
|
||||
// null
|
||||
// );
|
||||
// if ( attributeJoin.getSqmJoinType().getCorrespondingSqlJoinType() != joinProducer.getDefaultSqlAstJoinType( null ) ) {
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
resolvedPath = sqmJoin;
|
||||
if ( attributeJoin.isFetched() ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( resolvedPath == null ) {
|
||||
currentPath = currentPath.get( name );
|
||||
}
|
||||
else {
|
||||
currentPath = resolvedPath;
|
||||
}
|
||||
}
|
||||
if ( isTerminal ) {
|
||||
return currentPath;
|
||||
}
|
||||
|
@ -66,6 +104,45 @@ public class DomainPathPart implements SemanticPathPart {
|
|||
}
|
||||
}
|
||||
|
||||
private ModelPartContainer findModelPartContainer(SqmAttributeJoin<?, ?> attributeJoin, SqmCreationState creationState) {
|
||||
final SqmFrom<?, ?> lhs = attributeJoin.getLhs();
|
||||
if ( lhs instanceof SqmAttributeJoin<?, ?> ) {
|
||||
final SqmAttributeJoin<?, ?> lhsAttributeJoin = (SqmAttributeJoin<?, ?>) lhs;
|
||||
if ( lhsAttributeJoin.getReferencedPathSource() instanceof EntityDomainType<?> ) {
|
||||
final String entityName = ( (EntityDomainType<?>) lhsAttributeJoin.getReferencedPathSource() ).getHibernateEntityName();
|
||||
return (ModelPartContainer) creationState.getCreationContext().getQueryEngine()
|
||||
.getTypeConfiguration()
|
||||
.getSessionFactory()
|
||||
.getMetamodel()
|
||||
.entityPersister( entityName )
|
||||
.findSubPart( attributeJoin.getAttribute().getName(), null );
|
||||
}
|
||||
else {
|
||||
return (ModelPartContainer) findModelPartContainer( lhsAttributeJoin, creationState )
|
||||
.findSubPart( attributeJoin.getAttribute().getName(), null );
|
||||
}
|
||||
}
|
||||
else {
|
||||
final String entityName;
|
||||
if ( lhs instanceof SqmRoot<?> ) {
|
||||
entityName = ( (SqmRoot<?>) lhs ).getEntityName();
|
||||
}
|
||||
else if ( lhs instanceof SqmEntityJoin<?> ) {
|
||||
entityName = ( (SqmEntityJoin<?>) lhs ).getEntityName();
|
||||
}
|
||||
else {
|
||||
assert lhs instanceof SqmCrossJoin<?>;
|
||||
entityName = ( (SqmCrossJoin<?>) lhs ).getEntityName();
|
||||
}
|
||||
return (ModelPartContainer) creationState.getCreationContext().getQueryEngine()
|
||||
.getTypeConfiguration()
|
||||
.getSessionFactory()
|
||||
.getMetamodel()
|
||||
.entityPersister( entityName )
|
||||
.findSubPart( attributeJoin.getAttribute().getName(), null );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPath resolveIndexedAccess(
|
||||
SqmExpression selector,
|
||||
|
|
|
@ -55,7 +55,6 @@ import org.hibernate.metamodel.model.domain.SingularPersistentAttribute;
|
|||
import org.hibernate.query.BinaryArithmeticOperator;
|
||||
import org.hibernate.query.ComparisonOperator;
|
||||
import org.hibernate.query.FetchClauseType;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.NullPrecedence;
|
||||
import org.hibernate.query.PathException;
|
||||
import org.hibernate.query.SemanticException;
|
||||
|
@ -97,8 +96,6 @@ import org.hibernate.query.sqm.tree.SqmTypedNode;
|
|||
import org.hibernate.query.sqm.tree.delete.SqmDeleteStatement;
|
||||
import org.hibernate.query.sqm.tree.domain.AbstractSqmFrom;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmCorrelation;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmIndexedCollectionAccessPath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmListJoin;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmMapEntryReference;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmMapJoin;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmMaxElementPath;
|
||||
|
@ -3962,7 +3959,10 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
|
||||
@Override
|
||||
public SemanticPathPart visitGeneralPathFragment(HqlParser.GeneralPathFragmentContext ctx) {
|
||||
return visitDotIdentifierSequence( (HqlParser.DotIdentifierSequenceContext) ctx.getChild( 0 ) );
|
||||
return visitIndexedPathAccessFragment(
|
||||
(HqlParser.DotIdentifierSequenceContext) ctx.getChild( 0 ),
|
||||
ctx.getChildCount() == 1 ? null : (HqlParser.IndexedPathAccessFragmentContext) ctx.getChild( 1 )
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3981,84 +3981,37 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
return visitMapKeyNavigablePath( (HqlParser.MapKeyNavigablePathContext) firstChild );
|
||||
}
|
||||
else if ( firstChild instanceof HqlParser.DotIdentifierSequenceContext && ctx.getChildCount() == 2 ) {
|
||||
dotIdentifierConsumerStack.push(
|
||||
new QualifiedJoinPathConsumer(
|
||||
(SqmRoot<?>) dotIdentifierConsumerStack.getCurrent().getConsumedPart(),
|
||||
SqmJoinType.INNER,
|
||||
false,
|
||||
null,
|
||||
this
|
||||
)
|
||||
return visitIndexedPathAccessFragment(
|
||||
(HqlParser.DotIdentifierSequenceContext) firstChild,
|
||||
(HqlParser.IndexedPathAccessFragmentContext) ctx.getChild( 1 )
|
||||
);
|
||||
|
||||
final SqmAttributeJoin<?, ?> indexedJoinPath;
|
||||
try {
|
||||
indexedJoinPath = (SqmAttributeJoin<?, ?>) firstChild.accept( this );
|
||||
}
|
||||
finally {
|
||||
dotIdentifierConsumerStack.pop();
|
||||
}
|
||||
dotIdentifierConsumerStack.push(
|
||||
new BasicDotIdentifierConsumer( indexedJoinPath, this ) {
|
||||
@Override
|
||||
protected void reset() {
|
||||
}
|
||||
}
|
||||
);
|
||||
try {
|
||||
return (SemanticPathPart) ctx.getChild( 1 ).accept( this );
|
||||
}
|
||||
finally {
|
||||
dotIdentifierConsumerStack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
throw new ParsingException( "Unsure how to process `syntacticDomainPath` over : " + ctx.getText() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart visitIndexedPathAccessFragment(HqlParser.IndexedPathAccessFragmentContext ctx) {
|
||||
final DotIdentifierConsumer consumer = dotIdentifierConsumerStack.pop();
|
||||
final SqmExpression<?> indexExpression = (SqmExpression<?>) ctx.getChild( 1 ).accept( this );
|
||||
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) consumer.getConsumedPart();
|
||||
final NavigablePath navigablePath = attributeJoin.getNavigablePath().getParent().append(
|
||||
attributeJoin.getNavigablePath().getLocalName(),
|
||||
indexExpression.toHqlString()
|
||||
);
|
||||
// Reuse an existing indexed path join if possible
|
||||
for ( SqmJoin<?, ?> sqmJoin : attributeJoin.getSqmJoins() ) {
|
||||
if ( sqmJoin.getNavigablePath().getLocalName().equals( navigablePath.getLocalName() ) ) {
|
||||
return sqmJoin;
|
||||
}
|
||||
private SemanticPathPart visitIndexedPathAccessFragment(
|
||||
HqlParser.DotIdentifierSequenceContext ctx,
|
||||
HqlParser.IndexedPathAccessFragmentContext idxCtx) {
|
||||
final SemanticPathPart pathPart = visitDotIdentifierSequence( ctx );
|
||||
|
||||
if ( idxCtx == null ) {
|
||||
return pathPart;
|
||||
}
|
||||
|
||||
final SqmExpression<?> index;
|
||||
if ( attributeJoin instanceof SqmListJoin<?, ?> ) {
|
||||
index = ( (SqmListJoin<?, ?>) attributeJoin ).index();
|
||||
final SqmExpression<?> indexExpression = (SqmExpression<?>) idxCtx.getChild( 1 ).accept(this );
|
||||
final boolean hasIndexContinuation = idxCtx.getChildCount() == 5;
|
||||
final SqmPath<?> indexedPath = pathPart.resolveIndexedAccess( indexExpression, !hasIndexContinuation, this );
|
||||
|
||||
if ( hasIndexContinuation ) {
|
||||
return (SemanticPathPart) idxCtx.getChild( 4 ).accept( this );
|
||||
}
|
||||
else if ( attributeJoin instanceof SqmMapJoin<?, ?, ?> ) {
|
||||
index = ( (SqmMapJoin<?, ?, ?>) attributeJoin ).key();
|
||||
}
|
||||
else {
|
||||
throw new SemanticException( "Index access is only supported on list or map attributes: " + attributeJoin.getNavigablePath() );
|
||||
}
|
||||
attributeJoin.setJoinPredicate( creationContext.getNodeBuilder().equal( index, indexExpression ) );
|
||||
final SqmIndexedCollectionAccessPath<?> path = new SqmIndexedCollectionAccessPath<>(
|
||||
navigablePath,
|
||||
attributeJoin,
|
||||
indexExpression
|
||||
);
|
||||
dotIdentifierConsumerStack.push(
|
||||
new BasicDotIdentifierConsumer( path, this ) {
|
||||
@Override
|
||||
protected void reset() {
|
||||
}
|
||||
}
|
||||
);
|
||||
if ( ctx.getChildCount() == 5 ) {
|
||||
return (SemanticPathPart) ctx.getChild( 4 ).accept( this );
|
||||
}
|
||||
return path;
|
||||
return indexedPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SemanticPathPart visitIndexedPathAccessFragment(HqlParser.IndexedPathAccessFragmentContext idxCtx) {
|
||||
throw new UnsupportedOperationException( "Should be handled by #visitIndexedPathAccessFragment" );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -215,6 +215,38 @@ public class SqmPathRegistryImpl implements SqmPathRegistry {
|
|||
return (X) found;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X extends SqmFrom<?, ?>> X resolveFrom(NavigablePath navigablePath, Function<NavigablePath, SqmFrom<?, ?>> creator) {
|
||||
SqmTreeCreationLogger.LOGGER.tracef( "SqmProcessingIndex#resolvePath(NavigablePath) : %s", navigablePath );
|
||||
|
||||
final SqmFrom<?, ?> existing = sqmFromByPath.get( navigablePath );
|
||||
if ( existing != null ) {
|
||||
//noinspection unchecked
|
||||
return (X) existing;
|
||||
}
|
||||
|
||||
final SqmFrom<?, ?> sqmFrom = creator.apply( navigablePath );
|
||||
register( sqmFrom );
|
||||
//noinspection unchecked
|
||||
return (X) sqmFrom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X extends SqmFrom<?, ?>> X resolveFrom(SqmPath<?> path) {
|
||||
SqmTreeCreationLogger.LOGGER.tracef( "SqmProcessingIndex#resolvePath(SqmPath) : %s", path );
|
||||
|
||||
final SqmFrom<?, ?> existing = sqmFromByPath.get( path.getNavigablePath() );
|
||||
if ( existing != null ) {
|
||||
//noinspection unchecked
|
||||
return (X) existing;
|
||||
}
|
||||
|
||||
final SqmFrom<?, ?> sqmFrom = resolveFrom( path.getLhs() ).join( path.getNavigablePath().getUnaliasedLocalName() );
|
||||
register( sqmFrom );
|
||||
//noinspection unchecked
|
||||
return (X) sqmFrom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <X> SqmPath<X> resolvePath(NavigablePath navigablePath, Function<NavigablePath, SqmPath<X>> creator) {
|
||||
SqmTreeCreationLogger.LOGGER.tracef( "SqmProcessingIndex#resolvePath(NavigablePath) : %s", navigablePath );
|
||||
|
|
|
@ -55,6 +55,22 @@ public interface SqmPathRegistry {
|
|||
*/
|
||||
<X extends SqmFrom<?, ?>> X findFromExposing(String navigableName);
|
||||
|
||||
/**
|
||||
* Similar to {@link #findFromByPath}, but accepting a producer to be used
|
||||
* to create and register a SqmFrom if none yet registered.
|
||||
*
|
||||
* @return The existing or just-created SqmFrom
|
||||
*/
|
||||
<X extends SqmFrom<?, ?>> X resolveFrom(NavigablePath path, Function<NavigablePath, SqmFrom<?, ?>> creator);
|
||||
|
||||
/**
|
||||
* Similar to {@link #resolveFrom}, but accepting a SqmPath to be used
|
||||
* to create and register a SqmFrom if none yet registered.
|
||||
*
|
||||
* @return The existing or just-created SqmFrom
|
||||
*/
|
||||
<X extends SqmFrom<?, ?>> X resolveFrom(SqmPath<?> path);
|
||||
|
||||
/**
|
||||
* Find an SqmPath by its NavigablePath. Will return a SqmFrom if the NavigablePath
|
||||
* has (yet) been resolved to a SqmFrom. Otherwise, it will be a non-SqmFrom SqmPath
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
package org.hibernate.query.sqm.spi;
|
||||
|
||||
import org.hibernate.metamodel.mapping.CollectionPart;
|
||||
import org.hibernate.metamodel.model.domain.PluralPersistentAttribute;
|
||||
import org.hibernate.query.NavigablePath;
|
||||
import org.hibernate.query.sqm.tree.domain.SqmPath;
|
||||
|
||||
|
@ -29,8 +31,11 @@ public class SqmCreationHelper {
|
|||
"`lhs` cannot be null for a sub-navigable reference - " + subNavigable
|
||||
);
|
||||
}
|
||||
|
||||
return buildSubNavigablePath( lhs.getNavigablePath(), subNavigable, alias );
|
||||
NavigablePath navigablePath = lhs.getNavigablePath();
|
||||
if ( lhs.getReferencedPathSource() instanceof PluralPersistentAttribute<?, ?, ?> ) {
|
||||
navigablePath = navigablePath.append( CollectionPart.Nature.ELEMENT.getName() );
|
||||
}
|
||||
return buildSubNavigablePath( navigablePath, subNavigable, alias );
|
||||
}
|
||||
|
||||
private SqmCreationHelper() {
|
||||
|
|
|
@ -2401,7 +2401,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
if ( modelPart instanceof ToOneAttributeMapping ) {
|
||||
final ToOneAttributeMapping toOneAttributeMapping = (ToOneAttributeMapping) modelPart;
|
||||
keyPart = toOneAttributeMapping.findSubPart( toOneAttributeMapping.getTargetKeyPropertyName() );
|
||||
resultPart = toOneAttributeMapping;
|
||||
resultPart = modelPart;
|
||||
}
|
||||
else if ( modelPart instanceof PluralAttributeMapping ) {
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) modelPart;
|
||||
|
@ -2426,10 +2426,17 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
keyPart = modelPart;
|
||||
resultPart = modelPart;
|
||||
}
|
||||
final NavigablePath navigablePath;
|
||||
if ( resultPart == modelPart ) {
|
||||
navigablePath = tableGroup.getNavigablePath();
|
||||
}
|
||||
else {
|
||||
navigablePath = tableGroup.getNavigablePath().append( resultPart.getPartName() );
|
||||
}
|
||||
|
||||
final Expression result;
|
||||
if ( resultPart instanceof EntityValuedModelPart ) {
|
||||
final EntityValuedModelPart mapping = (EntityValuedModelPart) tableGroup.getModelPart();
|
||||
final EntityValuedModelPart mapping = (EntityValuedModelPart) resultPart;
|
||||
final boolean expandToAllColumns;
|
||||
if ( currentClauseStack.getCurrent() == Clause.GROUP ) {
|
||||
// When the table group is known to be fetched i.e. a fetch join
|
||||
|
@ -2440,9 +2447,10 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
else {
|
||||
expandToAllColumns = false;
|
||||
}
|
||||
final TableGroup parentGroupToUse = findTableGroup( navigablePath.getParent() );
|
||||
result = EntityValuedPathInterpretation.from(
|
||||
tableGroup.getNavigablePath(),
|
||||
tableGroup,
|
||||
navigablePath,
|
||||
parentGroupToUse == null ? tableGroup : parentGroupToUse,
|
||||
mapping,
|
||||
expandToAllColumns,
|
||||
this
|
||||
|
@ -2457,7 +2465,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
this,
|
||||
getSqlAstCreationState()
|
||||
),
|
||||
tableGroup.getNavigablePath(),
|
||||
navigablePath,
|
||||
(EmbeddableValuedModelPart) resultPart,
|
||||
tableGroup
|
||||
);
|
||||
|
@ -2466,7 +2474,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
assert resultPart instanceof BasicValuedModelPart;
|
||||
final BasicValuedModelPart mapping = (BasicValuedModelPart) keyPart;
|
||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||
tableGroup.getNavigablePath().append( keyPart.getPartName() ),
|
||||
navigablePath.append( keyPart.getPartName() ),
|
||||
mapping.getContainingTableExpression()
|
||||
);
|
||||
|
||||
|
@ -2495,7 +2503,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
result = new BasicValuedPathInterpretation<>(
|
||||
columnReference,
|
||||
tableGroup.getNavigablePath(),
|
||||
navigablePath,
|
||||
(BasicValuedModelPart) resultPart,
|
||||
tableGroup
|
||||
);
|
||||
|
@ -5199,7 +5207,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
final List<String> bagRoles = new ArrayList<>();
|
||||
|
||||
final BiConsumer<Fetchable, Boolean> fetchableBiConsumer = (fetchable, isKeyFetchable) -> {
|
||||
final NavigablePath fetchablePath = fetchParent.resolveNavigablePath( fetchable );
|
||||
final NavigablePath resolvedNavigablePath = fetchParent.resolveNavigablePath( fetchable );
|
||||
|
||||
final String alias;
|
||||
FetchTiming fetchTiming = fetchable.getMappedFetchOptions().getTiming();
|
||||
|
@ -5207,16 +5215,16 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
|
||||
EntityGraphTraversalState.TraversalResult traversalResult = null;
|
||||
final FromClauseIndex fromClauseIndex = getFromClauseIndex();
|
||||
final SqmAttributeJoin<?, ?> fetchedJoin = fromClauseIndex.findFetchedJoinByPath( fetchablePath );
|
||||
final SqmAttributeJoin<?, ?> fetchedJoin = fromClauseIndex.findFetchedJoinByPath( resolvedNavigablePath );
|
||||
boolean explicitFetch = false;
|
||||
|
||||
final NavigablePath fetchablePath;
|
||||
if ( fetchedJoin != null ) {
|
||||
fetchablePath = fetchedJoin.getNavigablePath();
|
||||
// there was an explicit fetch in the SQM
|
||||
// there should be a TableGroupJoin registered for this `fetchablePath` already
|
||||
// because it
|
||||
assert fromClauseIndex.getTableGroup( fetchablePath ) != null;
|
||||
assert fromClauseIndex.getTableGroup( fetchedJoin.getNavigablePath() ) != null;
|
||||
|
||||
//
|
||||
if ( fetchedJoin.isFetched() ) {
|
||||
fetchTiming = FetchTiming.IMMEDIATE;
|
||||
}
|
||||
|
@ -5225,6 +5233,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
explicitFetch = true;
|
||||
}
|
||||
else {
|
||||
fetchablePath = resolvedNavigablePath;
|
||||
// there was not an explicit fetch in the SQM
|
||||
alias = null;
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ public class FromClauseIndex extends SimpleFromClauseAccessImpl {
|
|||
}
|
||||
|
||||
public boolean isResolved(SqmFrom fromElement) {
|
||||
return tableGroupMap.containsKey( fromElement.getNavigablePath().getIdentifierForTableGroup() )
|
||||
return tableGroupMap.containsKey( fromElement.getNavigablePath() )
|
||||
|| parent != null && ( (FromClauseIndex) parent ).isResolved( fromElement );
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,9 @@ import org.hibernate.metamodel.mapping.EntityMappingType;
|
|||
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ForeignKeyDescriptor;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.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;
|
||||
|
@ -88,10 +90,12 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
|||
entityMappingType.getJdbcTypeCount() + identifierMapping.getJdbcTypeCount()
|
||||
+ ( discriminatorMapping == null ? 0 : 1 )
|
||||
);
|
||||
final TableGroup parentTableGroup = tableGroup;
|
||||
final SelectableConsumer selectableConsumer = (selectionIndex, selectableMapping) -> {
|
||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||
final TableReference tableReference = parentTableGroup.resolveTableReference(
|
||||
navigablePath,
|
||||
selectableMapping.getContainingTableExpression()
|
||||
selectableMapping.getContainingTableExpression(),
|
||||
false
|
||||
);
|
||||
expressions.add(
|
||||
sqlExprResolver.resolveSqlExpression(
|
||||
|
@ -119,7 +123,21 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
|||
final ForeignKeyDescriptor fkDescriptor = associationMapping.getForeignKeyDescriptor();
|
||||
final String lhsTable;
|
||||
final ModelPart lhsPart;
|
||||
if ( associationMapping.getSideNature() == ForeignKeyDescriptor.Nature.KEY ) {
|
||||
boolean useKeyPart = associationMapping.getSideNature() == ForeignKeyDescriptor.Nature.KEY;
|
||||
if ( mapping instanceof EntityCollectionPart ) {
|
||||
// EntityCollectionPart always returns TARGET, but sometimes we still want to use the KEY (element) side.
|
||||
// With a collection table, we can always use the TARGET for referring to the collection,
|
||||
// but in case of a one-to-many without collection table, this would be problematic,
|
||||
// as we can't use e.g. the primary key of the owner entity as substitution.
|
||||
final TableGroup pluralTableGroup = sqlAstCreationState.getFromClauseAccess()
|
||||
.findTableGroup( navigablePath.getParent() );
|
||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) pluralTableGroup.getModelPart();
|
||||
if ( pluralAttributeMapping.getSeparateCollectionTable() == null ) {
|
||||
useKeyPart = true;
|
||||
tableGroup = pluralTableGroup;
|
||||
}
|
||||
}
|
||||
if ( useKeyPart ) {
|
||||
lhsTable = fkDescriptor.getKeyTable();
|
||||
lhsPart = fkDescriptor.getKeyPart();
|
||||
}
|
||||
|
@ -167,12 +185,13 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
|||
else {
|
||||
assert mapping instanceof EntityMappingType;
|
||||
|
||||
final TableGroup parentTableGroup = tableGroup;
|
||||
final EntityMappingType entityMappingType = (EntityMappingType) mapping;
|
||||
final EntityIdentifierMapping identifierMapping = entityMappingType.getIdentifierMapping();
|
||||
if ( identifierMapping instanceof BasicEntityIdentifierMapping ) {
|
||||
final BasicEntityIdentifierMapping simpleIdMapping = (BasicEntityIdentifierMapping) identifierMapping;
|
||||
|
||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||
final TableReference tableReference = parentTableGroup.resolveTableReference(
|
||||
navigablePath,
|
||||
simpleIdMapping.getContainingTableExpression()
|
||||
);
|
||||
|
@ -191,7 +210,7 @@ public class EntityValuedPathInterpretation<T> extends AbstractSqmPathInterpreta
|
|||
final List<Expression> expressions = new ArrayList<>();
|
||||
identifierMapping.forEachSelectable(
|
||||
(selectionIndex, selectableMapping) -> {
|
||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||
final TableReference tableReference = parentTableGroup.resolveTableReference(
|
||||
navigablePath, selectableMapping.getContainingTableExpression() );
|
||||
|
||||
expressions.add(
|
||||
|
|
|
@ -105,7 +105,7 @@ public abstract class AbstractSqmPath<T> extends AbstractSqmExpression<T> implem
|
|||
reusablePaths = new HashMap<>();
|
||||
}
|
||||
|
||||
final String relativeName = path.getNavigablePath().getLocalName();
|
||||
final String relativeName = path.getNavigablePath().getUnaliasedLocalName();
|
||||
|
||||
final SqmPath<?> previous = reusablePaths.put( relativeName, path );
|
||||
if ( previous != null && previous != path ) {
|
||||
|
|
|
@ -7,12 +7,20 @@
|
|||
package org.hibernate.query.sqm.tree.domain;
|
||||
|
||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||
import org.hibernate.metamodel.model.domain.ListPersistentAttribute;
|
||||
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.SemanticException;
|
||||
import org.hibernate.query.hql.spi.SqmCreationState;
|
||||
import org.hibernate.query.hql.spi.SqmPathRegistry;
|
||||
import org.hibernate.query.sqm.NodeBuilder;
|
||||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
import org.hibernate.query.sqm.tree.from.SqmQualifiedJoin;
|
||||
|
||||
/**
|
||||
* An SqmPath for plural attribute paths
|
||||
|
@ -68,6 +76,61 @@ public class SqmPluralValuedSimplePath<E> extends AbstractSqmSimplePath<E> {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmPath<?> resolveIndexedAccess(
|
||||
SqmExpression<?> selector,
|
||||
boolean isTerminal,
|
||||
SqmCreationState creationState) {
|
||||
final SqmPathRegistry pathRegistry = creationState.getCurrentProcessingState().getPathRegistry();
|
||||
final String alias = selector.toHqlString();
|
||||
final NavigablePath navigablePath = getNavigablePath().getParent().append(
|
||||
getNavigablePath().getUnaliasedLocalName(),
|
||||
alias
|
||||
);
|
||||
SqmFrom<?, ?> path = pathRegistry.findFromByPath( navigablePath );
|
||||
if ( path == null ) {
|
||||
final PluralPersistentAttribute<?, ?, E> referencedPathSource = getReferencedPathSource();
|
||||
final SqmFrom<?, Object> parent = pathRegistry.resolveFrom( getLhs() );
|
||||
final SqmQualifiedJoin<Object, ?> join;
|
||||
final SqmExpression<?> index;
|
||||
if ( referencedPathSource instanceof ListPersistentAttribute<?, ?> ) {
|
||||
//noinspection unchecked
|
||||
join = new SqmListJoin<>(
|
||||
parent,
|
||||
(ListPersistentAttribute<Object, ?>) referencedPathSource,
|
||||
alias,
|
||||
SqmJoinType.INNER,
|
||||
false,
|
||||
parent.nodeBuilder()
|
||||
);
|
||||
index = ( (SqmListJoin<?, ?>) join ).index();
|
||||
}
|
||||
else if ( referencedPathSource instanceof MapPersistentAttribute<?, ?, ?> ) {
|
||||
//noinspection unchecked
|
||||
join = new SqmMapJoin<>(
|
||||
parent,
|
||||
(MapPersistentAttribute<Object, ?, ?>) referencedPathSource,
|
||||
alias,
|
||||
SqmJoinType.INNER,
|
||||
false,
|
||||
parent.nodeBuilder()
|
||||
);
|
||||
index = ( (SqmMapJoin<?, ?, ?>) join ).key();
|
||||
}
|
||||
else {
|
||||
throw new SemanticException( "Index access is only supported on list or map attributes: " + getNavigablePath() );
|
||||
}
|
||||
join.setJoinPredicate( creationState.getCreationContext().getNodeBuilder().equal( index, selector ) );
|
||||
parent.addSqmJoin( join );
|
||||
pathRegistry.register( path = join );
|
||||
}
|
||||
return new SqmIndexedCollectionAccessPath<>(
|
||||
path.getNavigablePath(),
|
||||
path,
|
||||
selector
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <S extends E> SqmTreatedSimplePath<E,S> treatAs(Class<S> treatJavaType) throws PathException {
|
||||
return (SqmTreatedSimplePath<E, S>) treatAs( nodeBuilder().getDomainModel().entity( treatJavaType ) );
|
||||
|
|
|
@ -21,7 +21,7 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
|
|||
public class SimpleFromClauseAccessImpl implements FromClauseAccess {
|
||||
|
||||
protected final FromClauseAccess parent;
|
||||
protected final Map<String, TableGroup> tableGroupMap = new HashMap<>();
|
||||
protected final Map<NavigablePath, TableGroup> tableGroupMap = new HashMap<>();
|
||||
|
||||
public SimpleFromClauseAccessImpl() {
|
||||
this( null );
|
||||
|
@ -33,7 +33,7 @@ public class SimpleFromClauseAccessImpl implements FromClauseAccess {
|
|||
|
||||
@Override
|
||||
public TableGroup findTableGroup(NavigablePath navigablePath) {
|
||||
final TableGroup tableGroup = tableGroupMap.get( navigablePath.getIdentifierForTableGroup() );
|
||||
final TableGroup tableGroup = tableGroupMap.get( navigablePath );
|
||||
if ( tableGroup == null && parent != null ) {
|
||||
return parent.findTableGroup( navigablePath );
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ public class SimpleFromClauseAccessImpl implements FromClauseAccess {
|
|||
tableGroup.getNavigablePath().getIdentifierForTableGroup(),
|
||||
navigablePath.getIdentifierForTableGroup()
|
||||
);
|
||||
final TableGroup previous = tableGroupMap.put( navigablePath.getIdentifierForTableGroup(), tableGroup );
|
||||
final TableGroup previous = tableGroupMap.put( navigablePath, tableGroup );
|
||||
if ( previous != null ) {
|
||||
SqlTreeCreationLogger.LOGGER.debugf(
|
||||
"Registration of TableGroup [%s] for NavigablePath [%s] overrode previous registration : %s",
|
||||
|
|
|
@ -22,16 +22,19 @@ public class CompositeTableGroup implements VirtualTableGroup {
|
|||
private final EmbeddableValuedModelPart compositionMapping;
|
||||
|
||||
private final TableGroup underlyingTableGroup;
|
||||
private final boolean fetched;
|
||||
|
||||
private List<TableGroupJoin> tableGroupJoins;
|
||||
|
||||
public CompositeTableGroup(
|
||||
NavigablePath navigablePath,
|
||||
EmbeddableValuedModelPart compositionMapping,
|
||||
TableGroup underlyingTableGroup) {
|
||||
TableGroup underlyingTableGroup,
|
||||
boolean fetched) {
|
||||
this.navigablePath = navigablePath;
|
||||
this.compositionMapping = compositionMapping;
|
||||
this.underlyingTableGroup = underlyingTableGroup;
|
||||
this.fetched = fetched;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -50,6 +53,23 @@ public class CompositeTableGroup implements VirtualTableGroup {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFetched() {
|
||||
// if ( fetched ) {
|
||||
// return true;
|
||||
// }
|
||||
// // We also consider it "fetched" if it contains fetched joins
|
||||
// if ( tableGroupJoins != null ) {
|
||||
// for ( TableGroupJoin tableGroupJoin : tableGroupJoins ) {
|
||||
// if ( tableGroupJoin.getJoinedGroup().isFetched() ) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
return fetched;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmbeddableValuedModelPart getModelPart() {
|
||||
return compositionMapping;
|
||||
|
|
|
@ -24,6 +24,7 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
|
|||
|
||||
private final boolean canUseInnerJoins;
|
||||
private final NavigablePath navigablePath;
|
||||
private final boolean fetched;
|
||||
private final TableGroupProducer producer;
|
||||
private final String sourceAlias;
|
||||
private final SqlAliasBase sqlAliasBase;
|
||||
|
@ -37,6 +38,7 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
|
|||
public LazyTableGroup(
|
||||
boolean canUseInnerJoins,
|
||||
NavigablePath navigablePath,
|
||||
boolean fetched,
|
||||
Supplier<TableGroup> tableGroupSupplier,
|
||||
BiPredicate<NavigablePath, String> navigablePathChecker,
|
||||
TableGroupProducer tableGroupProducer,
|
||||
|
@ -46,6 +48,7 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
|
|||
TableGroup parentTableGroup) {
|
||||
this.canUseInnerJoins = canUseInnerJoins;
|
||||
this.navigablePath = navigablePath;
|
||||
this.fetched = fetched;
|
||||
this.producer = tableGroupProducer;
|
||||
this.sourceAlias = sourceAlias;
|
||||
this.sqlAliasBase = sqlAliasBase;
|
||||
|
@ -155,6 +158,11 @@ public class LazyTableGroup extends AbstractColumnReferenceQualifier implements
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFetched() {
|
||||
return fetched;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableReference getTableReferenceInternal(
|
||||
NavigablePath navigablePath,
|
||||
|
|
|
@ -14,6 +14,7 @@ import org.hibernate.query.results.ResultsHelper;
|
|||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
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.results.graph.AbstractFetchParent;
|
||||
import org.hibernate.sql.results.graph.AssemblerCreationState;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
|
@ -35,6 +36,7 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab
|
|||
|
||||
private final FetchParent fetchParent;
|
||||
private final FetchTiming fetchTiming;
|
||||
private final TableGroup tableGroup;
|
||||
private final boolean hasTableGroup;
|
||||
private final boolean nullable;
|
||||
|
||||
|
@ -54,7 +56,7 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab
|
|||
this.hasTableGroup = hasTableGroup;
|
||||
this.nullable = nullable;
|
||||
|
||||
creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
|
||||
this.tableGroup = creationState.getSqlAstCreationState().getFromClauseAccess().resolveTableGroup(
|
||||
getNavigablePath(),
|
||||
np -> {
|
||||
final TableGroup lhsTableGroup = creationState.getSqlAstCreationState()
|
||||
|
@ -106,6 +108,19 @@ public class EmbeddableFetchImpl extends AbstractFetchParent implements Embeddab
|
|||
return getReferencedMappingContainer();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public NavigablePath resolveNavigablePath(Fetchable fetchable) {
|
||||
if ( fetchable instanceof TableGroupProducer ) {
|
||||
for ( TableGroupJoin tableGroupJoin : tableGroup.getTableGroupJoins() ) {
|
||||
if ( tableGroupJoin.getJoinedGroup().isFetched() && tableGroupJoin.getJoinedGroup().getModelPart() == fetchable ) {
|
||||
return tableGroupJoin.getNavigablePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.resolveNavigablePath( fetchable );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomainResult<?> asResult(DomainResultCreationState creationState) {
|
||||
return embeddedPartDescriptor.createDomainResult(
|
||||
|
|
|
@ -61,8 +61,6 @@ public class EntityResultImpl extends AbstractEntityResultGraphNode implements E
|
|||
|
||||
@Override
|
||||
public NavigablePath resolveNavigablePath(Fetchable fetchable) {
|
||||
// todo: this is not ideal yet as we could potentially resolve a path that we did not intend
|
||||
// to fix this, we'd need to know if the table group is for a fetch
|
||||
if ( fetchable instanceof TableGroupProducer &&
|
||||
!getNavigablePath().getUnaliasedLocalName().equals( getNavigablePath().getLocalName() ) ) {
|
||||
for ( TableGroupJoin tableGroupJoin : tableGroup.getTableGroupJoins() ) {
|
||||
|
|
|
@ -17,7 +17,7 @@ import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
|||
* @author Archie Cobbs
|
||||
* @author Nathan Xu
|
||||
*/
|
||||
@RequiresDialect( MySQLDialect.class )
|
||||
@RequiresDialect( value = MySQLDialect.class, strictMatching = true )
|
||||
public class HHH13908Test extends BaseEntityManagerFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -533,8 +533,7 @@ public class OneToManySizeTest2 {
|
|||
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > -1",
|
||||
Student.class
|
||||
).getResultList();
|
||||
// Since the join for "student.teacher" is never used and is a non-optional association we don't generate a SQL join for it
|
||||
assertEquals( 2, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) );
|
||||
assertEquals( 3, countNumberOfJoins( statementInspector.getSqlQueries().get( 0 ) ) );
|
||||
assertEquals( 3L, students.size() );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||
|
|
Loading…
Reference in New Issue