HHH-13011 : Add option enabling/disabling use of an entity's mapped where-clause when loading collections of that entity

This commit is contained in:
Gail Badner 2018-10-15 23:13:07 -07:00 committed by Guillaume Smet
parent 19dd186d11
commit 4735c2d5aa
3 changed files with 85 additions and 25 deletions

View File

@ -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., <class name="..." ... where="..." .../>
// 2) from the collection mapping; e.g., <set name="..." ... where="..." .../>
// 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., <class name="..." ... where="..." .../>
// 2) from the collection mapping; e.g., <set name="..." ... where="..." .../>
// 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., <set name="..." ... where="..." .../>
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., <class name="..." ... where="..." .../>
// 2) from the many-to-many mapping; i.e <many-to-many ... where="...".../>
// 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., <class name="..." ... where="..." .../>
// 2) from the many-to-many mapping; i.e <many-to-many ... where="...".../>
// 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,

View File

@ -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 <code>@Where(clause="....")</code>
* or <code>&lt;entity ... where="..."&gt;</code>, is taken into account when loading one-to-many
* or many-to-many collections of that type of entity.
* <p/>
* This setting has no affect on collections of embeddable values containing an association to
* that type of entity.
* <p/>
* 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.
* <p/>
* `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.

View File

@ -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<Rating> getRatings();
// for many-to-many e.g., @ManyToMany @Where(clause="...") public Set<Rating> 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();