Skip to content

Commit 1da8943

Browse files
beikovdreab8
authored andcommitted
HHH-15342 Inappropriate variation of HQL left join to SQL inner join
1 parent 985467b commit 1da8943

File tree

5 files changed

+84
-67
lines changed

5 files changed

+84
-67
lines changed

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/PluralAttributeMappingImpl.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,12 @@ public TableGroupJoin createTableGroupJoin(
559559
SqlAstCreationContext creationContext) {
560560
final SqlAstJoinType joinType;
561561
if ( requestedJoinType == null ) {
562-
joinType = SqlAstJoinType.INNER;
562+
if ( fetched ) {
563+
joinType = getDefaultSqlAstJoinType( lhs );
564+
}
565+
else {
566+
joinType = SqlAstJoinType.INNER;
567+
}
563568
}
564569
else {
565570
joinType = requestedJoinType;

hibernate-core/src/main/java/org/hibernate/metamodel/mapping/internal/ToOneAttributeMapping.java

Lines changed: 73 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,6 @@
100100
import org.hibernate.type.EntityType;
101101
import org.hibernate.type.Type;
102102

103-
import static java.util.Objects.requireNonNullElse;
104-
105103
/**
106104
* @author Steve Ebersole
107105
*/
@@ -1221,9 +1219,8 @@ public static class EntityB {
12211219
12221220
having the left join we don't want to add an extra implicit join that will be translated into an SQL inner join (see HHH-15342)
12231221
*/
1224-
if ( ( ( hasNotFoundAction() && doesNotExistATableGroupJoin( parentTableGroup, fetchablePath ) )
1225-
|| ( fetchTiming == FetchTiming.IMMEDIATE && selected ) ) ) {
1226-
final TableGroup tableGroup = determineTableGroup(
1222+
if ( fetchTiming == FetchTiming.IMMEDIATE && selected || hasNotFoundAction() ) {
1223+
final TableGroup tableGroup = determineTableGroupForFetch(
12271224
fetchablePath,
12281225
fetchParent,
12291226
parentTableGroup,
@@ -1332,58 +1329,66 @@ public static class EntityB {
13321329
);
13331330
}
13341331

1335-
private boolean doesNotExistATableGroupJoin(TableGroup parentTableGroup, NavigablePath fetchablePath) {
1336-
for ( TableGroupJoin tableGroupJoin : parentTableGroup.getTableGroupJoins() ) {
1337-
if ( tableGroupJoin.getNavigablePath().pathsMatch( fetchablePath ) ) {
1338-
return false;
1339-
}
1340-
}
1341-
return true;
1342-
}
1343-
1344-
private TableGroup determineTableGroup(NavigablePath fetchablePath, FetchParent fetchParent, TableGroup parentTableGroup, String resultVariable, FromClauseAccess fromClauseAccess, DomainResultCreationState creationState) {
1345-
final TableGroup tableGroup;
1332+
private TableGroup determineTableGroupForFetch(
1333+
NavigablePath fetchablePath,
1334+
FetchParent fetchParent,
1335+
TableGroup parentTableGroup,
1336+
String resultVariable,
1337+
FromClauseAccess fromClauseAccess,
1338+
DomainResultCreationState creationState) {
1339+
final SqlAstJoinType joinType;
13461340
if ( fetchParent instanceof EntityResultJoinedSubclassImpl
13471341
&& ( (EntityPersister) fetchParent.getReferencedModePart() ).findDeclaredAttributeMapping( getPartName() ) == null ) {
1348-
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
1349-
fetchablePath,
1350-
parentTableGroup,
1351-
resultVariable,
1352-
getJoinType( fetchablePath, parentTableGroup ),
1353-
true,
1354-
false,
1355-
creationState.getSqlAstCreationState()
1356-
);
1357-
if ( hasNotFoundAction() ) {
1358-
tableGroupJoin.setImplicit();
1359-
}
1360-
1361-
parentTableGroup.addTableGroupJoin( tableGroupJoin );
1362-
tableGroup = tableGroupJoin.getJoinedGroup();
1363-
fromClauseAccess.registerTableGroup( fetchablePath, tableGroup );
1342+
joinType = getJoinTypeForFetch( fetchablePath, parentTableGroup );
13641343
}
13651344
else {
1366-
tableGroup = fromClauseAccess.resolveTableGroup(
1367-
fetchablePath,
1368-
np -> {
1369-
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
1370-
fetchablePath,
1371-
parentTableGroup,
1372-
resultVariable,
1373-
getDefaultSqlAstJoinType( parentTableGroup ),
1374-
true,
1375-
false,
1376-
creationState.getSqlAstCreationState()
1377-
);
1378-
if ( hasNotFoundAction() ) {
1379-
tableGroupJoin.setImplicit();
1345+
joinType = null;
1346+
}
1347+
return fromClauseAccess.resolveTableGroup(
1348+
fetchablePath,
1349+
np -> {
1350+
// Try to reuse an existing join if possible,
1351+
// and note that we prefer reusing an inner over a left join,
1352+
// because a left join might stay uninitialized if unused
1353+
TableGroup leftJoined = null;
1354+
for ( TableGroupJoin tableGroupJoin : parentTableGroup.getTableGroupJoins() ) {
1355+
switch ( tableGroupJoin.getJoinType() ) {
1356+
case INNER:
1357+
// If this is an inner joins, it's fine if the paths match
1358+
// Since this inner join would filter the parent row anyway,
1359+
// it makes no sense to add another left join for this association
1360+
if ( tableGroupJoin.getNavigablePath().pathsMatch( np ) ) {
1361+
return tableGroupJoin.getJoinedGroup();
1362+
}
1363+
break;
1364+
case LEFT:
1365+
// For an existing left join on the other hand which is row preserving,
1366+
// it is important to check if the predicate has user defined bits in it
1367+
// and only if it doesn't, we can reuse the join
1368+
if ( tableGroupJoin.getNavigablePath().pathsMatch( np )
1369+
&& isSimpleJoinPredicate( tableGroupJoin.getPredicate() ) ) {
1370+
leftJoined = tableGroupJoin.getJoinedGroup();
1371+
}
13801372
}
1381-
parentTableGroup.addTableGroupJoin( tableGroupJoin );
1382-
return tableGroupJoin.getJoinedGroup();
13831373
}
1384-
);
1385-
}
1386-
return tableGroup;
1374+
1375+
if ( leftJoined != null ) {
1376+
return leftJoined;
1377+
}
1378+
1379+
final TableGroupJoin tableGroupJoin = createTableGroupJoin(
1380+
fetchablePath,
1381+
parentTableGroup,
1382+
resultVariable,
1383+
joinType,
1384+
true,
1385+
false,
1386+
creationState.getSqlAstCreationState()
1387+
);
1388+
parentTableGroup.addTableGroupJoin( tableGroupJoin );
1389+
return tableGroupJoin.getJoinedGroup();
1390+
}
1391+
);
13871392
}
13881393

13891394
private boolean isSelectByUniqueKey(ForeignKeyDescriptor.Nature side) {
@@ -1531,7 +1536,9 @@ else if ( parentTableGroup.getModelPart() instanceof CollectionPart ) {
15311536

15321537
@Override
15331538
public boolean isSimpleJoinPredicate(Predicate predicate) {
1534-
return foreignKeyDescriptor.isSimpleJoinPredicate( predicate );
1539+
// Since the table group is lazy, the initial predicate is null,
1540+
// but if we get null here, we can safely assume this will be a simple join predicate
1541+
return predicate == null || foreignKeyDescriptor.isSimpleJoinPredicate( predicate );
15351542
}
15361543

15371544
@Override
@@ -1555,7 +1562,18 @@ public TableGroupJoin createTableGroupJoin(
15551562
// This is vital for the map key property check that comes next
15561563
assert !( lhs instanceof PluralTableGroup );
15571564

1558-
final SqlAstJoinType joinType = requireNonNullElse( requestedJoinType, SqlAstJoinType.INNER );
1565+
final SqlAstJoinType joinType;
1566+
if ( requestedJoinType == null ) {
1567+
if ( fetched ) {
1568+
joinType = getDefaultSqlAstJoinType( lhs );
1569+
}
1570+
else {
1571+
joinType = SqlAstJoinType.INNER;
1572+
}
1573+
}
1574+
else {
1575+
joinType = requestedJoinType;
1576+
}
15591577

15601578
// If a parent is a collection part, there is no custom predicate and the join is INNER or LEFT
15611579
// we check if this attribute is the map key property to reuse the existing index table group
@@ -1800,13 +1818,13 @@ private void initializeIfNeeded(TableGroup lhs, SqlAstJoinType sqlAstJoinType, T
18001818
}
18011819
}
18021820

1803-
private SqlAstJoinType getJoinType(NavigablePath navigablePath, TableGroup tableGroup) {
1821+
private SqlAstJoinType getJoinTypeForFetch(NavigablePath navigablePath, TableGroup tableGroup) {
18041822
for ( TableGroupJoin tableGroupJoin : tableGroup.getTableGroupJoins() ) {
18051823
if ( tableGroupJoin.getNavigablePath().equals( navigablePath ) ) {
18061824
return tableGroupJoin.getJoinType();
18071825
}
18081826
}
1809-
return getDefaultSqlAstJoinType( tableGroup );
1827+
return null;
18101828
}
18111829

18121830
public TableGroup createTableGroupInternal(

hibernate-core/src/main/java/org/hibernate/query/sqm/sql/BaseSqmToSqlAstConverter.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3312,7 +3312,6 @@ private TableGroup createTableGroup(TableGroup parentTableGroup, SqmPath<?> join
33123312
false,
33133313
this
33143314
);
3315-
tableGroupJoin.setImplicit();
33163315
// Implicit joins in the ON clause of attribute joins need to be added as nested table group joins
33173316
// We don't have to do that for entity joins etc. as these do not have an inherent dependency on the lhs.
33183317
// We can just add the implicit join before the currently processing join
@@ -6987,7 +6986,7 @@ else if ( getLoadQueryInfluencers().hasEnabledFetchProfiles() ) {
69876986
fetchablePath,
69886987
lhs,
69896988
alias,
6990-
tableGroupJoinProducer.getDefaultSqlAstJoinType( lhs ),
6989+
null,
69916990
true,
69926991
false,
69936992
BaseSqmToSqlAstConverter.this

hibernate-core/src/main/java/org/hibernate/query/sqm/sql/internal/PluralValuedSimplePathInterpretation.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.hibernate.query.sqm.sql.internal;
88

99
import org.hibernate.NotYetImplementedFor6Exception;
10+
import org.hibernate.metamodel.mapping.CollectionPart;
1011
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
1112
import org.hibernate.spi.NavigablePath;
1213
import org.hibernate.query.sqm.sql.SqmToSqlAstConverter;
@@ -60,7 +61,7 @@ public DomainResult<T> createDomainResult(String resultVariable, DomainResultCre
6061
// This is only invoked when a plural attribute is a top level select, order by or group by item
6162
// in which case we have to produce results for the element
6263
return ( (PluralAttributeMapping) getExpressionType() ).getElementDescriptor().createDomainResult(
63-
getNavigablePath(),
64+
getNavigablePath().append( CollectionPart.Nature.ELEMENT.getName() ),
6465
getTableGroup(),
6566
resultVariable,
6667
creationState

hibernate-core/src/main/java/org/hibernate/sql/ast/tree/from/TableGroupJoin.java

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ public class TableGroupJoin implements TableJoin, DomainResultProducer {
2727
private SqlAstJoinType joinType;
2828
private Predicate predicate;
2929

30-
private boolean implicit;
31-
3230
public TableGroupJoin(
3331
NavigablePath navigablePath,
3432
SqlAstJoinType joinType,
@@ -93,12 +91,8 @@ public NavigablePath getNavigablePath() {
9391
return navigablePath;
9492
}
9593

96-
public void setImplicit(){
97-
this.implicit = true;
98-
}
99-
100-
public boolean isImplicit(){
101-
return implicit;
94+
public boolean isImplicit() {
95+
return !navigablePath.isAliased();
10296
}
10397

10498
@Override

0 commit comments

Comments
 (0)