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.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||||
import org.hibernate.engine.spi.SubselectFetch;
|
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.EmptyScrollableResults;
|
||||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||||
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
||||||
|
@ -86,6 +90,13 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
||||||
|
|
||||||
this.rowTransformer = determineRowTransformer( sqm, resultType, tupleMetadata, queryOptions );
|
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) -> {
|
this.listInterpreter = (unused, executionContext, sqmInterpretation, jdbcParameterBindings) -> {
|
||||||
final SharedSessionContractImplementor session = executionContext.getSession();
|
final SharedSessionContractImplementor session = executionContext.getSession();
|
||||||
final JdbcSelect jdbcSelect = sqmInterpretation.getJdbcSelect();
|
final JdbcSelect jdbcSelect = sqmInterpretation.getJdbcSelect();
|
||||||
|
@ -122,7 +133,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rowTransformer,
|
rowTransformer,
|
||||||
ListResultsConsumer.UniqueSemantic.ALLOW
|
uniqueSemantic
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
@ -168,6 +179,25 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
||||||
// `#performList` and `#performScroll`.
|
// `#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")
|
@SuppressWarnings("unchecked")
|
||||||
private RowTransformer<R> determineRowTransformer(
|
private RowTransformer<R> determineRowTransformer(
|
||||||
SqmSelectStatement<?> sqm,
|
SqmSelectStatement<?> sqm,
|
||||||
|
|
|
@ -146,6 +146,41 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
|
||||||
this.fromClause = fromClause;
|
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() {
|
public boolean containsCollectionFetches() {
|
||||||
final List<SqmFrom<?, ?>> fromNodes = new ArrayList<>( fromClause.getRoots() );
|
final List<SqmFrom<?, ?>> fromNodes = new ArrayList<>( fromClause.getRoots() );
|
||||||
while ( !fromNodes.isEmpty() ) {
|
while ( !fromNodes.isEmpty() ) {
|
||||||
|
@ -159,6 +194,7 @@ public class SqmQuerySpec<T> extends SqmQueryPart<T>
|
||||||
}
|
}
|
||||||
fromNodes.add( sqmJoin );
|
fromNodes.add( sqmJoin );
|
||||||
}
|
}
|
||||||
|
fromNodes.addAll( fromNode.getSqmTreats() );
|
||||||
}
|
}
|
||||||
return false;
|
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() {
|
public boolean containsCollectionFetches() {
|
||||||
return containsCollectionFetches( getQueryPart() );
|
return containsCollectionFetches( getQueryPart() );
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue