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