diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java index 1ce6152abe87..126b44605a98 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryDelegatingImpl.java @@ -198,6 +198,11 @@ public Collection getAutoEnabledFilters() { return delegate.getAutoEnabledFilters(); } + @Override + public boolean hasLoadByKeyFilter() { + return delegate.hasLoadByKeyFilter(); + } + @Override public boolean containsFetchProfileDefinition(String name) { return delegate.containsFetchProfileDefinition( name ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java index 88b3eae4d299..4d7c1591a65c 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java @@ -286,6 +286,9 @@ default JpaMetamodel getJpaMetamodel() { */ Collection getAutoEnabledFilters(); + @Incubating + boolean hasLoadByKeyFilter(); + /** * Obtain the {@link JdbcServices} service. */ diff --git a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java index 6dc2d168d8cc..f51983771b1d 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/SessionFactoryImpl.java @@ -182,6 +182,7 @@ public class SessionFactoryImpl implements SessionFactoryImplementor { private final transient Map filters; private final transient Collection autoEnabledFilters = new ArrayList<>(); + private transient boolean hasLoadByKeyFilter = false; private final transient JavaType tenantIdentifierJavaType; private final transient EventListenerGroups eventListenerGroups; @@ -246,6 +247,9 @@ public SessionFactoryImpl( if ( filter.isAutoEnabled() ) { autoEnabledFilters.add( filter ); } + if ( filter.isAppliedToLoadByKey() ) { + hasLoadByKeyFilter = true; + } } entityNameResolver = new CoordinatingEntityNameResolver( this, getInterceptor() ); @@ -962,6 +966,10 @@ public Collection getAutoEnabledFilters() { return autoEnabledFilters; } + public boolean hasLoadByKeyFilter() { + return hasLoadByKeyFilter; + } + @Override public Set getDefinedFilterNames() { return unmodifiableSet( filters.keySet() ); 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 396f98b972a2..0d7ca2a6ba5d 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 @@ -3678,10 +3678,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; + } +}