HHH-14968 - Support for auto-enabled filters
This commit is contained in:
parent
a5e276571c
commit
527beb0bdb
|
@ -508,7 +508,7 @@ include::{extrasdir}/pc-filter-persistence-example.sql[]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
By default, without explicitly enabling the filter, Hibernate is going to fetch all `Account` entities.
|
By default, without enabling the filter, Hibernate is going to fetch all `Account` entities.
|
||||||
|
|
||||||
[[pc-no-filter-entity-query-example]]
|
[[pc-no-filter-entity-query-example]]
|
||||||
.Query entities mapped without activating the `@Filter`
|
.Query entities mapped without activating the `@Filter`
|
||||||
|
@ -526,6 +526,8 @@ include::{extrasdir}/pc-no-filter-entity-query-example.sql[]
|
||||||
|
|
||||||
If the filter is enabled and the filter parameter value is provided,
|
If the filter is enabled and the filter parameter value is provided,
|
||||||
then Hibernate is going to apply the filtering criteria to the associated `Account` entities.
|
then Hibernate is going to apply the filtering criteria to the associated `Account` entities.
|
||||||
|
The filter can be enabled explicitly on the session or by specifying
|
||||||
|
that it will be enabled by default directly on its `@FilterDef`.
|
||||||
|
|
||||||
[[pc-filter-entity-query-example]]
|
[[pc-filter-entity-query-example]]
|
||||||
.Query entities mapped with `@Filter`
|
.Query entities mapped with `@Filter`
|
||||||
|
@ -534,6 +536,10 @@ then Hibernate is going to apply the filtering criteria to the associated `Accou
|
||||||
----
|
----
|
||||||
include::{example-dir-pc}/FilterTest.java[tags=pc-filter-entity-query-example]
|
include::{example-dir-pc}/FilterTest.java[tags=pc-filter-entity-query-example]
|
||||||
----
|
----
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{example-dir-pc}/FilterTest.java[tags=pc-filter-auto-enabled-Account-example]
|
||||||
|
----
|
||||||
|
|
||||||
[source, SQL, indent=0]
|
[source, SQL, indent=0]
|
||||||
----
|
----
|
||||||
|
@ -541,6 +547,15 @@ include::{extrasdir}/pc-filter-entity-query-example.sql[]
|
||||||
----
|
----
|
||||||
====
|
====
|
||||||
|
|
||||||
|
A parameter's value can be explicitly set on the filter itself, or can be
|
||||||
|
resolved by using a custom `Supplier`. The resolver must implement
|
||||||
|
the interface `java.util.function.Supplier` and must be defined as a managed bean.
|
||||||
|
|
||||||
|
[source, JAVA, indent=0]
|
||||||
|
----
|
||||||
|
include::{example-dir-pc}/FilterTest.java[tags=pc-filter-resolver-Account-example]
|
||||||
|
----
|
||||||
|
|
||||||
[IMPORTANT]
|
[IMPORTANT]
|
||||||
====
|
====
|
||||||
Filters apply to entity queries, but not to direct fetching.
|
Filters apply to entity queries, but not to direct fetching.
|
||||||
|
@ -562,7 +577,8 @@ include::{extrasdir}/pc-filter-entity-example.sql[]
|
||||||
As you can see from the example above, contrary to an entity query, the filter does not prevent the entity from being loaded.
|
As you can see from the example above, contrary to an entity query, the filter does not prevent the entity from being loaded.
|
||||||
====
|
====
|
||||||
|
|
||||||
Just like with entity queries, collections can be filtered as well, but only if the filter is explicitly enabled on the currently running Hibernate `Session`.
|
Just like with entity queries, collections can be filtered as well, but only if the filter is enabled on the currently running Hibernate `Session`,
|
||||||
|
either if the filter is enabled explicitly or by setting `autoEnabled` to `true`.
|
||||||
|
|
||||||
[[pc-no-filter-collection-query-example]]
|
[[pc-no-filter-collection-query-example]]
|
||||||
.Traversing collections without activating the `@Filter`
|
.Traversing collections without activating the `@Filter`
|
||||||
|
|
|
@ -87,4 +87,12 @@ public interface Filter {
|
||||||
* @throws HibernateException If the state is not currently valid.
|
* @throws HibernateException If the state is not currently valid.
|
||||||
*/
|
*/
|
||||||
void validate() throws HibernateException;
|
void validate() throws HibernateException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the associated {@link FilterDefinition autoEnabled} of this
|
||||||
|
* named filter.
|
||||||
|
*
|
||||||
|
* @return The flag value
|
||||||
|
*/
|
||||||
|
boolean isAutoEnabled();
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,4 +90,9 @@ public @interface FilterDef {
|
||||||
* The names and types of the parameters of the filter.
|
* The names and types of the parameters of the filter.
|
||||||
*/
|
*/
|
||||||
ParamDef[] parameters() default {};
|
ParamDef[] parameters() default {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The flag used to auto-enable the filter on the session.
|
||||||
|
*/
|
||||||
|
boolean autoEnabled() default false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.annotations;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||||
|
|
||||||
|
@ -52,4 +53,14 @@ public @interface ParamDef {
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
Class<?> type();
|
Class<?> type();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The resolver to use when retrieving the parameter value.
|
||||||
|
* <p>
|
||||||
|
* The parameter value can either be defined using the {@link org.hibernate.Filter setParameter}
|
||||||
|
* method or by providing a resolver, that will be executed to retrieve the value.
|
||||||
|
* <p>
|
||||||
|
* The supplied Class must implement {@link Supplier}
|
||||||
|
*/
|
||||||
|
Class<? extends Supplier> resolver() default Supplier.class;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.binder.internal;
|
package org.hibernate.binder.internal;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.annotations.TenantId;
|
import org.hibernate.annotations.TenantId;
|
||||||
import org.hibernate.binder.AttributeBinder;
|
import org.hibernate.binder.AttributeBinder;
|
||||||
|
@ -54,7 +57,9 @@ public class TenantIdBinder implements AttributeBinder<TenantId> {
|
||||||
new FilterDefinition(
|
new FilterDefinition(
|
||||||
FILTER_NAME,
|
FILTER_NAME,
|
||||||
"",
|
"",
|
||||||
singletonMap( PARAMETER_NAME, tenantIdType )
|
singletonMap( PARAMETER_NAME, tenantIdType ),
|
||||||
|
Collections.emptyMap(),
|
||||||
|
true
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.lang.reflect.ParameterizedType;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static java.util.Collections.emptyMap;
|
import static java.util.Collections.emptyMap;
|
||||||
import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation;
|
import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation;
|
||||||
|
@ -57,12 +58,16 @@ class FilterDefBinder {
|
||||||
if ( context.getMetadataCollector().getFilterDefinition( name ) != null ) {
|
if ( context.getMetadataCollector().getFilterDefinition( name ) != null ) {
|
||||||
throw new AnnotationException( "Multiple '@FilterDef' annotations define a filter named '" + name + "'" );
|
throw new AnnotationException( "Multiple '@FilterDef' annotations define a filter named '" + name + "'" );
|
||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, JdbcMapping> explicitParamJaMappings;
|
final Map<String, JdbcMapping> explicitParamJaMappings;
|
||||||
|
final Map<String, ManagedBean<? extends Supplier>> parameterResolvers;
|
||||||
if ( filterDef.parameters().length == 0 ) {
|
if ( filterDef.parameters().length == 0 ) {
|
||||||
explicitParamJaMappings = emptyMap();
|
explicitParamJaMappings = emptyMap();
|
||||||
|
parameterResolvers = emptyMap();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
explicitParamJaMappings = new HashMap<>();
|
explicitParamJaMappings = new HashMap<>();
|
||||||
|
parameterResolvers = new HashMap<>();
|
||||||
for ( ParamDef paramDef : filterDef.parameters() ) {
|
for ( ParamDef paramDef : filterDef.parameters() ) {
|
||||||
final JdbcMapping jdbcMapping = resolveFilterParamType( paramDef.type(), context );
|
final JdbcMapping jdbcMapping = resolveFilterParamType( paramDef.type(), context );
|
||||||
if ( jdbcMapping == null ) {
|
if ( jdbcMapping == null ) {
|
||||||
|
@ -76,14 +81,31 @@ class FilterDefBinder {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
explicitParamJaMappings.put( paramDef.name(), jdbcMapping );
|
explicitParamJaMappings.put( paramDef.name(), jdbcMapping );
|
||||||
|
|
||||||
|
if ( paramDef.resolver() != Supplier.class ) {
|
||||||
|
parameterResolvers.put( paramDef.name(), resolveParamResolver( paramDef, context ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final FilterDefinition filterDefinition =
|
}
|
||||||
new FilterDefinition( name, filterDef.defaultCondition(), explicitParamJaMappings );
|
final FilterDefinition filterDefinition = new FilterDefinition(
|
||||||
|
name,
|
||||||
|
filterDef.defaultCondition(),
|
||||||
|
explicitParamJaMappings,
|
||||||
|
parameterResolvers,
|
||||||
|
filterDef.autoEnabled()
|
||||||
|
);
|
||||||
LOG.debugf( "Binding filter definition: %s", filterDefinition.getFilterName() );
|
LOG.debugf( "Binding filter definition: %s", filterDefinition.getFilterName() );
|
||||||
context.getMetadataCollector().addFilterDefinition( filterDefinition );
|
context.getMetadataCollector().addFilterDefinition( filterDefinition );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ManagedBean<? extends Supplier> resolveParamResolver(ParamDef paramDef, MetadataBuildingContext context) {
|
||||||
|
Class<? extends Supplier> clazz = paramDef.resolver();
|
||||||
|
assert clazz != Supplier.class;
|
||||||
|
|
||||||
|
final ManagedBeanRegistry beanRegistry = context.getBootstrapContext().getServiceRegistry().getService( ManagedBeanRegistry.class );
|
||||||
|
return beanRegistry.getBean( clazz, context.getBootstrapContext().getCustomTypeProducer() );
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static JdbcMapping resolveFilterParamType(Class<?> type, MetadataBuildingContext context) {
|
private static JdbcMapping resolveFilterParamType(Class<?> type, MetadataBuildingContext context) {
|
||||||
if ( UserType.class.isAssignableFrom( type ) ) {
|
if ( UserType.class.isAssignableFrom( type ) ) {
|
||||||
|
|
|
@ -7,11 +7,14 @@
|
||||||
package org.hibernate.engine.spi;
|
package org.hibernate.engine.spi;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
import org.hibernate.resource.beans.spi.ManagedBean;
|
||||||
|
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
|
@ -32,6 +35,8 @@ public class FilterDefinition implements Serializable {
|
||||||
private final String filterName;
|
private final String filterName;
|
||||||
private final String defaultFilterCondition;
|
private final String defaultFilterCondition;
|
||||||
private final Map<String, JdbcMapping> explicitParamJaMappings = new HashMap<>();
|
private final Map<String, JdbcMapping> explicitParamJaMappings = new HashMap<>();
|
||||||
|
private final Map<String, ManagedBean<? extends Supplier>> parameterResolverMap = new HashMap<>();
|
||||||
|
private final boolean autoEnabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a new FilterDefinition instance.
|
* Construct a new FilterDefinition instance.
|
||||||
|
@ -39,11 +44,20 @@ public class FilterDefinition implements Serializable {
|
||||||
* @param name The name of the filter for which this configuration is in effect.
|
* @param name The name of the filter for which this configuration is in effect.
|
||||||
*/
|
*/
|
||||||
public FilterDefinition(String name, String defaultCondition, @Nullable Map<String, JdbcMapping> explicitParamJaMappings) {
|
public FilterDefinition(String name, String defaultCondition, @Nullable Map<String, JdbcMapping> explicitParamJaMappings) {
|
||||||
|
this( name, defaultCondition, explicitParamJaMappings, Collections.emptyMap(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FilterDefinition(String name, String defaultCondition, @Nullable Map<String, JdbcMapping> explicitParamJaMappings,
|
||||||
|
Map<String, ManagedBean<? extends Supplier>> parameterResolverMap, boolean autoEnabled) {
|
||||||
this.filterName = name;
|
this.filterName = name;
|
||||||
this.defaultFilterCondition = defaultCondition;
|
this.defaultFilterCondition = defaultCondition;
|
||||||
if ( explicitParamJaMappings != null ) {
|
if ( explicitParamJaMappings != null ) {
|
||||||
this.explicitParamJaMappings.putAll( explicitParamJaMappings );
|
this.explicitParamJaMappings.putAll( explicitParamJaMappings );
|
||||||
}
|
}
|
||||||
|
if ( parameterResolverMap != null ) {
|
||||||
|
this.parameterResolverMap.putAll( parameterResolverMap );
|
||||||
|
}
|
||||||
|
this.autoEnabled = autoEnabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,6 +91,11 @@ public class FilterDefinition implements Serializable {
|
||||||
return explicitParamJaMappings.get( parameterName );
|
return explicitParamJaMappings.get( parameterName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable Supplier getParameterResolver(String parameterName) {
|
||||||
|
final ManagedBean<? extends Supplier> resolver = parameterResolverMap.get( parameterName );
|
||||||
|
return resolver == null ? null : resolver.getBeanInstance();
|
||||||
|
}
|
||||||
|
|
||||||
public String getDefaultFilterCondition() {
|
public String getDefaultFilterCondition() {
|
||||||
return defaultFilterCondition;
|
return defaultFilterCondition;
|
||||||
}
|
}
|
||||||
|
@ -91,4 +110,13 @@ public class FilterDefinition implements Serializable {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a flag that defines if the filter should be enabled by default.
|
||||||
|
*
|
||||||
|
* @return The flag value.
|
||||||
|
*/
|
||||||
|
public boolean isAutoEnabled() {
|
||||||
|
return autoEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,13 @@ public class LoadQueryInfluencers implements Serializable {
|
||||||
this.sessionFactory = sessionFactory;
|
this.sessionFactory = sessionFactory;
|
||||||
batchSize = options.getDefaultBatchFetchSize();
|
batchSize = options.getDefaultBatchFetchSize();
|
||||||
subselectFetchEnabled = options.isSubselectFetchEnabled();
|
subselectFetchEnabled = options.isSubselectFetchEnabled();
|
||||||
|
for (FilterDefinition filterDefinition : sessionFactory.getAutoEnabledFilters()) {
|
||||||
|
FilterImpl filter = new FilterImpl( filterDefinition );
|
||||||
|
if ( enabledFilters == null ) {
|
||||||
|
this.enabledFilters = new HashMap<>();
|
||||||
|
}
|
||||||
|
enabledFilters.put( filterDefinition.getFilterName(), filter );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EffectiveEntityGraph applyEntityGraph(@Nullable RootGraphImplementor<?> rootGraph, @Nullable GraphSemantic graphSemantic) {
|
public EffectiveEntityGraph applyEntityGraph(@Nullable RootGraphImplementor<?> rootGraph, @Nullable GraphSemantic graphSemantic) {
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
package org.hibernate.engine.spi;
|
package org.hibernate.engine.spi;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -170,6 +171,11 @@ public class SessionFactoryDelegatingImpl implements SessionFactoryImplementor,
|
||||||
return delegate.getFilterDefinition( filterName );
|
return delegate.getFilterDefinition( filterName );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<FilterDefinition> getAutoEnabledFilters() {
|
||||||
|
return delegate.getAutoEnabledFilters();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsFetchProfileDefinition(String name) {
|
public boolean containsFetchProfileDefinition(String name) {
|
||||||
return delegate.containsFetchProfileDefinition( name );
|
return delegate.containsFetchProfileDefinition( name );
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
package org.hibernate.engine.spi;
|
package org.hibernate.engine.spi;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.hibernate.CustomEntityDirtinessStrategy;
|
import org.hibernate.CustomEntityDirtinessStrategy;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
|
@ -164,6 +165,8 @@ public interface SessionFactoryImplementor
|
||||||
|
|
||||||
FilterDefinition getFilterDefinition(String filterName);
|
FilterDefinition getFilterDefinition(String filterName);
|
||||||
|
|
||||||
|
Collection<FilterDefinition> getAutoEnabledFilters();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -248,6 +248,14 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final void setupAutoEnabledFilters(SessionFactoryImplementor factory, LoadQueryInfluencers loadQueryInfluencers) {
|
||||||
|
factory.getDefinedFilterNames()
|
||||||
|
.stream()
|
||||||
|
.map( filterName -> factory.getFilterDefinition( filterName ) )
|
||||||
|
.filter( filterDefinition -> filterDefinition.isAutoEnabled() )
|
||||||
|
.forEach( filterDefinition -> loadQueryInfluencers.enableFilter( filterDefinition.getFilterName() ) );
|
||||||
|
}
|
||||||
|
|
||||||
private void logInconsistentOptions(SharedSessionCreationOptions sharedOptions) {
|
private void logInconsistentOptions(SharedSessionCreationOptions sharedOptions) {
|
||||||
if ( sharedOptions.shouldAutoJoinTransactions() ) {
|
if ( sharedOptions.shouldAutoJoinTransactions() ) {
|
||||||
log.debug(
|
log.debug(
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.hibernate.Filter;
|
import org.hibernate.Filter;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
|
@ -30,6 +31,7 @@ public class FilterImpl implements Filter, Serializable {
|
||||||
private transient FilterDefinition definition;
|
private transient FilterDefinition definition;
|
||||||
private final String filterName;
|
private final String filterName;
|
||||||
private final Map<String,Object> parameters = new HashMap<>();
|
private final Map<String,Object> parameters = new HashMap<>();
|
||||||
|
private final boolean autoEnabled;
|
||||||
|
|
||||||
void afterDeserialize(SessionFactoryImplementor factory) {
|
void afterDeserialize(SessionFactoryImplementor factory) {
|
||||||
definition = factory.getFilterDefinition( filterName );
|
definition = factory.getFilterDefinition( filterName );
|
||||||
|
@ -44,6 +46,7 @@ public class FilterImpl implements Filter, Serializable {
|
||||||
public FilterImpl(FilterDefinition configuration) {
|
public FilterImpl(FilterDefinition configuration) {
|
||||||
this.definition = configuration;
|
this.definition = configuration;
|
||||||
filterName = definition.getFilterName();
|
filterName = definition.getFilterName();
|
||||||
|
this.autoEnabled = definition.isAutoEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FilterDefinition getFilterDefinition() {
|
public FilterDefinition getFilterDefinition() {
|
||||||
|
@ -59,6 +62,15 @@ public class FilterImpl implements Filter, Serializable {
|
||||||
return definition.getFilterName();
|
return definition.getFilterName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a flag that defines if the filter should be enabled by default.
|
||||||
|
*
|
||||||
|
* @return The flag value.
|
||||||
|
*/
|
||||||
|
public boolean isAutoEnabled() {
|
||||||
|
return autoEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String,?> getParameters() {
|
public Map<String,?> getParameters() {
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
@ -136,6 +148,10 @@ public class FilterImpl implements Filter, Serializable {
|
||||||
return parameters.get( name );
|
return parameters.get( name );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Supplier getParameterResolver(String name) {
|
||||||
|
return definition.getParameterResolver(name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform validation of the filter state. This is used to verify the
|
* Perform validation of the filter state. This is used to verify the
|
||||||
* state of the filter after its enablement and before its use.
|
* state of the filter after its enablement and before its use.
|
||||||
|
@ -144,12 +160,13 @@ public class FilterImpl implements Filter, Serializable {
|
||||||
*/
|
*/
|
||||||
public void validate() throws HibernateException {
|
public void validate() throws HibernateException {
|
||||||
// for each of the defined parameters, make sure its value
|
// for each of the defined parameters, make sure its value
|
||||||
// has been set
|
// has been set or a resolver has been implemented and specified
|
||||||
|
|
||||||
for ( final String parameterName : definition.getParameterNames() ) {
|
for ( final String parameterName : definition.getParameterNames() ) {
|
||||||
if ( parameters.get( parameterName ) == null ) {
|
if ( parameters.get( parameterName ) == null &&
|
||||||
|
(getParameterResolver( parameterName ) == null || getParameterResolver( parameterName ).getClass().isInterface()) ) {
|
||||||
throw new HibernateException(
|
throw new HibernateException(
|
||||||
"Filter [" + getName() + "] parameter [" + parameterName + "] value not set"
|
"Either value and resolver for filter [" + getName() + "] parameter [" + parameterName + "] not set"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,6 +192,7 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
|
||||||
// todo : move to MetamodelImpl
|
// todo : move to MetamodelImpl
|
||||||
private final transient Map<String, Generator> identifierGenerators;
|
private final transient Map<String, Generator> identifierGenerators;
|
||||||
private final transient Map<String, FilterDefinition> filters;
|
private final transient Map<String, FilterDefinition> filters;
|
||||||
|
private final transient java.util.Collection<FilterDefinition> autoEnabledFilters = new HashSet<>();
|
||||||
private final transient Map<String, FetchProfile> fetchProfiles;
|
private final transient Map<String, FetchProfile> fetchProfiles;
|
||||||
private final transient JavaType<Object> tenantIdentifierJavaType;
|
private final transient JavaType<Object> tenantIdentifierJavaType;
|
||||||
|
|
||||||
|
@ -260,6 +261,11 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
tenantIdentifierJavaType = jdbcMapping.getJavaTypeDescriptor();
|
tenantIdentifierJavaType = jdbcMapping.getJavaTypeDescriptor();
|
||||||
}
|
}
|
||||||
|
for (Map.Entry<String, FilterDefinition> filterEntry : filters.entrySet()) {
|
||||||
|
if (filterEntry.getValue().isAutoEnabled()) {
|
||||||
|
autoEnabledFilters.add( filterEntry.getValue() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
entityNameResolver = new CoordinatingEntityNameResolver( this, getInterceptor() );
|
entityNameResolver = new CoordinatingEntityNameResolver( this, getInterceptor() );
|
||||||
schemaManager = new SchemaManagerImpl( this, bootMetamodel );
|
schemaManager = new SchemaManagerImpl( this, bootMetamodel );
|
||||||
|
@ -1089,6 +1095,11 @@ public class SessionFactoryImpl extends QueryParameterBindingTypeResolverImpl im
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public java.util.Collection<FilterDefinition> getAutoEnabledFilters() {
|
||||||
|
return autoEnabledFilters;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean containsFetchProfileDefinition(String name) {
|
public boolean containsFetchProfileDefinition(String name) {
|
||||||
return fetchProfiles.containsKey( name );
|
return fetchProfiles.containsKey( name );
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,13 +8,17 @@ package org.hibernate.sql.ast.tree.predicate;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.hibernate.MappingException;
|
||||||
import org.hibernate.engine.spi.FilterDefinition;
|
import org.hibernate.engine.spi.FilterDefinition;
|
||||||
import org.hibernate.internal.FilterImpl;
|
import org.hibernate.internal.FilterImpl;
|
||||||
import org.hibernate.internal.FilterJdbcParameter;
|
import org.hibernate.internal.FilterJdbcParameter;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
import org.hibernate.metamodel.mapping.JdbcMappingContainer;
|
||||||
|
import org.hibernate.resource.beans.container.spi.BeanContainer;
|
||||||
|
import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer;
|
||||||
import org.hibernate.sql.ast.SqlAstWalker;
|
import org.hibernate.sql.ast.SqlAstWalker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -116,7 +120,7 @@ public class FilterPredicate implements Predicate {
|
||||||
parameters = CollectionHelper.arrayList( parameterNames.size() );
|
parameters = CollectionHelper.arrayList( parameterNames.size() );
|
||||||
for ( int i = 0; i < parameterNames.size(); i++ ) {
|
for ( int i = 0; i < parameterNames.size(); i++ ) {
|
||||||
final String paramName = parameterNames.get( i );
|
final String paramName = parameterNames.get( i );
|
||||||
final Object paramValue = filter.getParameter( paramName );
|
final Object paramValue = retrieveParamValue(filter, paramName);
|
||||||
final FilterDefinition filterDefinition = filter.getFilterDefinition();
|
final FilterDefinition filterDefinition = filter.getFilterDefinition();
|
||||||
final JdbcMapping jdbcMapping = filterDefinition.getParameterJdbcMapping( paramName );
|
final JdbcMapping jdbcMapping = filterDefinition.getParameterJdbcMapping( paramName );
|
||||||
|
|
||||||
|
@ -155,5 +159,15 @@ public class FilterPredicate implements Predicate {
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Object retrieveParamValue(FilterImpl filter, String paramName) {
|
||||||
|
Object value = filter.getParameter(paramName);
|
||||||
|
if (value != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Supplier filterParamResolver = filter.getParameterResolver( paramName );
|
||||||
|
return filterParamResolver == null ? null : filterParamResolver.get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2277,6 +2277,7 @@
|
||||||
</xsd:annotation>
|
</xsd:annotation>
|
||||||
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||||
<xsd:attribute name="type" use="required" type="xsd:string"/>
|
<xsd:attribute name="type" use="required" type="xsd:string"/>
|
||||||
|
<xsd:attribute name="resolver" type="xsd:string"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="condition" type="xsd:string" minOccurs="0"/>
|
<xsd:element name="condition" type="xsd:string" minOccurs="0"/>
|
||||||
|
|
|
@ -9,19 +9,18 @@ package org.hibernate.orm.test.filter;
|
||||||
import java.sql.Types;
|
import java.sql.Types;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import jakarta.persistence.Basic;
|
|
||||||
import jakarta.persistence.Column;
|
|
||||||
import jakarta.persistence.Convert;
|
|
||||||
import jakarta.persistence.Entity;
|
|
||||||
import jakarta.persistence.Id;
|
|
||||||
import jakarta.persistence.Table;
|
|
||||||
|
|
||||||
import org.hibernate.SharedSessionContract;
|
import org.hibernate.SharedSessionContract;
|
||||||
import org.hibernate.annotations.Filter;
|
import org.hibernate.annotations.Filter;
|
||||||
import org.hibernate.annotations.FilterDef;
|
import org.hibernate.annotations.FilterDef;
|
||||||
import org.hibernate.annotations.JdbcTypeCode;
|
import org.hibernate.annotations.JdbcTypeCode;
|
||||||
import org.hibernate.annotations.ParamDef;
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
import org.hibernate.boot.registry.BootstrapServiceRegistry;
|
||||||
|
import org.hibernate.boot.registry.BootstrapServiceRegistryBuilder;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||||
|
import org.hibernate.cfg.AvailableSettings;
|
||||||
import org.hibernate.community.dialect.AltibaseDialect;
|
import org.hibernate.community.dialect.AltibaseDialect;
|
||||||
import org.hibernate.community.dialect.FirebirdDialect;
|
import org.hibernate.community.dialect.FirebirdDialect;
|
||||||
import org.hibernate.dialect.AbstractHANADialect;
|
import org.hibernate.dialect.AbstractHANADialect;
|
||||||
|
@ -43,11 +42,21 @@ import org.hibernate.type.YesNoConverter;
|
||||||
import org.hibernate.testing.orm.junit.DomainModel;
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
import org.hibernate.testing.orm.junit.SkipForDialect;
|
import org.hibernate.testing.orm.junit.SkipForDialect;
|
||||||
|
import org.hibernate.testing.util.ServiceRegistryUtil;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import jakarta.enterprise.inject.se.SeContainer;
|
||||||
|
import jakarta.enterprise.inject.se.SeContainerInitializer;
|
||||||
|
import jakarta.persistence.Basic;
|
||||||
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Convert;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
|
import jakarta.persistence.Table;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
|
||||||
|
@ -57,7 +66,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
@DomainModel( annotatedClasses = {
|
@DomainModel( annotatedClasses = {
|
||||||
FilterParameterTests.EntityOne.class,
|
FilterParameterTests.EntityOne.class,
|
||||||
FilterParameterTests.EntityTwo.class,
|
FilterParameterTests.EntityTwo.class,
|
||||||
FilterParameterTests.EntityThree.class
|
FilterParameterTests.EntityThree.class,
|
||||||
|
FilterParameterTests.EntityFour.class
|
||||||
} )
|
} )
|
||||||
public class FilterParameterTests extends AbstractStatefulStatelessFilterTest {
|
public class FilterParameterTests extends AbstractStatefulStatelessFilterTest {
|
||||||
|
|
||||||
|
@ -65,11 +75,13 @@ public class FilterParameterTests extends AbstractStatefulStatelessFilterTest {
|
||||||
@MethodSource("transactionKind")
|
@MethodSource("transactionKind")
|
||||||
public void testYesNo(BiConsumer<SessionFactoryScope, Consumer<? extends SharedSessionContract>> inTransaction) {
|
public void testYesNo(BiConsumer<SessionFactoryScope, Consumer<? extends SharedSessionContract>> inTransaction) {
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
final EntityOne loaded = session.byId( EntityOne.class ).load( 1 );
|
final EntityOne loaded = session.byId( EntityOne.class ).load( 1 );
|
||||||
assertThat( loaded ).isNotNull();
|
assertThat( loaded ).isNotNull();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
inTransaction.accept( scope, session -> {
|
inTransaction.accept( scope, session -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
session.enableFilter( "filterYesNoConverter" ).setParameter( "yesNo", Boolean.FALSE );
|
session.enableFilter( "filterYesNoConverter" ).setParameter( "yesNo", Boolean.FALSE );
|
||||||
|
|
||||||
final EntityOne loaded = session.createQuery( "from EntityOne e where e.id = :id", EntityOne.class )
|
final EntityOne loaded = session.createQuery( "from EntityOne e where e.id = :id", EntityOne.class )
|
||||||
|
@ -97,11 +109,13 @@ public class FilterParameterTests extends AbstractStatefulStatelessFilterTest {
|
||||||
@SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 23, reason = "Oracle 23 interprets Y and T as true and N and F as false, so this works")
|
@SkipForDialect(dialectClass = OracleDialect.class, majorVersion = 23, reason = "Oracle 23 interprets Y and T as true and N and F as false, so this works")
|
||||||
public void testYesNoMismatch(BiConsumer<SessionFactoryScope, Consumer<? extends SharedSessionContract>> inTransaction) {
|
public void testYesNoMismatch(BiConsumer<SessionFactoryScope, Consumer<? extends SharedSessionContract>> inTransaction) {
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
final EntityOne loaded = session.byId( EntityOne.class ).load( 1 );
|
final EntityOne loaded = session.byId( EntityOne.class ).load( 1 );
|
||||||
assertThat( loaded ).isNotNull();
|
assertThat( loaded ).isNotNull();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
inTransaction.accept( scope, session -> {
|
inTransaction.accept( scope, session -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
session.enableFilter( "filterYesNoBoolean" ).setParameter( "yesNo", Boolean.FALSE );
|
session.enableFilter( "filterYesNoBoolean" ).setParameter( "yesNo", Boolean.FALSE );
|
||||||
|
|
||||||
assertThatThrownBy( () -> session.createQuery( "from EntityOne e where e.id = :id", EntityOne.class )
|
assertThatThrownBy( () -> session.createQuery( "from EntityOne e where e.id = :id", EntityOne.class )
|
||||||
|
@ -115,11 +129,13 @@ public class FilterParameterTests extends AbstractStatefulStatelessFilterTest {
|
||||||
@MethodSource("transactionKind")
|
@MethodSource("transactionKind")
|
||||||
public void testNumeric(BiConsumer<SessionFactoryScope, Consumer<? extends SharedSessionContract>> inTransaction) {
|
public void testNumeric(BiConsumer<SessionFactoryScope, Consumer<? extends SharedSessionContract>> inTransaction) {
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
final EntityTwo loaded = session.byId( EntityTwo.class ).load( 1 );
|
final EntityTwo loaded = session.byId( EntityTwo.class ).load( 1 );
|
||||||
assertThat( loaded ).isNotNull();
|
assertThat( loaded ).isNotNull();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
inTransaction.accept( scope, session -> {
|
inTransaction.accept( scope, session -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
session.enableFilter( "filterNumberConverter" ).setParameter( "zeroOne", Boolean.FALSE );
|
session.enableFilter( "filterNumberConverter" ).setParameter( "zeroOne", Boolean.FALSE );
|
||||||
|
|
||||||
final EntityTwo loaded = session.createQuery( "from EntityTwo e where e.id = :id", EntityTwo.class )
|
final EntityTwo loaded = session.createQuery( "from EntityTwo e where e.id = :id", EntityTwo.class )
|
||||||
|
@ -146,11 +162,13 @@ public class FilterParameterTests extends AbstractStatefulStatelessFilterTest {
|
||||||
@SkipForDialect(dialectClass = FirebirdDialect.class, matchSubTypes = true, reason = "Firebird silently converts a boolean to integral types")
|
@SkipForDialect(dialectClass = FirebirdDialect.class, matchSubTypes = true, reason = "Firebird silently converts a boolean to integral types")
|
||||||
public void testNumericMismatch(BiConsumer<SessionFactoryScope, Consumer<? extends SharedSessionContract>> inTransaction) {
|
public void testNumericMismatch(BiConsumer<SessionFactoryScope, Consumer<? extends SharedSessionContract>> inTransaction) {
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
final EntityTwo loaded = session.byId( EntityTwo.class ).load( 1 );
|
final EntityTwo loaded = session.byId( EntityTwo.class ).load( 1 );
|
||||||
assertThat( loaded ).isNotNull();
|
assertThat( loaded ).isNotNull();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
inTransaction.accept( scope, session -> {
|
inTransaction.accept( scope, session -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
session.enableFilter( "filterNumberBoolean" ).setParameter( "zeroOne", Boolean.FALSE );
|
session.enableFilter( "filterNumberBoolean" ).setParameter( "zeroOne", Boolean.FALSE );
|
||||||
|
|
||||||
assertThatThrownBy( () -> session.createQuery( "from EntityTwo e where e.id = :id", EntityTwo.class )
|
assertThatThrownBy( () -> session.createQuery( "from EntityTwo e where e.id = :id", EntityTwo.class )
|
||||||
|
@ -168,11 +186,13 @@ public class FilterParameterTests extends AbstractStatefulStatelessFilterTest {
|
||||||
@SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "PostgresPlus silently converts strings to integral types")
|
@SkipForDialect(dialectClass = PostgresPlusDialect.class, reason = "PostgresPlus silently converts strings to integral types")
|
||||||
public void testMismatch(BiConsumer<SessionFactoryScope, Consumer<? extends SharedSessionContract>> inTransaction) {
|
public void testMismatch(BiConsumer<SessionFactoryScope, Consumer<? extends SharedSessionContract>> inTransaction) {
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
final EntityThree loaded = session.byId( EntityThree.class ).load( 1 );
|
final EntityThree loaded = session.byId( EntityThree.class ).load( 1 );
|
||||||
assertThat( loaded ).isNotNull();
|
assertThat( loaded ).isNotNull();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
inTransaction.accept( scope, session -> {
|
inTransaction.accept( scope, session -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
session.enableFilter( "filterMismatchConverter" ).setParameter( "mismatch", Boolean.FALSE );
|
session.enableFilter( "filterMismatchConverter" ).setParameter( "mismatch", Boolean.FALSE );
|
||||||
|
|
||||||
assertThatThrownBy( () -> session.createQuery( "from EntityThree e where e.id = :id", EntityThree.class )
|
assertThatThrownBy( () -> session.createQuery( "from EntityThree e where e.id = :id", EntityThree.class )
|
||||||
|
@ -182,6 +202,73 @@ public class FilterParameterTests extends AbstractStatefulStatelessFilterTest {
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("transactionKind")
|
||||||
|
public void testAutoEnableWithResolver() {
|
||||||
|
final SeContainerInitializer cdiInitializer = SeContainerInitializer.newInstance()
|
||||||
|
.disableDiscovery()
|
||||||
|
.addBeanClasses( EntityFourDepartmentResolver.class );
|
||||||
|
try ( final SeContainer cdiContainer = cdiInitializer.initialize() ) {
|
||||||
|
BootstrapServiceRegistry bsr = new BootstrapServiceRegistryBuilder().build();
|
||||||
|
|
||||||
|
final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistryBuilder( bsr )
|
||||||
|
.applySetting( AvailableSettings.CDI_BEAN_MANAGER, cdiContainer.getBeanManager() )
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
session.getEnabledFilter("subDepartmentFilter").setParameter("subdepartment", "FIRST_A" );
|
||||||
|
final EntityFour first_a = session.createQuery( "from EntityFour e where e.id = :id", EntityFour.class )
|
||||||
|
.setParameter( "id", 1 )
|
||||||
|
.getSingleResultOrNull();
|
||||||
|
assertThat( first_a ).isNotNull();
|
||||||
|
assertThat( first_a.getDepartment() ).isEqualTo( "FIRST" );
|
||||||
|
session.getEnabledFilter("subDepartmentFilter").setParameter("subdepartment", "SECOND_A" );
|
||||||
|
final EntityFour second = session.createQuery( "from EntityFour e where e.id = :id", EntityFour.class )
|
||||||
|
.setParameter( "id", 3 )
|
||||||
|
.getSingleResultOrNull();
|
||||||
|
assertThat( second ).isNull();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
StandardServiceRegistryBuilder.destroy( ssr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("transactionKind")
|
||||||
|
public void testAutoEnableWithoutResolver() {
|
||||||
|
final SeContainerInitializer cdiInitializer = SeContainerInitializer.newInstance()
|
||||||
|
.disableDiscovery()
|
||||||
|
.addBeanClasses( EntityFourDepartmentResolver.class );
|
||||||
|
try ( final SeContainer cdiContainer = cdiInitializer.initialize() ) {
|
||||||
|
BootstrapServiceRegistry bsr = new BootstrapServiceRegistryBuilder().build();
|
||||||
|
|
||||||
|
final StandardServiceRegistry ssr = ServiceRegistryUtil.serviceRegistryBuilder( bsr )
|
||||||
|
.applySetting( AvailableSettings.CDI_BEAN_MANAGER, cdiContainer.getBeanManager() )
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
scope.inTransaction( (session) -> {
|
||||||
|
session.getEnabledFilter("subDepartmentFilter").setParameter("subdepartment", "FIRST_A" );
|
||||||
|
final EntityFour first_a = session.createQuery( "from EntityFour e where e.id = :id", EntityFour.class )
|
||||||
|
.setParameter( "id", 1 )
|
||||||
|
.getSingleResultOrNull();
|
||||||
|
assertThat( first_a ).isNotNull();
|
||||||
|
assertThat( first_a.getDepartment() ).isEqualTo( "FIRST" );
|
||||||
|
final EntityFour first_b = session.createQuery( "from EntityFour e where e.id = :id", EntityFour.class )
|
||||||
|
.setParameter( "id", 2 )
|
||||||
|
.getSingleResultOrNull();
|
||||||
|
assertThat( first_b ).isNull();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
StandardServiceRegistryBuilder.destroy( ssr );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void prepareTestData() {
|
public void prepareTestData() {
|
||||||
|
@ -189,15 +276,21 @@ public class FilterParameterTests extends AbstractStatefulStatelessFilterTest {
|
||||||
session.persist( new EntityOne( 1, "one" ) );
|
session.persist( new EntityOne( 1, "one" ) );
|
||||||
session.persist( new EntityTwo( 1, "two" ) );
|
session.persist( new EntityTwo( 1, "two" ) );
|
||||||
session.persist( new EntityThree( 1, "three" ) );
|
session.persist( new EntityThree( 1, "three" ) );
|
||||||
|
session.persist( new EntityFour( 1, "four", "FIRST", "FIRST_A" ) );
|
||||||
|
session.persist( new EntityFour( 2, "four", "FIRST", "FIRST_B" ) );
|
||||||
|
session.persist( new EntityFour( 3, "four", "SECOND", "SECOND_A" ) );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void dropTestData() {
|
public void dropTestData() {
|
||||||
scope.inTransaction( (session) -> {
|
scope.inTransaction( (session) -> {
|
||||||
|
session.disableFilter( "subDepartmentFilter" );
|
||||||
|
session.disableFilter( "departmentFilter" );
|
||||||
session.createMutationQuery( "delete EntityOne" ).executeUpdate();
|
session.createMutationQuery( "delete EntityOne" ).executeUpdate();
|
||||||
session.createMutationQuery( "delete EntityTwo" ).executeUpdate();
|
session.createMutationQuery( "delete EntityTwo" ).executeUpdate();
|
||||||
session.createMutationQuery( "delete EntityThree" ).executeUpdate();
|
session.createMutationQuery( "delete EntityThree" ).executeUpdate();
|
||||||
|
session.createMutationQuery( "delete EntityFour" ).executeUpdate();
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,4 +450,75 @@ public class FilterParameterTests extends AbstractStatefulStatelessFilterTest {
|
||||||
this.mismatch = mismatch;
|
this.mismatch = mismatch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FilterDef(
|
||||||
|
name = "departmentFilter",
|
||||||
|
defaultCondition = "department = :department",
|
||||||
|
parameters = @ParamDef( name = "department", type = String.class, resolver = EntityFourDepartmentResolver.class),
|
||||||
|
autoEnabled = true
|
||||||
|
)
|
||||||
|
@Filter( name = "departmentFilter" )
|
||||||
|
@FilterDef(
|
||||||
|
name = "subDepartmentFilter",
|
||||||
|
defaultCondition = "subdepartment = :subdepartment",
|
||||||
|
parameters = @ParamDef( name = "subdepartment", type = String.class ),
|
||||||
|
autoEnabled = true
|
||||||
|
)
|
||||||
|
@Filter( name = "subDepartmentFilter" )
|
||||||
|
@Entity( name = "EntityFour" )
|
||||||
|
@Table( name = "EntityFour" )
|
||||||
|
public static class EntityFour {
|
||||||
|
@Id
|
||||||
|
private Integer id;
|
||||||
|
@Basic
|
||||||
|
private String name;
|
||||||
|
private String department;
|
||||||
|
private String subdepartment;
|
||||||
|
|
||||||
|
private EntityFour() {
|
||||||
|
// for use by Hibernate
|
||||||
|
}
|
||||||
|
|
||||||
|
public EntityFour(Integer id, String name, String department, String subdepartment) {
|
||||||
|
this.id = id;
|
||||||
|
this.name = name;
|
||||||
|
this.department = department;
|
||||||
|
this.subdepartment = subdepartment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDepartment() {
|
||||||
|
return department;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDepartment(String department) {
|
||||||
|
this.department = department;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSubdepartment() {
|
||||||
|
return subdepartment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSubdepartment(String subdepartment) {
|
||||||
|
this.subdepartment = subdepartment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EntityFourDepartmentResolver implements Supplier<String> {
|
||||||
|
@Override
|
||||||
|
public String get() {
|
||||||
|
return "FIRST";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.orm.test.pc;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import jakarta.persistence.CascadeType;
|
import jakarta.persistence.CascadeType;
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
|
@ -360,4 +361,69 @@ public class FilterTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
//tag::pc-filter-Account-example[]
|
//tag::pc-filter-Account-example[]
|
||||||
}
|
}
|
||||||
//end::pc-filter-Account-example[]
|
//end::pc-filter-Account-example[]
|
||||||
|
|
||||||
|
|
||||||
|
@Entity(name = "AutoFilteredAccount")
|
||||||
|
@Table(name = "autofilteredaccount")
|
||||||
|
//tag::pc-filter-auto-enabled-Account-example[]
|
||||||
|
@FilterDef(
|
||||||
|
name="activeAccount",
|
||||||
|
parameters = @ParamDef(
|
||||||
|
name="active",
|
||||||
|
type=Boolean.class
|
||||||
|
),
|
||||||
|
autoEnabled = true
|
||||||
|
)
|
||||||
|
//end::pc-filter-auto-enabled-Account-example[]
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="active_status = :active"
|
||||||
|
)
|
||||||
|
//tag::pc-filter-resolver-Account-example[]
|
||||||
|
@FilterDef(
|
||||||
|
name="activeAccountWithResolver",
|
||||||
|
parameters = @ParamDef(
|
||||||
|
name="active",
|
||||||
|
type=Boolean.class,
|
||||||
|
resolver = AccountIsActiveResolver.class
|
||||||
|
),
|
||||||
|
autoEnabled = true
|
||||||
|
)
|
||||||
|
//end::pc-filter-resolver-Account-example[]
|
||||||
|
public static class AutoFilteredAccount {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "active_status")
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AutoFilteredAccount setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AutoFilteredAccount setActive(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//tag::pc-filter-resolver-Account-example[]
|
||||||
|
|
||||||
|
public static class AccountIsActiveResolver implements Supplier<Boolean> {
|
||||||
|
@Override
|
||||||
|
public Boolean get() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//end::pc-filter-resolver-Account-example[]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue