diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java index c64d98d3ae..2c7ec3ee1f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java @@ -92,10 +92,12 @@ import org.hibernate.boot.spi.InFlightMetadataCollector; import org.hibernate.boot.spi.InFlightMetadataCollector.EntityTableXref; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.NaturalIdUniqueKeyBinder; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.FkSecondPass; import org.hibernate.cfg.SecondPass; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; +import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.engine.spi.FilterDefinition; import org.hibernate.id.PersistentIdentifierGenerator; @@ -104,6 +106,7 @@ import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.log.DeprecationLogger; import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.loader.PropertyPath; import org.hibernate.mapping.Any; import org.hibernate.mapping.Array; @@ -3460,18 +3463,25 @@ public class ModelBinder { final PersistentClass referencedEntityBinding = mappingDocument.getMetadataCollector() .getEntityBinding( elementSource.getReferencedEntityName() ); - // For a one-to-many association, there are 2 possible sources of "where" clauses that apply - // to the associated entity table: - // 1) from the associated entity mapping; i.e., - // 2) from the collection mapping; e.g., - // Collection#setWhere is used to set the "where" clause that applies to the collection table - // (which is the associated entity table for a one-to-many association). - collectionBinding.setWhere( - StringHelper.getNonEmptyOrConjunctionIfBothNonEmpty( - referencedEntityBinding.getWhere(), - getPluralAttributeSource().getWhere() - ) - ); + + if ( useEntityWhereClauseForCollections() ) { + // For a one-to-many association, there are 2 possible sources of "where" clauses that apply + // to the associated entity table: + // 1) from the associated entity mapping; i.e., + // 2) from the collection mapping; e.g., + // Collection#setWhere is used to set the "where" clause that applies to the collection table + // (which is the associated entity table for a one-to-many association). + collectionBinding.setWhere( + StringHelper.getNonEmptyOrConjunctionIfBothNonEmpty( + referencedEntityBinding.getWhere(), + getPluralAttributeSource().getWhere() + ) + ); + } + else { + // ignore entity's where clause + collectionBinding.setWhere( getPluralAttributeSource().getWhere() ); + } elementBinding.setReferencedEntityName( referencedEntityBinding.getEntityName() ); elementBinding.setAssociatedClass( referencedEntityBinding ); @@ -3531,18 +3541,26 @@ public class ModelBinder { // (which is the join table for a many-to-many association). // This "where" clause comes from the collection mapping; e.g., getCollectionBinding().setWhere( getPluralAttributeSource().getWhere() ); - // For a many-to-many association, there are 2 possible sources of "where" clauses that apply - // to the associated entity table (not the join table): - // 1) from the associated entity mapping; i.e., - // 2) from the many-to-many mapping; i.e - // Collection#setManytoManyWhere is used to set the "where" clause that applies to - // to the many-to-many associated entity table (not the join table). - getCollectionBinding().setManyToManyWhere( - StringHelper.getNonEmptyOrConjunctionIfBothNonEmpty( - referencedEntityBinding.getWhere(), - elementSource.getWhere() - ) - ); + + if ( useEntityWhereClauseForCollections() ) { + // For a many-to-many association, there are 2 possible sources of "where" clauses that apply + // to the associated entity table (not the join table): + // 1) from the associated entity mapping; i.e., + // 2) from the many-to-many mapping; i.e + // Collection#setManytoManyWhere is used to set the "where" clause that applies to + // to the many-to-many associated entity table (not the join table). + getCollectionBinding().setManyToManyWhere( + StringHelper.getNonEmptyOrConjunctionIfBothNonEmpty( + referencedEntityBinding.getWhere(), + elementSource.getWhere() + ) + ); + } + else { + // ignore entity's where clause + getCollectionBinding().setManyToManyWhere( elementSource.getWhere() ); + } + getCollectionBinding().setManyToManyOrdering( elementSource.getOrder() ); if ( !CollectionHelper.isEmpty( elementSource.getFilterSources() ) @@ -3624,6 +3642,18 @@ public class ModelBinder { } } + private boolean useEntityWhereClauseForCollections() { + return ConfigurationHelper.getBoolean( + AvailableSettings.USE_ENTITY_WHERE_CLAUSE_FOR_COLLECTIONS, + metadataBuildingContext + .getBuildingOptions() + .getServiceRegistry() + .getService( ConfigurationService.class ) + .getSettings(), + true + ); + } + private class PluralAttributeListSecondPass extends AbstractPluralAttributeSecondPass { public PluralAttributeListSecondPass( MappingDocument sourceDocument, diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index 4d2712bccf..e8634f160f 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -1480,6 +1480,22 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings { */ String CUSTOM_ENTITY_DIRTINESS_STRATEGY = "hibernate.entity_dirtiness_strategy"; + /** + * Controls whether an entity's "where" clause, mapped using @Where(clause="....") + * or <entity ... where="...">, is taken into account when loading one-to-many + * or many-to-many collections of that type of entity. + *

+ * This setting has no affect on collections of embeddable values containing an association to + * that type of entity. + *

+ * When `true` (the default), the entity's "where" clause will be taken into account when loading + * one-to-many or many-to-many collections of that type of entity. + *

+ * `false` indicates that the entity's "where" clause will be ignored when loading one-to-many or + * many-to-many collections of that type of entity. + */ + String USE_ENTITY_WHERE_CLAUSE_FOR_COLLECTIONS = "hibernate.use_entity_where_clause_for_collections"; + /** * Strategy for multi-tenancy. diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java index dbeeea5ba4..554c13cfc0 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java @@ -70,6 +70,7 @@ import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.cfg.AccessType; import org.hibernate.cfg.AnnotatedClassType; import org.hibernate.cfg.AnnotationBinder; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.cfg.BinderHelper; import org.hibernate.cfg.CollectionPropertyHolder; import org.hibernate.cfg.CollectionSecondPass; @@ -84,9 +85,11 @@ import org.hibernate.cfg.PropertyInferredData; import org.hibernate.cfg.PropertyPreloadedData; import org.hibernate.cfg.SecondPass; import org.hibernate.criterion.Junction; +import org.hibernate.engine.config.spi.ConfigurationService; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.StringHelper; +import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.mapping.Any; import org.hibernate.mapping.Backref; import org.hibernate.mapping.Collection; @@ -953,13 +956,24 @@ public abstract class CollectionBinder { } } + final boolean useEntityWhereClauseForCollections = ConfigurationHelper.getBoolean( + AvailableSettings.USE_ENTITY_WHERE_CLAUSE_FOR_COLLECTIONS, + buildingContext + .getBuildingOptions() + .getServiceRegistry() + .getService( ConfigurationService.class ) + .getSettings(), + true + ); + // There are 2 possible sources of "where" clauses that apply to the associated entity table: // 1) from the associated entity mapping; i.e., @Entity @Where(clause="...") + // (ignored if useEntityWhereClauseForCollections == false) // 2) from the collection mapping; // for one-to-many, e.g., @OneToMany @JoinColumn @Where(clause="...") public Set getRatings(); // for many-to-many e.g., @ManyToMany @Where(clause="...") public Set getRatings(); String whereOnClassClause = null; - if ( property.getElementClass() != null ) { + if ( useEntityWhereClauseForCollections && property.getElementClass() != null ) { Where whereOnClass = property.getElementClass().getAnnotation( Where.class ); if ( whereOnClass != null ) { whereOnClassClause = whereOnClass.clause();