HHH-15342 Inappropriate variation of HQL left join to SQL inner join

This commit is contained in:
Andrea Boriero 2022-06-22 15:27:58 +02:00
parent d553dea5a8
commit 985467bcba
4 changed files with 54 additions and 5 deletions

View File

@ -1196,9 +1196,33 @@ public class ToOneAttributeMapping
|| fetchParent.getNavigablePath() instanceof TreatedNavigablePath || fetchParent.getNavigablePath() instanceof TreatedNavigablePath
&& parentNavigablePath.equals( fetchParent.getNavigablePath().getRealParent() ); && parentNavigablePath.equals( fetchParent.getNavigablePath().getRealParent() );
/*
In case of @NotFound we are going to add fetch for the `fetchablePath` only if there is not already a `TableGroupJoin`.
if ( (hasNotFoundAction() e.g. given :
|| ( fetchTiming == FetchTiming.IMMEDIATE && selected )) ) { public static class EntityA {
...
@ManyToOne(fetch = FetchType.LAZY)
@NotFound(action = NotFoundAction.IGNORE)
private EntityB entityB;
}
@Entity(name = "EntityB")
public static class EntityB {
...
private String name;
}
and the HQL query :
`Select a From EntityA a Left Join a.entityB b Where ( b.name IS NOT NULL )`
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)
*/
if ( ( ( hasNotFoundAction() && doesNotExistATableGroupJoin( parentTableGroup, fetchablePath ) )
|| ( fetchTiming == FetchTiming.IMMEDIATE && selected ) ) ) {
final TableGroup tableGroup = determineTableGroup( final TableGroup tableGroup = determineTableGroup(
fetchablePath, fetchablePath,
fetchParent, fetchParent,
@ -1308,6 +1332,15 @@ public class ToOneAttributeMapping
); );
} }
private boolean doesNotExistATableGroupJoin(TableGroup parentTableGroup, NavigablePath fetchablePath) {
for ( TableGroupJoin tableGroupJoin : parentTableGroup.getTableGroupJoins() ) {
if ( tableGroupJoin.getNavigablePath().pathsMatch( fetchablePath ) ) {
return false;
}
}
return true;
}
private TableGroup determineTableGroup(NavigablePath fetchablePath, FetchParent fetchParent, TableGroup parentTableGroup, String resultVariable, FromClauseAccess fromClauseAccess, DomainResultCreationState creationState) { private TableGroup determineTableGroup(NavigablePath fetchablePath, FetchParent fetchParent, TableGroup parentTableGroup, String resultVariable, FromClauseAccess fromClauseAccess, DomainResultCreationState creationState) {
final TableGroup tableGroup; final TableGroup tableGroup;
if ( fetchParent instanceof EntityResultJoinedSubclassImpl if ( fetchParent instanceof EntityResultJoinedSubclassImpl
@ -1321,6 +1354,10 @@ public class ToOneAttributeMapping
false, false,
creationState.getSqlAstCreationState() creationState.getSqlAstCreationState()
); );
if ( hasNotFoundAction() ) {
tableGroupJoin.setImplicit();
}
parentTableGroup.addTableGroupJoin( tableGroupJoin ); parentTableGroup.addTableGroupJoin( tableGroupJoin );
tableGroup = tableGroupJoin.getJoinedGroup(); tableGroup = tableGroupJoin.getJoinedGroup();
fromClauseAccess.registerTableGroup( fetchablePath, tableGroup ); fromClauseAccess.registerTableGroup( fetchablePath, tableGroup );
@ -1338,6 +1375,9 @@ public class ToOneAttributeMapping
false, false,
creationState.getSqlAstCreationState() creationState.getSqlAstCreationState()
); );
if ( hasNotFoundAction() ) {
tableGroupJoin.setImplicit();
}
parentTableGroup.addTableGroupJoin( tableGroupJoin ); parentTableGroup.addTableGroupJoin( tableGroupJoin );
return tableGroupJoin.getJoinedGroup(); return tableGroupJoin.getJoinedGroup();
} }

View File

@ -3209,7 +3209,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
final NavigablePath parentParentPath = parentPath.getParentPath().getNavigablePath(); final NavigablePath parentParentPath = parentPath.getParentPath().getNavigablePath();
final TableGroup parentParentTableGroup = fromClauseIndex.findTableGroup( parentParentPath ); final TableGroup parentParentTableGroup = fromClauseIndex.findTableGroup( parentParentPath );
parentParentTableGroup.visitTableGroupJoins( (join) -> { parentParentTableGroup.visitTableGroupJoins( (join) -> {
if ( join.getNavigablePath().equals( parentPath.getNavigablePath() ) ) { if ( join.getNavigablePath().equals( parentPath.getNavigablePath() ) && join.isImplicit() ) {
join.setJoinType( SqlAstJoinType.INNER ); join.setJoinType( SqlAstJoinType.INNER );
} }
} ); } );
@ -3312,6 +3312,7 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
false, false,
this this
); );
tableGroupJoin.setImplicit();
// Implicit joins in the ON clause of attribute joins need to be added as nested table group joins // Implicit joins in the ON clause of attribute joins need to be added as nested table group joins
// We don't have to do that for entity joins etc. as these do not have an inherent dependency on the lhs. // We don't have to do that for entity joins etc. as these do not have an inherent dependency on the lhs.
// We can just add the implicit join before the currently processing join // We can just add the implicit join before the currently processing join

View File

@ -27,6 +27,8 @@ public class TableGroupJoin implements TableJoin, DomainResultProducer {
private SqlAstJoinType joinType; private SqlAstJoinType joinType;
private Predicate predicate; private Predicate predicate;
private boolean implicit;
public TableGroupJoin( public TableGroupJoin(
NavigablePath navigablePath, NavigablePath navigablePath,
SqlAstJoinType joinType, SqlAstJoinType joinType,
@ -91,6 +93,14 @@ public class TableGroupJoin implements TableJoin, DomainResultProducer {
return navigablePath; return navigablePath;
} }
public void setImplicit(){
this.implicit = true;
}
public boolean isImplicit(){
return implicit;
}
@Override @Override
public DomainResult createDomainResult( public DomainResult createDomainResult(
String resultVariable, String resultVariable,

View File

@ -36,8 +36,6 @@ public class EntityFetchSelectImpl extends AbstractNonJoinedEntityFetch {
@SuppressWarnings("unused") DomainResultCreationState creationState) { @SuppressWarnings("unused") DomainResultCreationState creationState) {
super( navigablePath, fetchedAttribute, fetchParent ); super( navigablePath, fetchedAttribute, fetchParent );
assert fetchedAttribute.getNotFoundAction() == null;
this.keyResult = keyResult; this.keyResult = keyResult;
this.selectByUniqueKey = selectByUniqueKey; this.selectByUniqueKey = selectByUniqueKey;
} }