HHH-18584 fix logic for deciding if something is implicitly selectable
implicit joins should not be added to the select list! Signed-off-by: Gavin King <gavin@hibernate.org>
This commit is contained in:
parent
306991f8d9
commit
0c1a1e9832
|
@ -1286,10 +1286,10 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private EntityDomainType<R> getResultEntity() {
|
||||
final JpaMetamodelImplementor jpaMetamodel = creationContext.getJpaMetamodel();
|
||||
if ( expectedResultEntity != null ) {
|
||||
@SuppressWarnings("rawtypes")
|
||||
final EntityDomainType entityDescriptor = jpaMetamodel.entity( expectedResultEntity );
|
||||
if ( entityDescriptor == null ) {
|
||||
throw new SemanticException( "Query has no 'from' clause, and the result type '"
|
||||
|
@ -1331,7 +1331,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
// we found an entity with the alias 'this'
|
||||
// assigned explicitly, JPA says we should
|
||||
// infer the select list 'select this'
|
||||
SqmSelectClause selectClause = new SqmSelectClause( false, 1, nodeBuilder );
|
||||
final SqmSelectClause selectClause =
|
||||
new SqmSelectClause( false, 1, nodeBuilder );
|
||||
selectClause.addSelection( new SqmSelection<>( sqmRoot, "this", nodeBuilder) );
|
||||
return selectClause;
|
||||
}
|
||||
|
@ -1353,7 +1354,7 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
// we may safely assume the query returns that entity
|
||||
if ( fromClause.getNumberOfRoots() == 1 ) {
|
||||
final SqmRoot<?> sqmRoot = fromClause.getRoots().get(0);
|
||||
if ( sqmRoot.hasTrueJoin() ) {
|
||||
if ( sqmRoot.hasImplicitlySelectableJoin() ) {
|
||||
// the entity has joins, and doesn't explicitly have
|
||||
// the alias 'this', so the 'select' list cannot be
|
||||
// inferred
|
||||
|
@ -1365,7 +1366,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
// 'this', and that we should infer 'select this', but we
|
||||
// accept even the case where the entity has an explicit
|
||||
// alias, and infer 'select explicit_alias'
|
||||
SqmSelectClause selectClause = new SqmSelectClause( false, 1, nodeBuilder );
|
||||
final SqmSelectClause selectClause =
|
||||
new SqmSelectClause( false, 1, nodeBuilder );
|
||||
selectClause.addSelection( new SqmSelection<>( sqmRoot, sqmRoot.getAlias(), nodeBuilder) );
|
||||
return selectClause;
|
||||
}
|
||||
|
@ -1398,7 +1400,8 @@ public class SemanticQueryBuilder<R> extends HqlParserBaseVisitor<Object> implem
|
|||
else {
|
||||
// exactly one root entity, return it
|
||||
// (joined entities are not returned)
|
||||
final SqmSelectClause selectClause = new SqmSelectClause( false, 1, nodeBuilder );
|
||||
final SqmSelectClause selectClause =
|
||||
new SqmSelectClause( false, 1, nodeBuilder );
|
||||
selectClause.addSelection( new SqmSelection<>( sqmRoot, sqmRoot.getAlias(), nodeBuilder) );
|
||||
return selectClause;
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import org.hibernate.query.criteria.JpaExpression;
|
|||
import org.hibernate.query.criteria.JpaPredicate;
|
||||
import org.hibernate.query.sqm.NodeBuilder;
|
||||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||
import org.hibernate.query.sqm.SqmJoinable;
|
||||
import org.hibernate.query.sqm.SqmPathSource;
|
||||
import org.hibernate.query.sqm.spi.SqmCreationHelper;
|
||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||
|
@ -33,48 +32,38 @@ public abstract class AbstractSqmAttributeJoin<L, R>
|
|||
extends AbstractSqmJoin<L, R>
|
||||
implements SqmAttributeJoin<L, R> {
|
||||
|
||||
private boolean fetched;
|
||||
private final boolean implicitJoin;
|
||||
private boolean fetchJoin;
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
public AbstractSqmAttributeJoin(
|
||||
SqmFrom<?, L> lhs,
|
||||
SqmJoinable joinedNavigable,
|
||||
String alias,
|
||||
SqmJoinType joinType,
|
||||
boolean fetched,
|
||||
NodeBuilder nodeBuilder) {
|
||||
//noinspection StringEquality
|
||||
this(
|
||||
lhs,
|
||||
joinedNavigable.createNavigablePath( lhs, alias ),
|
||||
joinedNavigable,
|
||||
alias == SqmCreationHelper.IMPLICIT_ALIAS ? null : alias,
|
||||
joinType,
|
||||
fetched,
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
protected AbstractSqmAttributeJoin(
|
||||
SqmFrom<?, L> lhs,
|
||||
NavigablePath navigablePath,
|
||||
SqmJoinable joinedNavigable,
|
||||
SqmPathSource<R> joinedNavigable,
|
||||
String alias,
|
||||
SqmJoinType joinType,
|
||||
boolean fetched,
|
||||
boolean fetchJoin,
|
||||
NodeBuilder nodeBuilder) {
|
||||
//noinspection unchecked
|
||||
super(
|
||||
navigablePath,
|
||||
(SqmPathSource<R>) joinedNavigable,
|
||||
joinedNavigable,
|
||||
lhs,
|
||||
alias,
|
||||
isImplicitAlias( alias ) ? null : alias,
|
||||
joinType,
|
||||
nodeBuilder
|
||||
);
|
||||
this.fetched = fetched;
|
||||
this.fetchJoin = fetchJoin;
|
||||
validateFetchAlias( alias );
|
||||
implicitJoin = isImplicitAlias( alias ); //TODO: add a parameter
|
||||
}
|
||||
|
||||
@SuppressWarnings("StringEquality")
|
||||
private static boolean isImplicitAlias(String alias) {
|
||||
return alias == SqmCreationHelper.IMPLICIT_ALIAS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImplicitJoin() {
|
||||
return implicitJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -89,7 +78,7 @@ public abstract class AbstractSqmAttributeJoin<L, R>
|
|||
|
||||
@Override
|
||||
public boolean isFetched() {
|
||||
return fetched;
|
||||
return fetchJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -100,11 +89,11 @@ public abstract class AbstractSqmAttributeJoin<L, R>
|
|||
|
||||
@Override
|
||||
public void clearFetched() {
|
||||
fetched = false;
|
||||
fetchJoin = false;
|
||||
}
|
||||
|
||||
private void validateFetchAlias(String alias) {
|
||||
if ( fetched && alias != null && nodeBuilder().isJpaQueryComplianceEnabled() ) {
|
||||
if ( fetchJoin && alias != null && nodeBuilder().isJpaQueryComplianceEnabled() ) {
|
||||
throw new IllegalStateException(
|
||||
"The JPA specification does not permit specifying an alias for fetch joins."
|
||||
);
|
||||
|
|
|
@ -60,7 +60,6 @@ import jakarta.persistence.metamodel.PluralAttribute;
|
|||
import jakarta.persistence.metamodel.SetAttribute;
|
||||
import jakarta.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import static org.hibernate.metamodel.AttributeClassification.EMBEDDED;
|
||||
import static org.hibernate.query.sqm.internal.SqmUtil.findCompatibleFetchJoin;
|
||||
|
||||
/**
|
||||
|
@ -163,9 +162,8 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
|||
SqmPath<?> resolvedPath = null;
|
||||
for ( SqmJoin<?, ?> sqmJoin : getSqmJoins() ) {
|
||||
// We can only match singular joins here, as plural path parts are interpreted like sub-queries
|
||||
if ( sqmJoin instanceof SqmSingularJoin<?, ?>
|
||||
if ( sqmJoin instanceof SqmSingularJoin<?, ?> attributeJoin
|
||||
&& 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,
|
||||
|
@ -223,8 +221,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
|||
public void removeLeftFetchJoins() {
|
||||
if ( joins != null ) {
|
||||
for ( SqmJoin<T, ?> join : new ArrayList<>(joins) ) {
|
||||
if ( join instanceof SqmAttributeJoin ) {
|
||||
final SqmAttributeJoin<T, ?> attributeJoin = (SqmAttributeJoin<T, ?>) join;
|
||||
if ( join instanceof SqmAttributeJoin<T, ?> attributeJoin ) {
|
||||
if ( attributeJoin.isFetched() ) {
|
||||
if ( join.getSqmJoinType() == SqmJoinType.LEFT ) {
|
||||
joins.remove( join );
|
||||
|
@ -306,11 +303,10 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean hasTrueJoin() {
|
||||
public boolean hasImplicitlySelectableJoin() {
|
||||
return getSqmJoins().stream()
|
||||
.anyMatch( sqmJoin -> sqmJoin instanceof SqmAttributeJoin<?,?> attributeJoin
|
||||
&& !attributeJoin.isFetched()
|
||||
&& attributeJoin.getAttribute().getAttributeClassification()!=EMBEDDED );
|
||||
&& attributeJoin.isImplicitlySelectable() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -642,8 +638,7 @@ public abstract class AbstractSqmFrom<O,T> extends AbstractSqmPath<T> implements
|
|||
|
||||
@Override
|
||||
public <X> JpaCrossJoin<X> crossJoin(EntityDomainType<X> entity) {
|
||||
//noinspection unchecked
|
||||
final SqmCrossJoin<X> crossJoin = new SqmCrossJoin<>( entity, null, (SqmRoot<X>) findRoot() );
|
||||
final SqmCrossJoin<X> crossJoin = new SqmCrossJoin<>( entity, null, findRoot() );
|
||||
// noinspection unchecked
|
||||
addSqmJoin( (SqmJoin<T, ?>) crossJoin );
|
||||
return crossJoin;
|
||||
|
|
|
@ -35,7 +35,15 @@ public abstract class AbstractSqmPluralJoin<L,C,E>
|
|||
SqmJoinType joinType,
|
||||
boolean fetched,
|
||||
NodeBuilder nodeBuilder) {
|
||||
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
|
||||
super(
|
||||
lhs,
|
||||
joinedNavigable.createNavigablePath( lhs, alias ),
|
||||
joinedNavigable,
|
||||
alias,
|
||||
joinType,
|
||||
fetched,
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
|
||||
protected AbstractSqmPluralJoin(
|
||||
|
|
|
@ -15,7 +15,6 @@ import org.hibernate.metamodel.model.domain.TreatableDomainType;
|
|||
import org.hibernate.query.sqm.SemanticQueryWalker;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.query.sqm.NodeBuilder;
|
||||
import org.hibernate.query.sqm.SqmJoinable;
|
||||
import org.hibernate.query.sqm.tree.SqmCopyContext;
|
||||
import org.hibernate.query.sqm.tree.SqmJoinType;
|
||||
import org.hibernate.query.sqm.tree.from.SqmFrom;
|
||||
|
@ -25,6 +24,7 @@ import org.hibernate.query.sqm.tree.from.SqmTreatedAttributeJoin;
|
|||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SqmSingularJoin<O,T> extends AbstractSqmAttributeJoin<O,T> implements SqmSingularValuedJoin<O,T> {
|
||||
|
||||
public SqmSingularJoin(
|
||||
SqmFrom<?,O> lhs,
|
||||
SingularPersistentAttribute<O, T> joinedNavigable,
|
||||
|
@ -32,17 +32,15 @@ public class SqmSingularJoin<O,T> extends AbstractSqmAttributeJoin<O,T> implemen
|
|||
SqmJoinType joinType,
|
||||
boolean fetched,
|
||||
NodeBuilder nodeBuilder) {
|
||||
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
|
||||
}
|
||||
|
||||
public SqmSingularJoin(
|
||||
SqmFrom<?,O> lhs,
|
||||
SqmJoinable<? extends O, T> joinedNavigable,
|
||||
String alias,
|
||||
SqmJoinType joinType,
|
||||
boolean fetched,
|
||||
NodeBuilder nodeBuilder) {
|
||||
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
|
||||
super(
|
||||
lhs,
|
||||
joinedNavigable.createNavigablePath( lhs, alias ),
|
||||
joinedNavigable,
|
||||
alias,
|
||||
joinType,
|
||||
fetched,
|
||||
nodeBuilder
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -30,7 +30,7 @@ public interface SqmAttributeJoin<O,T> extends SqmJoin<O,T>, JpaFetch<O,T>, JpaJ
|
|||
|
||||
@Override
|
||||
default boolean isImplicitlySelectable() {
|
||||
return !isFetched();
|
||||
return !isFetched() && !isImplicitJoin();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,8 +39,16 @@ public interface SqmAttributeJoin<O,T> extends SqmJoin<O,T>, JpaFetch<O,T>, JpaJ
|
|||
@Override
|
||||
JavaType<T> getJavaTypeDescriptor();
|
||||
|
||||
/**
|
||||
* Is this a fetch join?
|
||||
*/
|
||||
boolean isFetched();
|
||||
|
||||
/**
|
||||
* Is this an implicit join inferred from a path expression?
|
||||
*/
|
||||
boolean isImplicitJoin();
|
||||
|
||||
@Internal
|
||||
void clearFetched();
|
||||
|
||||
|
|
|
@ -102,7 +102,7 @@ public interface SqmFrom<L, R> extends SqmVisitableNode, SqmPath<R>, JpaFrom<L,
|
|||
<Y> SqmEntityJoin<R, Y> join(Class<Y> entityClass, JoinType joinType);
|
||||
|
||||
@Incubating
|
||||
boolean hasTrueJoin();
|
||||
boolean hasImplicitlySelectableJoin();
|
||||
|
||||
@Override
|
||||
<A> SqmSingularJoin<R, A> join(SingularAttribute<? super R, A> attribute);
|
||||
|
|
Loading…
Reference in New Issue