HHH-15719 Hint UniqueSematics.NONE for entity queries without collection join fetches
This commit is contained in:
parent
de9b82f994
commit
68324b9297
|
@ -18,6 +18,10 @@ import org.hibernate.engine.spi.EntityKey;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.engine.spi.SubselectFetch;
|
||||
import org.hibernate.graph.spi.AppliedGraph;
|
||||
import org.hibernate.graph.spi.AttributeNodeImplementor;
|
||||
import org.hibernate.graph.spi.GraphImplementor;
|
||||
import org.hibernate.graph.spi.SubGraphImplementor;
|
||||
import org.hibernate.internal.EmptyScrollableResults;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||
|
@ -86,6 +90,13 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
|||
|
||||
this.rowTransformer = determineRowTransformer( sqm, resultType, tupleMetadata, queryOptions );
|
||||
|
||||
final ListResultsConsumer.UniqueSemantic uniqueSemantic;
|
||||
if ( sqm.producesUniqueResults() && !containsCollectionFetches( queryOptions ) ) {
|
||||
uniqueSemantic = ListResultsConsumer.UniqueSemantic.NONE;
|
||||
}
|
||||
else {
|
||||
uniqueSemantic = ListResultsConsumer.UniqueSemantic.ALLOW;
|
||||
}
|
||||
this.listInterpreter = (unused, executionContext, sqmInterpretation, jdbcParameterBindings) -> {
|
||||
final SharedSessionContractImplementor session = executionContext.getSession();
|
||||
final JdbcSelect jdbcSelect = sqmInterpretation.getJdbcSelect();
|
||||
|
@ -122,7 +133,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
|||
}
|
||||
},
|
||||
rowTransformer,
|
||||
ListResultsConsumer.UniqueSemantic.ALLOW
|
||||
uniqueSemantic
|
||||
);
|
||||
}
|
||||
finally {
|
||||
|
@ -168,6 +179,25 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
|||
// `#performList` and `#performScroll`.
|
||||
}
|
||||
|
||||
private static boolean containsCollectionFetches(QueryOptions queryOptions) {
|
||||
final AppliedGraph appliedGraph = queryOptions.getAppliedGraph();
|
||||
return appliedGraph != null && appliedGraph.getGraph() != null && containsCollectionFetches( appliedGraph.getGraph() );
|
||||
}
|
||||
|
||||
private static boolean containsCollectionFetches(GraphImplementor<?> graph) {
|
||||
for ( AttributeNodeImplementor<?> attributeNodeImplementor : graph.getAttributeNodeImplementors() ) {
|
||||
if ( attributeNodeImplementor.getAttributeDescriptor().isCollection() ) {
|
||||
return true;
|
||||
}
|
||||
for ( SubGraphImplementor<?> subGraph : attributeNodeImplementor.getSubGraphMap().values() ) {
|
||||
if ( containsCollectionFetches( subGraph ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private RowTransformer<R> determineRowTransformer(
|
||||
SqmSelectStatement<?> sqm,
|
||||
|
|
|
@ -146,6 +146,41 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
|
|||
this.fromClause = fromClause;
|
||||
}
|
||||
|
||||
public boolean producesUniqueResults() {
|
||||
if ( fromClause.getRoots().size() != 1 ) {
|
||||
return false;
|
||||
}
|
||||
final SqmRoot<?> sqmRoot = fromClause.getRoots().get( 0 );
|
||||
if ( selectClause != null ) {
|
||||
final List<SqmSelection<?>> selections = selectClause.getSelections();
|
||||
if ( selections.size() != 1 || selections.get( 0 ).getSelectableNode() != sqmRoot ) {
|
||||
// If we select anything but the query root, let's be pessimistic about unique results
|
||||
return false;
|
||||
}
|
||||
}
|
||||
final List<SqmFrom<?, ?>> fromNodes = new ArrayList<>( sqmRoot.getSqmJoins().size() + 1 );
|
||||
fromNodes.add( sqmRoot );
|
||||
while ( !fromNodes.isEmpty() ) {
|
||||
final SqmFrom<?, ?> fromNode = fromNodes.remove( fromNodes.size() - 1 );
|
||||
for ( SqmJoin<?, ?> sqmJoin : fromNode.getSqmJoins() ) {
|
||||
if ( sqmJoin instanceof SqmAttributeJoin<?, ?> ) {
|
||||
final SqmAttributeJoin<?, ?> join = (SqmAttributeJoin<?, ?>) sqmJoin;
|
||||
if ( join.getAttribute().isCollection() ) {
|
||||
// Collections joins always alter cardinality
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// For now, consider all non-attribute joins as cardinality altering
|
||||
return false;
|
||||
}
|
||||
fromNodes.add( sqmJoin );
|
||||
}
|
||||
fromNodes.addAll( fromNode.getSqmTreats() );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean containsCollectionFetches() {
|
||||
final List<SqmFrom<?, ?>> fromNodes = new ArrayList<>( fromClause.getRoots() );
|
||||
while ( !fromNodes.isEmpty() ) {
|
||||
|
@ -159,6 +194,7 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
|
|||
}
|
||||
fromNodes.add( sqmJoin );
|
||||
}
|
||||
fromNodes.addAll( fromNode.getSqmTreats() );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -153,6 +153,20 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
|
|||
}
|
||||
}
|
||||
|
||||
public boolean producesUniqueResults() {
|
||||
return producesUniqueResults( getQueryPart() );
|
||||
}
|
||||
|
||||
private boolean producesUniqueResults(SqmQueryPart<?> queryPart) {
|
||||
if ( queryPart instanceof SqmQuerySpec<?> ) {
|
||||
return ( (SqmQuerySpec<?>) queryPart ).producesUniqueResults();
|
||||
}
|
||||
else {
|
||||
// For query groups we have to assume that duplicates are possible
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean containsCollectionFetches() {
|
||||
return containsCollectionFetches( getQueryPart() );
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue