Skip to content

Commit 0c1a1e9

Browse files
committed
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 <[email protected]>
1 parent 306991f commit 0c1a1e9

File tree

7 files changed

+63
-62
lines changed

7 files changed

+63
-62
lines changed

hibernate-core/src/main/java/org/hibernate/query/hql/internal/SemanticQueryBuilder.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,10 +1286,10 @@ private SqmFromClause buildInferredFromClause(HqlParser.SelectClauseContext sele
12861286
}
12871287
}
12881288

1289-
@SuppressWarnings("rawtypes")
12901289
private EntityDomainType<R> getResultEntity() {
12911290
final JpaMetamodelImplementor jpaMetamodel = creationContext.getJpaMetamodel();
12921291
if ( expectedResultEntity != null ) {
1292+
@SuppressWarnings("rawtypes")
12931293
final EntityDomainType entityDescriptor = jpaMetamodel.entity( expectedResultEntity );
12941294
if ( entityDescriptor == null ) {
12951295
throw new SemanticException( "Query has no 'from' clause, and the result type '"
@@ -1331,7 +1331,8 @@ protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
13311331
// we found an entity with the alias 'this'
13321332
// assigned explicitly, JPA says we should
13331333
// infer the select list 'select this'
1334-
SqmSelectClause selectClause = new SqmSelectClause( false, 1, nodeBuilder );
1334+
final SqmSelectClause selectClause =
1335+
new SqmSelectClause( false, 1, nodeBuilder );
13351336
selectClause.addSelection( new SqmSelection<>( sqmRoot, "this", nodeBuilder) );
13361337
return selectClause;
13371338
}
@@ -1353,7 +1354,7 @@ protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
13531354
// we may safely assume the query returns that entity
13541355
if ( fromClause.getNumberOfRoots() == 1 ) {
13551356
final SqmRoot<?> sqmRoot = fromClause.getRoots().get(0);
1356-
if ( sqmRoot.hasTrueJoin() ) {
1357+
if ( sqmRoot.hasImplicitlySelectableJoin() ) {
13571358
// the entity has joins, and doesn't explicitly have
13581359
// the alias 'this', so the 'select' list cannot be
13591360
// inferred
@@ -1365,7 +1366,8 @@ protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
13651366
// 'this', and that we should infer 'select this', but we
13661367
// accept even the case where the entity has an explicit
13671368
// alias, and infer 'select explicit_alias'
1368-
SqmSelectClause selectClause = new SqmSelectClause( false, 1, nodeBuilder );
1369+
final SqmSelectClause selectClause =
1370+
new SqmSelectClause( false, 1, nodeBuilder );
13691371
selectClause.addSelection( new SqmSelection<>( sqmRoot, sqmRoot.getAlias(), nodeBuilder) );
13701372
return selectClause;
13711373
}
@@ -1398,7 +1400,8 @@ protected SqmSelectClause buildInferredSelectClause(SqmFromClause fromClause) {
13981400
else {
13991401
// exactly one root entity, return it
14001402
// (joined entities are not returned)
1401-
final SqmSelectClause selectClause = new SqmSelectClause( false, 1, nodeBuilder );
1403+
final SqmSelectClause selectClause =
1404+
new SqmSelectClause( false, 1, nodeBuilder );
14021405
selectClause.addSelection( new SqmSelection<>( sqmRoot, sqmRoot.getAlias(), nodeBuilder) );
14031406
return selectClause;
14041407
}

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmAttributeJoin.java

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import org.hibernate.query.criteria.JpaPredicate;
1313
import org.hibernate.query.sqm.NodeBuilder;
1414
import org.hibernate.query.sqm.SemanticQueryWalker;
15-
import org.hibernate.query.sqm.SqmJoinable;
1615
import org.hibernate.query.sqm.SqmPathSource;
1716
import org.hibernate.query.sqm.spi.SqmCreationHelper;
1817
import org.hibernate.query.sqm.tree.SqmJoinType;
@@ -33,48 +32,38 @@ public abstract class AbstractSqmAttributeJoin<L, R>
3332
extends AbstractSqmJoin<L, R>
3433
implements SqmAttributeJoin<L, R> {
3534

36-
private boolean fetched;
35+
private final boolean implicitJoin;
36+
private boolean fetchJoin;
3737

38-
@SuppressWarnings({ "rawtypes", "unchecked" })
39-
public AbstractSqmAttributeJoin(
40-
SqmFrom<?, L> lhs,
41-
SqmJoinable joinedNavigable,
42-
String alias,
43-
SqmJoinType joinType,
44-
boolean fetched,
45-
NodeBuilder nodeBuilder) {
46-
//noinspection StringEquality
47-
this(
48-
lhs,
49-
joinedNavigable.createNavigablePath( lhs, alias ),
50-
joinedNavigable,
51-
alias == SqmCreationHelper.IMPLICIT_ALIAS ? null : alias,
52-
joinType,
53-
fetched,
54-
nodeBuilder
55-
);
56-
}
57-
58-
@SuppressWarnings("rawtypes")
5938
protected AbstractSqmAttributeJoin(
6039
SqmFrom<?, L> lhs,
6140
NavigablePath navigablePath,
62-
SqmJoinable joinedNavigable,
41+
SqmPathSource<R> joinedNavigable,
6342
String alias,
6443
SqmJoinType joinType,
65-
boolean fetched,
44+
boolean fetchJoin,
6645
NodeBuilder nodeBuilder) {
67-
//noinspection unchecked
6846
super(
6947
navigablePath,
70-
(SqmPathSource<R>) joinedNavigable,
48+
joinedNavigable,
7149
lhs,
72-
alias,
50+
isImplicitAlias( alias ) ? null : alias,
7351
joinType,
7452
nodeBuilder
7553
);
76-
this.fetched = fetched;
54+
this.fetchJoin = fetchJoin;
7755
validateFetchAlias( alias );
56+
implicitJoin = isImplicitAlias( alias ); //TODO: add a parameter
57+
}
58+
59+
@SuppressWarnings("StringEquality")
60+
private static boolean isImplicitAlias(String alias) {
61+
return alias == SqmCreationHelper.IMPLICIT_ALIAS;
62+
}
63+
64+
@Override
65+
public boolean isImplicitJoin() {
66+
return implicitJoin;
7867
}
7968

8069
@Override
@@ -89,7 +78,7 @@ public JavaType<R> getNodeJavaType() {
8978

9079
@Override
9180
public boolean isFetched() {
92-
return fetched;
81+
return fetchJoin;
9382
}
9483

9584
@Override
@@ -100,11 +89,11 @@ public SqmAttributeJoin<L,R> alias(String name) {
10089

10190
@Override
10291
public void clearFetched() {
103-
fetched = false;
92+
fetchJoin = false;
10493
}
10594

10695
private void validateFetchAlias(String alias) {
107-
if ( fetched && alias != null && nodeBuilder().isJpaQueryComplianceEnabled() ) {
96+
if ( fetchJoin && alias != null && nodeBuilder().isJpaQueryComplianceEnabled() ) {
10897
throw new IllegalStateException(
10998
"The JPA specification does not permit specifying an alias for fetch joins."
11099
);

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmFrom.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@
6060
import jakarta.persistence.metamodel.SetAttribute;
6161
import jakarta.persistence.metamodel.SingularAttribute;
6262

63-
import static org.hibernate.metamodel.AttributeClassification.EMBEDDED;
6463
import static org.hibernate.query.sqm.internal.SqmUtil.findCompatibleFetchJoin;
6564

6665
/**
@@ -163,9 +162,8 @@ public SqmPath<?> resolvePathPart(
163162
SqmPath<?> resolvedPath = null;
164163
for ( SqmJoin<?, ?> sqmJoin : getSqmJoins() ) {
165164
// We can only match singular joins here, as plural path parts are interpreted like sub-queries
166-
if ( sqmJoin instanceof SqmSingularJoin<?, ?>
165+
if ( sqmJoin instanceof SqmSingularJoin<?, ?> attributeJoin
167166
&& name.equals( sqmJoin.getReferencedPathSource().getPathName() ) ) {
168-
final SqmAttributeJoin<?, ?> attributeJoin = (SqmAttributeJoin<?, ?>) sqmJoin;
169167
if ( attributeJoin.getOn() == null ) {
170168
// todo (6.0): to match the expectation of the JPA spec I think we also have to check
171169
// that the join type is INNER or the default join type for the attribute,
@@ -223,8 +221,7 @@ public void addSqmJoin(SqmJoin<T, ?> join) {
223221
public void removeLeftFetchJoins() {
224222
if ( joins != null ) {
225223
for ( SqmJoin<T, ?> join : new ArrayList<>(joins) ) {
226-
if ( join instanceof SqmAttributeJoin ) {
227-
final SqmAttributeJoin<T, ?> attributeJoin = (SqmAttributeJoin<T, ?>) join;
224+
if ( join instanceof SqmAttributeJoin<T, ?> attributeJoin ) {
228225
if ( attributeJoin.isFetched() ) {
229226
if ( join.getSqmJoinType() == SqmJoinType.LEFT ) {
230227
joins.remove( join );
@@ -306,11 +303,10 @@ public boolean isCorrelated() {
306303
}
307304

308305
@Override
309-
public boolean hasTrueJoin() {
306+
public boolean hasImplicitlySelectableJoin() {
310307
return getSqmJoins().stream()
311308
.anyMatch( sqmJoin -> sqmJoin instanceof SqmAttributeJoin<?,?> attributeJoin
312-
&& !attributeJoin.isFetched()
313-
&& attributeJoin.getAttribute().getAttributeClassification()!=EMBEDDED );
309+
&& attributeJoin.isImplicitlySelectable() );
314310
}
315311

316312
@Override
@@ -642,8 +638,7 @@ public <X> JpaCrossJoin<X> crossJoin(Class<X> entityJavaType) {
642638

643639
@Override
644640
public <X> JpaCrossJoin<X> crossJoin(EntityDomainType<X> entity) {
645-
//noinspection unchecked
646-
final SqmCrossJoin<X> crossJoin = new SqmCrossJoin<>( entity, null, (SqmRoot<X>) findRoot() );
641+
final SqmCrossJoin<X> crossJoin = new SqmCrossJoin<>( entity, null, findRoot() );
647642
// noinspection unchecked
648643
addSqmJoin( (SqmJoin<T, ?>) crossJoin );
649644
return crossJoin;

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/AbstractSqmPluralJoin.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,15 @@ public AbstractSqmPluralJoin(
3535
SqmJoinType joinType,
3636
boolean fetched,
3737
NodeBuilder nodeBuilder) {
38-
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
38+
super(
39+
lhs,
40+
joinedNavigable.createNavigablePath( lhs, alias ),
41+
joinedNavigable,
42+
alias,
43+
joinType,
44+
fetched,
45+
nodeBuilder
46+
);
3947
}
4048

4149
protected AbstractSqmPluralJoin(

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/domain/SqmSingularJoin.java

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
import org.hibernate.query.sqm.SemanticQueryWalker;
1616
import org.hibernate.spi.NavigablePath;
1717
import org.hibernate.query.sqm.NodeBuilder;
18-
import org.hibernate.query.sqm.SqmJoinable;
1918
import org.hibernate.query.sqm.tree.SqmCopyContext;
2019
import org.hibernate.query.sqm.tree.SqmJoinType;
2120
import org.hibernate.query.sqm.tree.from.SqmFrom;
@@ -25,24 +24,23 @@
2524
* @author Steve Ebersole
2625
*/
2726
public class SqmSingularJoin<O,T> extends AbstractSqmAttributeJoin<O,T> implements SqmSingularValuedJoin<O,T> {
28-
public SqmSingularJoin(
29-
SqmFrom<?,O> lhs,
30-
SingularPersistentAttribute<O, T> joinedNavigable,
31-
String alias,
32-
SqmJoinType joinType,
33-
boolean fetched,
34-
NodeBuilder nodeBuilder) {
35-
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
36-
}
3727

3828
public SqmSingularJoin(
3929
SqmFrom<?,O> lhs,
40-
SqmJoinable<? extends O, T> joinedNavigable,
30+
SingularPersistentAttribute<O, T> joinedNavigable,
4131
String alias,
4232
SqmJoinType joinType,
4333
boolean fetched,
4434
NodeBuilder nodeBuilder) {
45-
super( lhs, joinedNavigable, alias, joinType, fetched, nodeBuilder );
35+
super(
36+
lhs,
37+
joinedNavigable.createNavigablePath( lhs, alias ),
38+
joinedNavigable,
39+
alias,
40+
joinType,
41+
fetched,
42+
nodeBuilder
43+
);
4644
}
4745

4846
@Override

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmAttributeJoin.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public interface SqmAttributeJoin<O,T> extends SqmJoin<O,T>, JpaFetch<O,T>, JpaJ
3030

3131
@Override
3232
default boolean isImplicitlySelectable() {
33-
return !isFetched();
33+
return !isFetched() && !isImplicitJoin();
3434
}
3535

3636
@Override
@@ -39,8 +39,16 @@ default boolean isImplicitlySelectable() {
3939
@Override
4040
JavaType<T> getJavaTypeDescriptor();
4141

42+
/**
43+
* Is this a fetch join?
44+
*/
4245
boolean isFetched();
4346

47+
/**
48+
* Is this an implicit join inferred from a path expression?
49+
*/
50+
boolean isImplicitJoin();
51+
4452
@Internal
4553
void clearFetched();
4654

hibernate-core/src/main/java/org/hibernate/query/sqm/tree/from/SqmFrom.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ default boolean hasTreats() {
102102
<Y> SqmEntityJoin<R, Y> join(Class<Y> entityClass, JoinType joinType);
103103

104104
@Incubating
105-
boolean hasTrueJoin();
105+
boolean hasImplicitlySelectableJoin();
106106

107107
@Override
108108
<A> SqmSingularJoin<R, A> join(SingularAttribute<? super R, A> attribute);

0 commit comments

Comments
 (0)