From 0b75638e673ec906e710b5d744b408b99b0f109f Mon Sep 17 00:00:00 2001 From: Rob Green Date: Tue, 28 Oct 2025 07:07:09 -0400 Subject: [PATCH 1/2] HHH-19566 allow checking for load by key filters with generic session find --- .../metamodel/mapping/ManagedMappingType.java | 4 + .../entity/AbstractEntityPersister.java | 3 +- .../annotations/ManyToOneFilterTest.java | 82 +++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/ManyToOneFilterTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ManagedMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ManagedMappingType.java index a2ff2c67a662..d04d2cf89c5a 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ManagedMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ManagedMappingType.java @@ -13,6 +13,7 @@ import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.collection.CollectionPersister; +import org.hibernate.persister.entity.SingleTableEntityPersister; import org.hibernate.sql.results.graph.FetchOptions; import org.hibernate.sql.results.graph.FetchableContainer; import org.hibernate.type.descriptor.java.JavaType; @@ -145,6 +146,9 @@ else if ( attributeMapping instanceof ToOneAttributeMapping toOneAttributeMappin } } } + if ( onlyApplyForLoadByKey && this instanceof SingleTableEntityPersister persister ) { + return persister.getFactory().hasLoadByKeyFilter(); + } return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index dee04d4063bd..354975dae38d 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -3668,10 +3668,9 @@ public boolean isAffectedByEnabledFilters( if ( filterHelper != null && loadQueryInfluencers.hasEnabledFilters() ) { return filterHelper.isAffectedBy( loadQueryInfluencers.getEnabledFilters(), onlyApplyForLoadByKeyFilters ) || isAffectedByEnabledFilters( new HashSet<>(), loadQueryInfluencers, onlyApplyForLoadByKeyFilters ); - } else { - return false; + return isAffectedByEnabledFilters( new HashSet<>(), loadQueryInfluencers, onlyApplyForLoadByKeyFilters ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/ManyToOneFilterTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/ManyToOneFilterTest.java new file mode 100644 index 000000000000..d8864f5e36cc --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/where/annotations/ManyToOneFilterTest.java @@ -0,0 +1,82 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.where.annotations; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import org.hibernate.EntityFilterException; +import org.hibernate.annotations.Filter; +import org.hibernate.annotations.FilterDef; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.junit.jupiter.api.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +@SessionFactory +@DomainModel(annotatedClasses = + {ManyToOneFilterTest.X.class, + ManyToOneFilterTest.Y.class}) +class ManyToOneFilterTest { + @Test void test(SessionFactoryScope scope) { + scope.inTransaction(session -> { + Y y = new Y(); + X x = new X(); + x.id = -1; + y.x = x; + session.persist(x); + session.persist(y); + }); + scope.inTransaction(session -> { + Y y = session.find(Y.class, 0L); + assertNotNull(y.x); + }); + try { + scope.inTransaction( session -> { + session.enableFilter( "filter" ).validate(); + var graph = session.createEntityGraph(Y.class); + Y y = session.find( graph, 0L ); + } ); + fail(); + } + catch (EntityFilterException efe) { + //required + } + try { + scope.inTransaction(session -> { + session.enableFilter( "filter" ).validate(); + Y y = session.find(Y.class, 0L); + }); + fail(); + } + catch (EntityFilterException efe) { + //required + } + } + + @Entity + @Table(name = "XX") + @FilterDef(name = "filter", applyToLoadByKey = true) + @Filter(name = "filter", condition = "id>0") + static class X { + @Id + long id; + } + @Entity + @Table(name = "YY") + static class Y { + @Id + long id; + String name; + @ManyToOne + @JoinColumn(name = "xx") + X x; + } +} From 18072ebde9c458ee4ebc76af44a0c82650fe6cf5 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Thu, 8 Jan 2026 17:57:23 +0100 Subject: [PATCH 2/2] HHH-19566 Respect association target entity/collection filters in nested isAffectedByEnabledFilters --- .../metamodel/mapping/ManagedMappingType.java | 12 +++++++---- .../AbstractCollectionPersister.java | 19 +++++++---------- .../entity/AbstractEntityPersister.java | 21 +++++++++++++------ 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ManagedMappingType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ManagedMappingType.java index d04d2cf89c5a..c57d9f2ed039 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ManagedMappingType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/mapping/ManagedMappingType.java @@ -13,7 +13,6 @@ import org.hibernate.internal.util.IndexedConsumer; import org.hibernate.metamodel.mapping.internal.ToOneAttributeMapping; import org.hibernate.persister.collection.CollectionPersister; -import org.hibernate.persister.entity.SingleTableEntityPersister; import org.hibernate.sql.results.graph.FetchOptions; import org.hibernate.sql.results.graph.FetchableContainer; import org.hibernate.type.descriptor.java.JavaType; @@ -125,6 +124,14 @@ default boolean isAffectedByEnabledFilters( if ( !visitedTypes.add( this ) ) { return false; } + + return areAttributesAffectedByEnabledFilters( visitedTypes, influencers, onlyApplyForLoadByKey ); + } + + default boolean areAttributesAffectedByEnabledFilters( + Set visitedTypes, + LoadQueryInfluencers influencers, + boolean onlyApplyForLoadByKey) { // we still need to verify collection fields to be eagerly loaded by join final AttributeMappingsList attributeMappings = getAttributeMappings(); for ( int i = 0; i < attributeMappings.size(); i++ ) { @@ -146,9 +153,6 @@ else if ( attributeMapping instanceof ToOneAttributeMapping toOneAttributeMappin } } } - if ( onlyApplyForLoadByKey && this instanceof SingleTableEntityPersister persister ) { - return persister.getFactory().hasLoadByKeyFilter(); - } return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java index 8f9d8968cccc..fe2ad0cee8ad 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/collection/AbstractCollectionPersister.java @@ -1454,7 +1454,8 @@ private boolean exists(Object key, Object indexOrElement, Type indexOrElementTyp @Override public Object getElementByIndex(Object key, Object index, SharedSessionContractImplementor session, Object owner) { final var influencers = session.getLoadQueryInfluencers(); - if ( isAffectedByFilters( new HashSet<>(), attributeMapping.getElementDescriptor(), influencers, true ) ) { + if ( influencers.hasEnabledFilters() + && isAffectedByFilters( new HashSet<>(), attributeMapping.getElementDescriptor(), influencers, true ) ) { return new CollectionElementLoaderByIndex( attributeMapping, influencers, factory ) .load( key, index, session ); } @@ -1537,7 +1538,7 @@ public boolean isAffectedByEnabledFilters(LoadQueryInfluencers influencers, bool final var enabledFilters = influencers.getEnabledFilters(); return filterHelper != null && filterHelper.isAffectedBy( enabledFilters ) || manyToManyFilterHelper != null && manyToManyFilterHelper.isAffectedBy( enabledFilters ) - || isKeyOrElementAffectedByFilters( new HashSet<>(), influencers, onlyApplyForLoadByKeyFilters); + || isKeyOrElementAffectedByFilters( new HashSet<>(), influencers, onlyApplyForLoadByKeyFilters ); } else { return false; @@ -1549,15 +1550,11 @@ public boolean isAffectedByEnabledFilters( Set visitedTypes, LoadQueryInfluencers influencers, boolean onlyApplyForLoadByKeyFilters) { - if ( influencers.hasEnabledFilters() ) { - final var enabledFilters = influencers.getEnabledFilters(); - return filterHelper != null && filterHelper.isAffectedBy( enabledFilters ) - || manyToManyFilterHelper != null && manyToManyFilterHelper.isAffectedBy( enabledFilters ) - || isKeyOrElementAffectedByFilters( visitedTypes, influencers, onlyApplyForLoadByKeyFilters); - } - else { - return false; - } + assert influencers.hasEnabledFilters(); + final var enabledFilters = influencers.getEnabledFilters(); + return filterHelper != null && filterHelper.isAffectedBy( enabledFilters ) + || manyToManyFilterHelper != null && manyToManyFilterHelper.isAffectedBy( enabledFilters ) + || isKeyOrElementAffectedByFilters( visitedTypes, influencers, onlyApplyForLoadByKeyFilters ); } private boolean isKeyOrElementAffectedByFilters( diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 354975dae38d..875e11e0ef80 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -3665,13 +3665,22 @@ public boolean isAffectedByEnabledFetchProfiles(LoadQueryInfluencers loadQueryIn public boolean isAffectedByEnabledFilters( LoadQueryInfluencers loadQueryInfluencers, boolean onlyApplyForLoadByKeyFilters) { - if ( filterHelper != null && loadQueryInfluencers.hasEnabledFilters() ) { - return filterHelper.isAffectedBy( loadQueryInfluencers.getEnabledFilters(), onlyApplyForLoadByKeyFilters ) - || isAffectedByEnabledFilters( new HashSet<>(), loadQueryInfluencers, onlyApplyForLoadByKeyFilters ); - } - else { - return isAffectedByEnabledFilters( new HashSet<>(), loadQueryInfluencers, onlyApplyForLoadByKeyFilters ); + return loadQueryInfluencers.hasEnabledFilters() + && isAffectedByEnabledFilters( new HashSet<>(), loadQueryInfluencers, onlyApplyForLoadByKeyFilters ); + } + + @Override + public boolean isAffectedByEnabledFilters( + Set visitedTypes, + LoadQueryInfluencers influencers, + boolean onlyApplyForLoadByKey) { + assert influencers.hasEnabledFilters(); + if ( !visitedTypes.add( this ) ) { + return false; } + return filterHelper != null + && filterHelper.isAffectedBy( influencers.getEnabledFilters(), onlyApplyForLoadByKey ) + || areAttributesAffectedByEnabledFilters( visitedTypes, influencers, onlyApplyForLoadByKey ); } /**