HHH-17854 Avoid adding plural attribute restrictions multiple times
This commit is contained in:
parent
008090b60e
commit
bf807f2694
|
@ -688,6 +688,8 @@ public class LoaderSelectBuilder {
|
|||
SqlAstCreationState astCreationState) {
|
||||
final NavigablePath parentNavigablePath = tableGroup.getNavigablePath().getParent();
|
||||
if ( parentNavigablePath == null ) {
|
||||
// Only apply restrictions for root table groups,
|
||||
// because for table group joins the restriction is applied via PluralAttributeMappingImpl.createTableGroupJoin
|
||||
pluralAttributeMapping.applyBaseRestrictions(
|
||||
querySpec::applyPredicate,
|
||||
tableGroup,
|
||||
|
@ -705,31 +707,6 @@ public class LoaderSelectBuilder {
|
|||
astCreationState
|
||||
);
|
||||
}
|
||||
else {
|
||||
final TableGroup parentTableGroup = astCreationState.getFromClauseAccess().getTableGroup(
|
||||
parentNavigablePath );
|
||||
final TableGroupJoin pluralTableGroupJoin = parentTableGroup.findTableGroupJoin( tableGroup );
|
||||
assert pluralTableGroupJoin != null;
|
||||
|
||||
final TableGroupJoin joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( pluralTableGroupJoin );
|
||||
|
||||
pluralAttributeMapping.applyBaseRestrictions(
|
||||
joinForPredicate::applyPredicate,
|
||||
tableGroup,
|
||||
true,
|
||||
loadQueryInfluencers.getEnabledFilters(),
|
||||
null,
|
||||
astCreationState
|
||||
);
|
||||
pluralAttributeMapping.applyBaseManyToManyRestrictions(
|
||||
joinForPredicate::applyPredicate,
|
||||
tableGroup,
|
||||
true,
|
||||
loadQueryInfluencers.getEnabledFilters(),
|
||||
null,
|
||||
astCreationState
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyFiltering(
|
||||
|
|
|
@ -302,7 +302,7 @@ public abstract class AbstractCollectionPersister
|
|||
|
||||
if ( StringHelper.isNotEmpty( collectionBootDescriptor.getWhere() ) ) {
|
||||
hasWhere = true;
|
||||
sqlWhereString = "(" + collectionBootDescriptor.getWhere() + ") ";
|
||||
sqlWhereString = "(" + collectionBootDescriptor.getWhere() + ")";
|
||||
sqlWhereStringTemplate = Template.renderWhereStringTemplate(
|
||||
sqlWhereString,
|
||||
dialect,
|
||||
|
|
|
@ -485,7 +485,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
*/
|
||||
private Map<NavigablePath, Map.Entry<Integer, List<SqlSelection>>> trackedFetchSelectionsForGroup = Collections.emptyMap();
|
||||
|
||||
private final Map<NavigablePath, PredicateCollector> collectionFilterPredicates = new IdentityHashMap<>();
|
||||
private List<Map.Entry<OrderByFragment, TableGroup>> orderByFragments;
|
||||
|
||||
private final SqlAliasBaseManager sqlAliasBaseManager = new SqlAliasBaseManager();
|
||||
|
@ -2088,7 +2087,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
);
|
||||
orderByFragments = null;
|
||||
}
|
||||
applyCollectionFilterPredicates( sqlQuerySpec );
|
||||
}
|
||||
|
||||
// Look for treated SqmFrom registrations that have uses of the untreated SqmFrom.
|
||||
|
@ -2181,32 +2179,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
return getFromClauseAccess().getTableGroup( navigablePath );
|
||||
}
|
||||
|
||||
protected void applyCollectionFilterPredicates(QuerySpec sqlQuerySpec) {
|
||||
if ( CollectionHelper.isNotEmpty( collectionFilterPredicates ) ) {
|
||||
final FromClauseAccess fromClauseAccess = getFromClauseAccess();
|
||||
OUTER:
|
||||
for ( Map.Entry<NavigablePath, PredicateCollector> entry : collectionFilterPredicates.entrySet() ) {
|
||||
final TableGroup parentTableGroup = fromClauseAccess.findTableGroup( entry.getKey().getParent() );
|
||||
if ( parentTableGroup == null ) {
|
||||
// Since we only keep a single map, this could return null for collections of subqueries
|
||||
continue;
|
||||
}
|
||||
for ( TableGroupJoin tableGroupJoin : parentTableGroup.getTableGroupJoins() ) {
|
||||
if ( tableGroupJoin.getJoinedGroup().getNavigablePath() == entry.getKey() ) {
|
||||
tableGroupJoin.applyPredicate( entry.getValue().getPredicate() );
|
||||
continue OUTER;
|
||||
}
|
||||
}
|
||||
for ( TableGroupJoin tableGroupJoin : parentTableGroup.getNestedTableGroupJoins() ) {
|
||||
if ( tableGroupJoin.getJoinedGroup().getNavigablePath() == entry.getKey() ) {
|
||||
tableGroupJoin.applyPredicate( entry.getValue().getPredicate() );
|
||||
continue OUTER;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SelectClause visitSelectClause(SqmSelectClause selectClause) {
|
||||
currentClauseStack.push( Clause.SELECT );
|
||||
|
@ -8310,49 +8282,13 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
|
||||
@Override
|
||||
public ImmutableFetchList visitFetches(FetchParent fetchParent) {
|
||||
if ( fetchParent instanceof EagerCollectionFetch ) {
|
||||
if ( fetchParent instanceof EagerCollectionFetch && currentQuerySpec().isRoot() ) {
|
||||
final EagerCollectionFetch collectionFetch = (EagerCollectionFetch) fetchParent;
|
||||
final PluralAttributeMapping pluralAttributeMapping = collectionFetch.getFetchedMapping();
|
||||
final NavigablePath fetchablePath = collectionFetch.getNavigablePath();
|
||||
|
||||
final TableGroup tableGroup = getFromClauseIndex().getTableGroup( fetchablePath );
|
||||
|
||||
// Base restrictions have already been applied if this is an explicit fetch
|
||||
if ( getFromClauseIndex().findFetchedJoinByPath( fetchablePath ) == null ) {
|
||||
final Restrictable restrictable = pluralAttributeMapping
|
||||
.getCollectionDescriptor()
|
||||
.getCollectionType()
|
||||
.getAssociatedJoinable( getCreationContext().getSessionFactory() );
|
||||
restrictable.applyBaseRestrictions(
|
||||
(predicate) -> addCollectionFilterPredicate( tableGroup.getNavigablePath(), predicate ),
|
||||
tableGroup,
|
||||
true,
|
||||
getLoadQueryInfluencers().getEnabledFilters(),
|
||||
null,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
pluralAttributeMapping.applyBaseManyToManyRestrictions(
|
||||
(predicate) -> {
|
||||
final TableGroup parentTableGroup = getFromClauseIndex().getTableGroup( collectionFetch.getFetchParent().getNavigablePath() );
|
||||
final TableGroupJoin pluralTableGroupJoin = parentTableGroup.findTableGroupJoin( tableGroup );
|
||||
assert pluralTableGroupJoin != null;
|
||||
|
||||
final TableGroupJoin joinForPredicate = TableGroupJoinHelper.determineJoinForPredicateApply( pluralTableGroupJoin );
|
||||
joinForPredicate.applyPredicate( predicate );
|
||||
},
|
||||
tableGroup,
|
||||
true,
|
||||
getLoadQueryInfluencers().getEnabledFilters(),
|
||||
null,
|
||||
this
|
||||
);
|
||||
|
||||
if ( currentQuerySpec().isRoot() ) {
|
||||
assert tableGroup.getModelPart() == pluralAttributeMapping;
|
||||
applyOrdering( tableGroup, pluralAttributeMapping );
|
||||
}
|
||||
assert tableGroup.getModelPart() == pluralAttributeMapping;
|
||||
applyOrdering( tableGroup, pluralAttributeMapping );
|
||||
}
|
||||
final FetchableContainer referencedMappingContainer = fetchParent.getReferencedMappingContainer();
|
||||
final int keySize = referencedMappingContainer.getNumberOfKeyFetchables();
|
||||
|
@ -8430,16 +8366,6 @@ public abstract class BaseSqmToSqlAstConverter<T extends Statement> extends Base
|
|||
}
|
||||
}
|
||||
|
||||
private void addCollectionFilterPredicate(NavigablePath navigablePath, Predicate predicate) {
|
||||
final PredicateCollector existing = collectionFilterPredicates.get( navigablePath );
|
||||
if ( existing != null ) {
|
||||
existing.applyPredicate( predicate );
|
||||
}
|
||||
else {
|
||||
collectionFilterPredicates.put( navigablePath, new PredicateCollector( predicate ) );
|
||||
}
|
||||
}
|
||||
|
||||
private void applyOrdering(TableGroup tableGroup, PluralAttributeMapping pluralAttributeMapping) {
|
||||
if ( pluralAttributeMapping.getOrderByFragment() != null ) {
|
||||
applyOrdering( tableGroup, pluralAttributeMapping.getOrderByFragment() );
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.where;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.jpa.AvailableHints;
|
||||
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.Jira;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@DomainModel(annotatedClasses = { OneToManySQLRestrictionTests.Parent.class, OneToManySQLRestrictionTests.Child.class })
|
||||
@SessionFactory(useCollectingStatementInspector = true)
|
||||
@Jira("https://hibernate.atlassian.net/browse/HHH-17854")
|
||||
public class OneToManySQLRestrictionTests {
|
||||
|
||||
@Test
|
||||
public void testLoad(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
session.find( Parent.class, 1 );
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) )
|
||||
.isEqualTo(
|
||||
"select p1_0.id,cs1_0.parent_id,cs1_0.id,cs1_0.deleted_at " +
|
||||
"from Parent p1_0 " +
|
||||
"left join Child cs1_0 " +
|
||||
"on p1_0.id=cs1_0.parent_id " +
|
||||
"and (cs1_0.deleted_at IS NULL) " +
|
||||
"where p1_0.id=?"
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoad2(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
session.createQuery( "from Parent p join fetch p.childSet" ).getResultList();
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) )
|
||||
.isEqualTo(
|
||||
"select p1_0.id,cs1_0.parent_id,cs1_0.id,cs1_0.deleted_at " +
|
||||
"from Parent p1_0 " +
|
||||
"join Child cs1_0 " +
|
||||
"on p1_0.id=cs1_0.parent_id " +
|
||||
"and (cs1_0.deleted_at IS NULL)"
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoad3(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector statementInspector = scope.getCollectingStatementInspector();
|
||||
statementInspector.clear();
|
||||
|
||||
scope.inTransaction( (session) -> {
|
||||
RootGraphImplementor<Parent> entityGraph = session.createEntityGraph( Parent.class );
|
||||
entityGraph.addAttributeNode( "childSet" );
|
||||
session.createQuery( "from Parent p" )
|
||||
.setHint( AvailableHints.HINT_SPEC_LOAD_GRAPH, entityGraph )
|
||||
.getResultList();
|
||||
assertThat( statementInspector.getSqlQueries() ).hasSize( 1 );
|
||||
assertThat( statementInspector.getSqlQueries().get( 0 ) )
|
||||
.isEqualTo(
|
||||
"select p1_0.id,cs1_0.parent_id,cs1_0.id,cs1_0.deleted_at " +
|
||||
"from Parent p1_0 " +
|
||||
"left join Child cs1_0 " +
|
||||
"on p1_0.id=cs1_0.parent_id " +
|
||||
"and (cs1_0.deleted_at IS NULL)"
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "Parent")
|
||||
public static class Parent {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
@SQLRestriction("deleted_at IS NULL")
|
||||
@OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
|
||||
private Set<Child> childSet = new HashSet<>();
|
||||
}
|
||||
|
||||
@Entity(name = "Child")
|
||||
public static class Child {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
@ManyToOne
|
||||
private Parent parent;
|
||||
@Column(name = "deleted_at")
|
||||
private LocalDateTime deletedAt;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue