HHH-16569 Batch fetch leads to some collections to be wrongly initialized with empty due to wrong generated SQL

This commit is contained in:
Andrea Boriero 2023-05-16 17:17:47 +02:00
parent 611fa48c9b
commit 57c2c6e926
3 changed files with 26 additions and 25 deletions

View File

@ -12,7 +12,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.metamodel.mapping.EntityValuedModelPart;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
import org.hibernate.sql.ast.tree.from.TableGroup;
@ -29,7 +28,6 @@ import org.hibernate.sql.results.graph.entity.internal.EntityResultInitializer;
* be sub-select fetched later during initialization
*/
public class SubselectFetch {
private final EntityValuedModelPart entityModelPart;
private final QuerySpec loadingSqlAst;
private final TableGroup ownerTableGroup;
private final List<JdbcParameter> loadingJdbcParameters;
@ -37,13 +35,11 @@ public class SubselectFetch {
private final Set<EntityKey> resultingEntityKeys;
public SubselectFetch(
EntityValuedModelPart entityModelPart,
QuerySpec loadingSqlAst,
TableGroup ownerTableGroup,
List<JdbcParameter> loadingJdbcParameters,
JdbcParameterBindings loadingJdbcParameterBindings,
Set<EntityKey> resultingEntityKeys) {
this.entityModelPart = entityModelPart;
this.loadingSqlAst = loadingSqlAst;
this.ownerTableGroup = ownerTableGroup;
this.loadingJdbcParameters = loadingJdbcParameters;
@ -51,10 +47,6 @@ public class SubselectFetch {
this.resultingEntityKeys = resultingEntityKeys;
}
public EntityValuedModelPart getEntityModelPart() {
return entityModelPart;
}
public List<JdbcParameter> getLoadingJdbcParameters() {
// todo (6.0) : do not believe this is needed
// - see org.hibernate.loader.ast.internal.LoaderSelectBuilder.generateSelect(org.hibernate.engine.spi.SubselectFetch)
@ -94,7 +86,7 @@ public class SubselectFetch {
@Override
public String toString() {
return "SubselectFetch(" + entityModelPart.getEntityMappingType().getEntityName() + ")";
return "SubselectFetch(" + ownerTableGroup.getNavigablePath() + ")";
}
public static RegistrationHandler createRegistrationHandler(
@ -140,7 +132,6 @@ public class SubselectFetch {
public static class StandardRegistrationHandler implements RegistrationHandler {
private final BatchFetchQueue batchFetchQueue;
private final SelectStatement loadingSqlAst;
private final TableGroup ownerTableGroup;
private final List<JdbcParameter> loadingJdbcParameters;
private final JdbcParameterBindings loadingJdbcParameterBindings;
private final Map<NavigablePath, SubselectFetch> subselectFetches = new HashMap<>();
@ -153,7 +144,6 @@ public class SubselectFetch {
JdbcParameterBindings loadingJdbcParameterBindings) {
this.batchFetchQueue = batchFetchQueue;
this.loadingSqlAst = loadingSqlAst;
this.ownerTableGroup = ownerTableGroup;
this.loadingJdbcParameters = loadingJdbcParameters;
this.loadingJdbcParameterBindings = loadingJdbcParameterBindings;
}
@ -167,9 +157,10 @@ public class SubselectFetch {
final SubselectFetch subselectFetch = subselectFetches.computeIfAbsent(
entry.getEntityInitializer().getNavigablePath(),
navigablePath -> new SubselectFetch(
null,
loadingSqlAst.getQuerySpec(),
ownerTableGroup,
loadingSqlAst.getQuerySpec()
.getFromClause()
.findTableGroup( entry.getEntityInitializer().getNavigablePath() ),
loadingJdbcParameters,
loadingJdbcParameterBindings,
new HashSet<>()
@ -188,7 +179,7 @@ public class SubselectFetch {
final NavigablePath entityInitializerParent = entry.getEntityInitializer().getNavigablePath().getParent();
// We want to add only the collections of the loading entities
for ( DomainResult domainResult : loadingSqlAst.getDomainResultDescriptors() ) {
for ( DomainResult<?> domainResult : loadingSqlAst.getDomainResultDescriptors() ) {
if ( domainResult.getNavigablePath().equals( entityInitializerParent ) ) {
return true;
}

View File

@ -1076,7 +1076,6 @@ public class LoaderSelectBuilder {
// generate and apply the restriction
applySubSelectRestriction(
rootQuerySpec,
rootNavigablePath,
rootTableGroup,
subselect,
sqlAstCreationState
@ -1107,7 +1106,6 @@ public class LoaderSelectBuilder {
private void applySubSelectRestriction(
QuerySpec querySpec,
NavigablePath rootNavigablePath,
TableGroup rootTableGroup,
SubselectFetch subselect,
LoaderSqlAstCreationState sqlAstCreationState) {
@ -1115,7 +1113,6 @@ public class LoaderSelectBuilder {
final PluralAttributeMapping attributeMapping = (PluralAttributeMapping) loadable;
final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor();
final NavigablePath navigablePath = rootNavigablePath.append( attributeMapping.getAttributeName() );
final Expression fkExpression;
@ -1124,7 +1121,8 @@ public class LoaderSelectBuilder {
assert fkDescriptor instanceof SimpleForeignKeyDescriptor;
final SimpleForeignKeyDescriptor simpleFkDescriptor = (SimpleForeignKeyDescriptor) fkDescriptor;
final TableReference tableReference = rootTableGroup.resolveTableReference(
navigablePath,
null,
fkDescriptor,
simpleFkDescriptor.getContainingTableExpression()
);
fkExpression = sqlAstCreationState.getSqlExpressionResolver().resolveSqlExpression(
@ -1137,7 +1135,8 @@ public class LoaderSelectBuilder {
fkDescriptor.forEachSelectable(
(columnIndex, selection) -> {
final TableReference tableReference = rootTableGroup.resolveTableReference(
navigablePath,
null,
fkDescriptor,
selection.getContainingTableExpression()
);
columnReferences.add(
@ -1168,24 +1167,19 @@ public class LoaderSelectBuilder {
SubselectFetch subselect,
LoaderSqlAstCreationState creationState) {
final ForeignKeyDescriptor fkDescriptor = attributeMapping.getKeyDescriptor();
final QuerySpec subQuery = new QuerySpec( false );
final QuerySpec loadingSqlAst = subselect.getLoadingSqlAst();
// todo (6.0) : we need to find the owner's TableGroup in the `loadingSqlAst`
final TableGroup ownerTableGroup = subselect.getOwnerTableGroup();
// transfer the from-clause
loadingSqlAst.getFromClause().visitRoots( subQuery.getFromClause()::addRoot );
final SqlExpressionResolver sqlExpressionResolver = creationState.getSqlExpressionResolver();
final NavigablePath navigablePath = ownerTableGroup.getNavigablePath().append( attributeMapping.getAttributeName() );
fkDescriptor.visitTargetSelectables(
(valuesPosition, selection) -> {
// for each column, resolve a SqlSelection and add it to the sub-query select-clause
final TableReference tableReference = ownerTableGroup.resolveTableReference( navigablePath, selection.getContainingTableExpression() );
final TableReference tableReference = ownerTableGroup.resolveTableReference( null, fkDescriptor, selection.getContainingTableExpression() );
final Expression expression = sqlExpressionResolver.resolveSqlExpression(
tableReference,
selection

View File

@ -12,6 +12,8 @@ import java.util.function.Consumer;
import java.util.function.Function;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.CollectionPart;
import org.hibernate.spi.NavigablePath;
import org.hibernate.sql.ast.SqlAstWalker;
import org.hibernate.sql.ast.tree.SqlAstNode;
@ -224,6 +226,20 @@ public class FromClause implements SqlAstNode {
return null;
}
public TableGroup findTableGroup(NavigablePath navigablePath) {
return queryTableGroups(
tg -> {
if ( navigablePath.equals( tg.getNavigablePath() ) ) {
return tg;
}
if ( tg instanceof OneToManyTableGroup && navigablePath.getParent().equals( tg.getNavigablePath() ) ) {
return ( (OneToManyTableGroup) tg ).getTableGroup( CollectionPart.Nature.fromName( navigablePath.getLocalName() ) );
}
return null;
}
);
}
@Override
public void accept(SqlAstWalker sqlTreeWalker) {
sqlTreeWalker.visitFromClause( this );