implement @Filter for HQL/Criteria
This commit is contained in:
parent
709e7d49b7
commit
686a519680
|
@ -18,7 +18,6 @@ import javax.persistence.OneToMany;
|
||||||
import javax.persistence.OrderColumn;
|
import javax.persistence.OrderColumn;
|
||||||
|
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.annotations.Filter;
|
|
||||||
import org.hibernate.annotations.FilterDef;
|
import org.hibernate.annotations.FilterDef;
|
||||||
import org.hibernate.annotations.FilterJoinTable;
|
import org.hibernate.annotations.FilterJoinTable;
|
||||||
import org.hibernate.annotations.ParamDef;
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
@ -26,8 +25,6 @@ import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
|
||||||
|
|
||||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
@ -118,10 +115,6 @@ public class FilterJoinTableTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
type="int"
|
type="int"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@Filter(
|
|
||||||
name="firstAccounts",
|
|
||||||
condition="order_id <= :maxOrderId"
|
|
||||||
)
|
|
||||||
public static class Client {
|
public static class Client {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
|
|
@ -18,11 +18,13 @@ import javax.persistence.Id;
|
||||||
import javax.persistence.ManyToOne;
|
import javax.persistence.ManyToOne;
|
||||||
import javax.persistence.NoResultException;
|
import javax.persistence.NoResultException;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.annotations.Filter;
|
import org.hibernate.annotations.Filter;
|
||||||
import org.hibernate.annotations.FilterDef;
|
import org.hibernate.annotations.FilterDef;
|
||||||
import org.hibernate.annotations.ParamDef;
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
import org.hibernate.annotations.SqlFragmentAlias;
|
||||||
import org.hibernate.annotations.Where;
|
import org.hibernate.annotations.Where;
|
||||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||||
|
|
||||||
|
@ -56,7 +58,8 @@ public class FilterTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
//tag::pc-filter-persistence-example[]
|
//tag::pc-filter-persistence-example[]
|
||||||
Client client = new Client()
|
Client client = new Client()
|
||||||
.setId( 1L )
|
.setId( 1L )
|
||||||
.setName( "John Doe" );
|
.setName( "John Doe" )
|
||||||
|
.setType( AccountType.DEBIT );
|
||||||
|
|
||||||
client.addAccount(
|
client.addAccount(
|
||||||
new Account()
|
new Account()
|
||||||
|
@ -186,7 +189,7 @@ public class FilterTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
Client client = entityManager.find( Client.class, 1L );
|
Client client = entityManager.find( Client.class, 1L );
|
||||||
|
|
||||||
assertEquals( 2, client.getAccounts().size() );
|
assertEquals( 1, client.getAccounts().size() );
|
||||||
//end::pc-filter-collection-query-example[]
|
//end::pc-filter-collection-query-example[]
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
@ -198,6 +201,7 @@ public class FilterTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
//tag::pc-filter-Client-example[]
|
//tag::pc-filter-Client-example[]
|
||||||
@Entity(name = "Client")
|
@Entity(name = "Client")
|
||||||
|
@Table(name = "client")
|
||||||
public static class Client {
|
public static class Client {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
@ -205,13 +209,19 @@ public class FilterTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
@OneToMany(
|
@OneToMany(
|
||||||
mappedBy = "client",
|
mappedBy = "client",
|
||||||
cascade = CascadeType.ALL
|
cascade = CascadeType.ALL
|
||||||
)
|
)
|
||||||
@Filter(
|
@Filter(
|
||||||
name="activeAccount",
|
name="activeAccount",
|
||||||
condition="active_status = :active"
|
condition="{a}.active_status = :active and {a}.type = {c}.type",
|
||||||
|
aliases = {
|
||||||
|
@SqlFragmentAlias( alias = "a", table= "account"),
|
||||||
|
@SqlFragmentAlias( alias = "c", table= "client"),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
private List<Account> accounts = new ArrayList<>( );
|
private List<Account> accounts = new ArrayList<>( );
|
||||||
|
|
||||||
|
@ -235,6 +245,15 @@ public class FilterTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public List<Account> getAccounts() {
|
public List<Account> getAccounts() {
|
||||||
return accounts;
|
return accounts;
|
||||||
}
|
}
|
||||||
|
@ -249,6 +268,7 @@ public class FilterTest extends BaseEntityManagerFunctionalTestCase {
|
||||||
|
|
||||||
//tag::pc-filter-Account-example[]
|
//tag::pc-filter-Account-example[]
|
||||||
@Entity(name = "Account")
|
@Entity(name = "Account")
|
||||||
|
@Table(name = "account")
|
||||||
@FilterDef(
|
@FilterDef(
|
||||||
name="activeAccount",
|
name="activeAccount",
|
||||||
parameters = @ParamDef(
|
parameters = @ParamDef(
|
||||||
|
|
|
@ -13,13 +13,22 @@ import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.hibernate.Filter;
|
import org.hibernate.Filter;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.MappingException;
|
||||||
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.internal.util.StringHelper;
|
import org.hibernate.internal.util.StringHelper;
|
||||||
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.PluralAttributeMapping;
|
||||||
|
import org.hibernate.persister.collection.AbstractCollectionPersister;
|
||||||
|
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||||
|
import org.hibernate.persister.entity.Joinable;
|
||||||
import org.hibernate.sql.Template;
|
import org.hibernate.sql.Template;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
|
||||||
import org.hibernate.type.Type;
|
import org.hibernate.type.Type;
|
||||||
|
|
||||||
|
import static org.hibernate.internal.util.StringHelper.join;
|
||||||
import static org.hibernate.internal.util.StringHelper.safeInterning;
|
import static org.hibernate.internal.util.StringHelper.safeInterning;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,7 +40,7 @@ import static org.hibernate.internal.util.StringHelper.safeInterning;
|
||||||
*/
|
*/
|
||||||
public class FilterHelper {
|
public class FilterHelper {
|
||||||
|
|
||||||
private static Pattern FILTER_PARAMETER_PATTERN = Pattern.compile( ":(\\w+)\\.(\\w+)" );
|
private static final Pattern FILTER_PARAMETER_PATTERN = Pattern.compile( ":(\\w+)\\.(\\w+)" );
|
||||||
|
|
||||||
private final String[] filterNames;
|
private final String[] filterNames;
|
||||||
private final String[] filterConditions;
|
private final String[] filterConditions;
|
||||||
|
@ -141,49 +150,30 @@ public class FilterHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TypedValue {
|
public static FilterPredicate createFilterPredicate(LoadQueryInfluencers loadQueryInfluencers, Joinable joinable, String alias) {
|
||||||
private final Type type;
|
if ( loadQueryInfluencers.hasEnabledFilters() ) {
|
||||||
private final Object value;
|
final String filterFragment;
|
||||||
|
if ( joinable instanceof AbstractCollectionPersister && ( (AbstractCollectionPersister) joinable ).isManyToMany() ) {
|
||||||
public TypedValue(Type type, Object value) {
|
filterFragment = ( (AbstractCollectionPersister) joinable ).getManyToManyFilterFragment(
|
||||||
this.type = type;
|
alias,
|
||||||
this.value = value;
|
loadQueryInfluencers.getEnabledFilters()
|
||||||
}
|
);
|
||||||
|
}
|
||||||
public Type getType() {
|
else {
|
||||||
return type;
|
filterFragment = joinable.filterFragment( alias, loadQueryInfluencers.getEnabledFilters() );
|
||||||
}
|
}
|
||||||
|
if ( ! StringHelper.isEmptyOrWhiteSpace( filterFragment ) ) {
|
||||||
public Object getValue() {
|
return doCreateFilterPredicate( filterFragment, loadQueryInfluencers.getEnabledFilters() );
|
||||||
return value;
|
}
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class TransformResult {
|
private static FilterPredicate doCreateFilterPredicate(String filterFragment, Map<String, Filter> enabledFilters) {
|
||||||
private final String transformedFilterFragment;
|
|
||||||
private final List<TypedValue> parameters;
|
|
||||||
|
|
||||||
public TransformResult(
|
|
||||||
String transformedFilterFragment,
|
|
||||||
List<TypedValue> parameters) {
|
|
||||||
this.transformedFilterFragment = transformedFilterFragment;
|
|
||||||
this.parameters = parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getTransformedFilterFragment() {
|
|
||||||
return transformedFilterFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<TypedValue> getParameters() {
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TransformResult transformToPositionalParameters(String filterFragment, Map<String, Filter> enabledFilters) {
|
|
||||||
final Matcher matcher = FILTER_PARAMETER_PATTERN.matcher( filterFragment );
|
final Matcher matcher = FILTER_PARAMETER_PATTERN.matcher( filterFragment );
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
int pos = 0;
|
int pos = 0;
|
||||||
final List<TypedValue> parameters = new ArrayList<>( matcher.groupCount() );
|
final List<FilterJdbcParameter> parameters = new ArrayList<>( matcher.groupCount() );
|
||||||
while( matcher.find() ) {
|
while( matcher.find() ) {
|
||||||
sb.append( filterFragment, pos, matcher.start() );
|
sb.append( filterFragment, pos, matcher.start() );
|
||||||
pos = matcher.end();
|
pos = matcher.end();
|
||||||
|
@ -192,16 +182,19 @@ public class FilterHelper {
|
||||||
final String parameterName = matcher.group( 2 );
|
final String parameterName = matcher.group( 2 );
|
||||||
final FilterImpl enabledFilter = (FilterImpl) enabledFilters.get( filterName );
|
final FilterImpl enabledFilter = (FilterImpl) enabledFilters.get( filterName );
|
||||||
if ( enabledFilter == null ) {
|
if ( enabledFilter == null ) {
|
||||||
throw new HibernateException( String.format( "unknown filter [%s]", filterName ) );
|
throw new MappingException( String.format( "unknown filter [%s]", filterName ) );
|
||||||
}
|
}
|
||||||
final Type parameterType = enabledFilter.getFilterDefinition().getParameterType( parameterName );
|
final Type parameterType = enabledFilter.getFilterDefinition().getParameterType( parameterName );
|
||||||
|
if ( ! (parameterType instanceof JdbcMapping ) ) {
|
||||||
|
throw new MappingException( String.format( "parameter [%s] for filter [%s] is not of JdbcMapping type", parameterName, filterName ) );
|
||||||
|
}
|
||||||
final Object parameterValue = enabledFilter.getParameter( parameterName );
|
final Object parameterValue = enabledFilter.getParameter( parameterName );
|
||||||
if ( parameterValue == null ) {
|
if ( parameterValue == null ) {
|
||||||
throw new HibernateException( String.format( "unknown parameter [%s] for filter [%s]", parameterName, filterName ) );
|
throw new MappingException( String.format( "unknown parameter [%s] for filter [%s]", parameterName, filterName ) );
|
||||||
}
|
}
|
||||||
parameters.add( new TypedValue( parameterType, parameterValue ) );
|
parameters.add( new FilterJdbcParameter( (JdbcMapping) parameterType, parameterValue ) );
|
||||||
}
|
}
|
||||||
sb.append( filterFragment, pos, filterFragment.length() );
|
sb.append( filterFragment, pos, filterFragment.length() );
|
||||||
return new TransformResult( sb.toString(), parameters );
|
return new FilterPredicate( sb.toString(), parameters );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.internal;
|
||||||
|
|
||||||
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||||
|
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nathan Xu
|
||||||
|
*/
|
||||||
|
public class FilterJdbcParameter {
|
||||||
|
private final JdbcParameter parameter;
|
||||||
|
private final JdbcMapping jdbcMapping;
|
||||||
|
private final Object jdbcParameterValue;
|
||||||
|
|
||||||
|
public FilterJdbcParameter(JdbcMapping jdbcMapping, Object jdbcParameterValue) {
|
||||||
|
this.parameter = new JdbcParameterImpl( jdbcMapping );
|
||||||
|
this.jdbcMapping = jdbcMapping;
|
||||||
|
this.jdbcParameterValue = jdbcParameterValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JdbcParameter getParameter() {
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JdbcParameterBinder getBinder() {
|
||||||
|
return parameter.getParameterBinder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JdbcParameterBinding getBinding() {
|
||||||
|
return new JdbcParameterBinding() {
|
||||||
|
@Override
|
||||||
|
public JdbcMapping getBindType() {
|
||||||
|
return jdbcMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getBindValue() {
|
||||||
|
return jdbcParameterValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -163,8 +163,7 @@ public class CollectionLoaderBatchKey implements CollectionLoader {
|
||||||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );
|
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );
|
||||||
|
|
||||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( keyJdbcCount * smallBatchLength );
|
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( keyJdbcCount * smallBatchLength );
|
||||||
|
jdbcSelect.registerFilterJdbcParameterBindings( jdbcParameterBindings );
|
||||||
sqlAst.getQuerySpec().bindFilterPredicateParameters( jdbcParameterBindings );
|
|
||||||
|
|
||||||
final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();
|
final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,7 @@ public class CollectionLoaderSingleKey implements CollectionLoader {
|
||||||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );
|
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );
|
||||||
|
|
||||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( keyJdbcCount );
|
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( keyJdbcCount );
|
||||||
|
jdbcSelect.registerFilterJdbcParameterBindings( jdbcParameterBindings );
|
||||||
|
|
||||||
final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();
|
final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();
|
||||||
|
|
||||||
|
@ -127,8 +128,6 @@ public class CollectionLoaderSingleKey implements CollectionLoader {
|
||||||
);
|
);
|
||||||
assert !paramItr.hasNext();
|
assert !paramItr.hasNext();
|
||||||
|
|
||||||
sqlAst.getQuerySpec().bindFilterPredicateParameters( jdbcParameterBindings );
|
|
||||||
|
|
||||||
jdbcServices.getJdbcSelectExecutor().list(
|
jdbcServices.getJdbcSelectExecutor().list(
|
||||||
jdbcSelect,
|
jdbcSelect,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.engine.spi.SubselectFetch;
|
import org.hibernate.engine.spi.SubselectFetch;
|
||||||
import org.hibernate.internal.FilterHelper;
|
import org.hibernate.internal.FilterHelper;
|
||||||
import org.hibernate.internal.FilterHelper.TransformResult;
|
|
||||||
import org.hibernate.loader.ast.spi.Loadable;
|
import org.hibernate.loader.ast.spi.Loadable;
|
||||||
import org.hibernate.loader.ast.spi.Loader;
|
import org.hibernate.loader.ast.spi.Loader;
|
||||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||||
|
@ -49,13 +48,11 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
|
||||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.TableReferenceJoin;
|
|
||||||
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
import org.hibernate.sql.ast.tree.predicate.ComparisonPredicate;
|
||||||
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
|
|
||||||
import org.hibernate.sql.ast.tree.predicate.InListPredicate;
|
import org.hibernate.sql.ast.tree.predicate.InListPredicate;
|
||||||
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
|
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
|
||||||
|
@ -231,7 +228,7 @@ public class LoaderSelectBuilder {
|
||||||
sqlAstCreationState.getFromClauseAccess().registerTableGroup( rootNavigablePath, rootTableGroup );
|
sqlAstCreationState.getFromClauseAccess().registerTableGroup( rootNavigablePath, rootTableGroup );
|
||||||
|
|
||||||
if ( loadable instanceof PluralAttributeMapping ) {
|
if ( loadable instanceof PluralAttributeMapping ) {
|
||||||
applyFiltering( rootQuerySpec, loadQueryInfluencers, (PluralAttributeMapping) loadable );
|
applyFiltering( rootQuerySpec, rootTableGroup, (PluralAttributeMapping) loadable );
|
||||||
applyOrdering( rootTableGroup, (PluralAttributeMapping) loadable );
|
applyOrdering( rootTableGroup, (PluralAttributeMapping) loadable );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,62 +379,22 @@ public class LoaderSelectBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyFiltering(
|
private void applyFiltering(QuerySpec querySpec, TableGroup tableGroup, PluralAttributeMapping pluralAttributeMapping) {
|
||||||
QuerySpec querySpec,
|
|
||||||
LoadQueryInfluencers loadQueryInfluencers,
|
|
||||||
PluralAttributeMapping pluralAttributeMapping) {
|
|
||||||
if ( loadQueryInfluencers.hasEnabledFilters() ) {
|
if ( loadQueryInfluencers.hasEnabledFilters() ) {
|
||||||
final Joinable joinable = pluralAttributeMapping
|
final Joinable joinable = pluralAttributeMapping
|
||||||
.getCollectionDescriptor()
|
.getCollectionDescriptor()
|
||||||
.getCollectionType()
|
.getCollectionType()
|
||||||
.getAssociatedJoinable( creationContext.getSessionFactory() );
|
.getAssociatedJoinable( creationContext.getSessionFactory() );
|
||||||
assert joinable instanceof AbstractCollectionPersister;
|
assert joinable instanceof AbstractCollectionPersister;
|
||||||
final AbstractCollectionPersister collectionPersister = (AbstractCollectionPersister) joinable;
|
final String tableExpression = joinable.getTableName();
|
||||||
querySpec.getFromClause().getRoots().forEach( tableGroup -> consumeTableAliasByTableExpression(
|
final String tableAlias = tableGroup.resolveTableReference( tableExpression ).getIdentificationVariable();
|
||||||
tableGroup,
|
final Predicate filterPredicate = FilterHelper.createFilterPredicate(
|
||||||
joinable.getTableName(),
|
loadQueryInfluencers,
|
||||||
alias -> {
|
joinable,
|
||||||
final boolean isManyToMany = collectionPersister.isManyToMany();
|
tableAlias
|
||||||
String filterFragment;
|
|
||||||
if ( isManyToMany ) {
|
|
||||||
filterFragment = collectionPersister.getManyToManyFilterFragment(
|
|
||||||
alias,
|
|
||||||
loadQueryInfluencers.getEnabledFilters()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
filterFragment = collectionPersister.filterFragment(
|
|
||||||
alias,
|
|
||||||
loadQueryInfluencers.getEnabledFilters()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final TransformResult transformResult = FilterHelper.transformToPositionalParameters(
|
|
||||||
filterFragment, loadQueryInfluencers.getEnabledFilters()
|
|
||||||
);
|
|
||||||
filterFragment = transformResult.getTransformedFilterFragment();
|
|
||||||
final FilterPredicate filterPredicate = new FilterPredicate(
|
|
||||||
filterFragment, transformResult.getParameters()
|
|
||||||
);
|
|
||||||
querySpec.applyPredicate( filterPredicate );
|
|
||||||
querySpec.addFilterPredicate( filterPredicate );
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
if ( filterPredicate != null ) {
|
||||||
}
|
querySpec.applyPredicate( filterPredicate );
|
||||||
|
|
||||||
private void consumeTableAliasByTableExpression(TableGroup tableGroup, String tableExpression, Consumer<String> aliasConsumer) {
|
|
||||||
if ( tableExpression.equals( tableGroup.getPrimaryTableReference().getTableExpression() ) ) {
|
|
||||||
aliasConsumer.accept( tableGroup.getPrimaryTableReference().getIdentificationVariable() );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for ( TableReferenceJoin referenceJoin : tableGroup.getTableReferenceJoins() ) {
|
|
||||||
if ( tableExpression.equals( referenceJoin.getJoinedTableReference().getTableExpression() ) ) {
|
|
||||||
aliasConsumer.accept( referenceJoin.getJoinedTableReference().getIdentificationVariable() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for ( TableGroupJoin tableGroupJoin : tableGroup.getTableGroupJoins() ) {
|
|
||||||
consumeTableAliasByTableExpression( tableGroupJoin.getJoinedGroup(), tableExpression, aliasConsumer );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -554,7 +511,11 @@ public class LoaderSelectBuilder {
|
||||||
fetches.add( fetch );
|
fetches.add( fetch );
|
||||||
|
|
||||||
if ( fetchable instanceof PluralAttributeMapping && fetchTiming == FetchTiming.IMMEDIATE && joined ) {
|
if ( fetchable instanceof PluralAttributeMapping && fetchTiming == FetchTiming.IMMEDIATE && joined ) {
|
||||||
applyFiltering( querySpec, loadQueryInfluencers, (PluralAttributeMapping) fetchable );
|
applyFiltering(
|
||||||
|
querySpec,
|
||||||
|
creationState.getFromClauseAccess().getTableGroup( fetchablePath ),
|
||||||
|
( (PluralAttributeMapping) fetchable )
|
||||||
|
);
|
||||||
applyOrdering(
|
applyOrdering(
|
||||||
querySpec,
|
querySpec,
|
||||||
fetchablePath,
|
fetchablePath,
|
||||||
|
@ -630,7 +591,7 @@ public class LoaderSelectBuilder {
|
||||||
sqlAstCreationState.getFromClauseAccess().registerTableGroup( rootNavigablePath, rootTableGroup );
|
sqlAstCreationState.getFromClauseAccess().registerTableGroup( rootNavigablePath, rootTableGroup );
|
||||||
|
|
||||||
// NOTE : no need to check - we are explicitly processing a plural-attribute
|
// NOTE : no need to check - we are explicitly processing a plural-attribute
|
||||||
applyFiltering( rootQuerySpec, loadQueryInfluencers, (PluralAttributeMapping) loadable );
|
applyFiltering( rootQuerySpec, rootTableGroup, attributeMapping );
|
||||||
applyOrdering( rootTableGroup, attributeMapping );
|
applyOrdering( rootTableGroup, attributeMapping );
|
||||||
|
|
||||||
// generate and apply the restriction
|
// generate and apply the restriction
|
||||||
|
|
|
@ -103,6 +103,7 @@ public class SingleIdLoadPlan<T> implements SingleEntityLoadPlan {
|
||||||
assert jdbcParameters.size() % jdbcTypeCount == 0;
|
assert jdbcParameters.size() % jdbcTypeCount == 0;
|
||||||
|
|
||||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( jdbcTypeCount );
|
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( jdbcTypeCount );
|
||||||
|
jdbcSelect.registerFilterJdbcParameterBindings( jdbcParameterBindings );
|
||||||
|
|
||||||
final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();
|
final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();
|
||||||
|
|
||||||
|
@ -132,8 +133,6 @@ public class SingleIdLoadPlan<T> implements SingleEntityLoadPlan {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlAst.getQuerySpec().bindFilterPredicateParameters( jdbcParameterBindings );
|
|
||||||
|
|
||||||
final List list = JdbcSelectExecutorStandardImpl.INSTANCE.list(
|
final List list = JdbcSelectExecutorStandardImpl.INSTANCE.list(
|
||||||
jdbcSelect,
|
jdbcSelect,
|
||||||
jdbcParameterBindings,
|
jdbcParameterBindings,
|
||||||
|
|
|
@ -160,6 +160,7 @@ public class ConcreteSqmSelectQueryPlan<R> implements SelectQueryPlan<R> {
|
||||||
sqmInterpretation.getTableGroupAccess()::findTableGroup,
|
sqmInterpretation.getTableGroupAccess()::findTableGroup,
|
||||||
session
|
session
|
||||||
);
|
);
|
||||||
|
sqmInterpretation.getJdbcSelect().registerFilterJdbcParameterBindings( jdbcParameterBindings );
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
return session.getFactory().getJdbcServices().getJdbcSelectExecutor().list(
|
||||||
|
|
|
@ -45,6 +45,11 @@ public class SqmInterpretationsKey implements QueryInterpretationCache.Key {
|
||||||
private static boolean isCacheable(QuerySqmImpl<?> query) {
|
private static boolean isCacheable(QuerySqmImpl<?> query) {
|
||||||
assert query.getQueryOptions().getAppliedGraph() != null;
|
assert query.getQueryOptions().getAppliedGraph() != null;
|
||||||
|
|
||||||
|
if ( query.getSession().getLoadQueryInfluencers().hasEnabledFilters() ) {
|
||||||
|
// At the moment we cannot cache query plan if there is filter enabled.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ( query.getQueryOptions().getAppliedGraph().getSemantic() != null ) {
|
if ( query.getQueryOptions().getAppliedGraph().getSemantic() != null ) {
|
||||||
// At the moment we cannot cache query plan if there is an
|
// At the moment we cannot cache query plan if there is an
|
||||||
// EntityGraph enabled.
|
// EntityGraph enabled.
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.query.sqm.sql.internal;
|
package org.hibernate.query.sqm.sql.internal;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
@ -19,15 +20,19 @@ import org.hibernate.NotYetImplementedFor6Exception;
|
||||||
import org.hibernate.engine.FetchTiming;
|
import org.hibernate.engine.FetchTiming;
|
||||||
import org.hibernate.engine.profile.FetchProfile;
|
import org.hibernate.engine.profile.FetchProfile;
|
||||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||||
|
import org.hibernate.internal.FilterHelper;
|
||||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
import org.hibernate.internal.util.collections.StandardStack;
|
import org.hibernate.internal.util.collections.StandardStack;
|
||||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
import org.hibernate.metamodel.mapping.ModelPart;
|
import org.hibernate.metamodel.mapping.ModelPart;
|
||||||
|
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||||
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
import org.hibernate.metamodel.mapping.PluralAttributeMapping;
|
||||||
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
|
import org.hibernate.metamodel.mapping.ordering.OrderByFragment;
|
||||||
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
import org.hibernate.metamodel.model.domain.EntityDomainType;
|
||||||
|
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||||
import org.hibernate.persister.entity.EntityPersister;
|
import org.hibernate.persister.entity.EntityPersister;
|
||||||
|
import org.hibernate.persister.entity.Joinable;
|
||||||
import org.hibernate.query.DynamicInstantiationNature;
|
import org.hibernate.query.DynamicInstantiationNature;
|
||||||
import org.hibernate.query.NavigablePath;
|
import org.hibernate.query.NavigablePath;
|
||||||
import org.hibernate.query.spi.QueryOptions;
|
import org.hibernate.query.spi.QueryOptions;
|
||||||
|
@ -57,6 +62,7 @@ import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
import org.hibernate.sql.ast.tree.from.TableGroupJoinProducer;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
|
||||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||||
import org.hibernate.sql.results.graph.DomainResult;
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
|
@ -89,6 +95,8 @@ public class StandardSqmSelectTranslator
|
||||||
|
|
||||||
private int fetchDepth;
|
private int fetchDepth;
|
||||||
|
|
||||||
|
private List<FilterPredicate> collectionFieldFilterPredicates;
|
||||||
|
|
||||||
public StandardSqmSelectTranslator(
|
public StandardSqmSelectTranslator(
|
||||||
QueryOptions queryOptions,
|
QueryOptions queryOptions,
|
||||||
DomainParameterXref domainParameterXref,
|
DomainParameterXref domainParameterXref,
|
||||||
|
@ -187,6 +195,24 @@ public class StandardSqmSelectTranslator
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void postProcessQuerySpec(QuerySpec sqlQuerySpec) {
|
protected void postProcessQuerySpec(QuerySpec sqlQuerySpec) {
|
||||||
|
final List<TableGroup> roots = sqlQuerySpec.getFromClause().getRoots();
|
||||||
|
if ( roots != null && roots.size() == 1 ) {
|
||||||
|
final TableGroup root = roots.get( 0 );
|
||||||
|
final ModelPartContainer modelPartContainer = root.getModelPart();
|
||||||
|
final EntityPersister entityPersister = modelPartContainer.findContainingEntityMapping().getEntityPersister();
|
||||||
|
assert entityPersister instanceof AbstractEntityPersister;
|
||||||
|
final String primaryTableAlias = root.getPrimaryTableReference().getIdentificationVariable();
|
||||||
|
final FilterPredicate filterPredicate = FilterHelper.createFilterPredicate(
|
||||||
|
fetchInfluencers, (AbstractEntityPersister) entityPersister, primaryTableAlias
|
||||||
|
);
|
||||||
|
if ( filterPredicate != null ) {
|
||||||
|
sqlQuerySpec.applyPredicate( filterPredicate );
|
||||||
|
}
|
||||||
|
if ( !CollectionHelper.isEmpty( collectionFieldFilterPredicates ) ) {
|
||||||
|
collectionFieldFilterPredicates.forEach( sqlQuerySpec::applyPredicate );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final OrderByFragmentConsumer orderByFragmentConsumer = orderByFragmentConsumerStack.getCurrent();
|
final OrderByFragmentConsumer orderByFragmentConsumer = orderByFragmentConsumerStack.getCurrent();
|
||||||
if ( orderByFragmentConsumer != null ) {
|
if ( orderByFragmentConsumer != null ) {
|
||||||
|
@ -384,10 +410,32 @@ public class StandardSqmSelectTranslator
|
||||||
StandardSqmSelectTranslator.this
|
StandardSqmSelectTranslator.this
|
||||||
);
|
);
|
||||||
|
|
||||||
final OrderByFragmentConsumer orderByFragmentConsumer = orderByFragmentConsumerStack.getCurrent();
|
if ( fetchable instanceof PluralAttributeMapping && fetch.getTiming() == FetchTiming.IMMEDIATE && joined ) {
|
||||||
if ( orderByFragmentConsumer != null ) {
|
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) fetchable;
|
||||||
if ( fetchable instanceof PluralAttributeMapping && fetch.getTiming() == FetchTiming.IMMEDIATE ) {
|
|
||||||
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) fetchable;
|
String tableAlias = alias;
|
||||||
|
if ( tableAlias == null ) {
|
||||||
|
tableAlias = getFromClauseAccess().getTableGroup( fetchablePath ).getPrimaryTableReference().getIdentificationVariable();
|
||||||
|
}
|
||||||
|
final Joinable joinable = pluralAttributeMapping
|
||||||
|
.getCollectionDescriptor()
|
||||||
|
.getCollectionType()
|
||||||
|
.getAssociatedJoinable( getCreationContext().getSessionFactory() );
|
||||||
|
final FilterPredicate collectionFieldFilterPredicate = FilterHelper.createFilterPredicate(
|
||||||
|
fetchInfluencers,
|
||||||
|
joinable,
|
||||||
|
tableAlias
|
||||||
|
);
|
||||||
|
if ( collectionFieldFilterPredicate != null ) {
|
||||||
|
if ( collectionFieldFilterPredicates == null ) {
|
||||||
|
collectionFieldFilterPredicates = new ArrayList<>();
|
||||||
|
}
|
||||||
|
collectionFieldFilterPredicates.add( collectionFieldFilterPredicate );
|
||||||
|
}
|
||||||
|
|
||||||
|
final OrderByFragmentConsumer orderByFragmentConsumer = orderByFragmentConsumerStack.getCurrent();
|
||||||
|
if ( orderByFragmentConsumer != null ) {
|
||||||
|
|
||||||
final TableGroup tableGroup = getFromClauseIndex().getTableGroup( fetchablePath );
|
final TableGroup tableGroup = getFromClauseIndex().getTableGroup( fetchablePath );
|
||||||
assert tableGroup.getModelPart() == pluralAttributeMapping;
|
assert tableGroup.getModelPart() == pluralAttributeMapping;
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ import org.hibernate.SortOrder;
|
||||||
import org.hibernate.dialect.Dialect;
|
import org.hibernate.dialect.Dialect;
|
||||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.internal.FilterJdbcParameter;
|
||||||
|
import org.hibernate.internal.util.StringHelper;
|
||||||
import org.hibernate.internal.util.collections.Stack;
|
import org.hibernate.internal.util.collections.Stack;
|
||||||
import org.hibernate.internal.util.collections.StandardStack;
|
import org.hibernate.internal.util.collections.StandardStack;
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||||
|
@ -70,11 +72,10 @@ import org.hibernate.sql.ast.tree.predicate.SelfRenderingPredicate;
|
||||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
import org.hibernate.sql.ast.tree.select.SelectClause;
|
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||||
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
import org.hibernate.sql.ast.tree.select.SortSpecification;
|
||||||
import org.hibernate.sql.exec.internal.AbstractJdbcParameter;
|
|
||||||
import org.hibernate.sql.exec.internal.JdbcParametersImpl;
|
import org.hibernate.sql.exec.internal.JdbcParametersImpl;
|
||||||
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||||
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
|
|
||||||
import org.hibernate.type.descriptor.sql.JdbcLiteralFormatter;
|
import org.hibernate.type.descriptor.sql.JdbcLiteralFormatter;
|
||||||
|
import org.hibernate.type.descriptor.sql.SqlTypeDescriptorIndicators;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
import static org.hibernate.query.TemporalUnit.NANOSECOND;
|
import static org.hibernate.query.TemporalUnit.NANOSECOND;
|
||||||
|
@ -98,10 +99,12 @@ public abstract class AbstractSqlAstWalker
|
||||||
|
|
||||||
// In-flight state
|
// In-flight state
|
||||||
private final StringBuilder sqlBuffer = new StringBuilder();
|
private final StringBuilder sqlBuffer = new StringBuilder();
|
||||||
private final List<JdbcParameterBinder> parameterBinders = new ArrayList<>();
|
|
||||||
|
|
||||||
|
private final List<JdbcParameterBinder> parameterBinders = new ArrayList<>();
|
||||||
private final JdbcParametersImpl jdbcParameters = new JdbcParametersImpl();
|
private final JdbcParametersImpl jdbcParameters = new JdbcParametersImpl();
|
||||||
|
|
||||||
|
protected final List<FilterJdbcParameter> filterJdbcParameters = new ArrayList<>();
|
||||||
|
|
||||||
private final Stack<Clause> clauseStack = new StandardStack<>();
|
private final Stack<Clause> clauseStack = new StandardStack<>();
|
||||||
|
|
||||||
private final Dialect dialect;
|
private final Dialect dialect;
|
||||||
|
@ -1034,12 +1037,12 @@ public abstract class AbstractSqlAstWalker
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visitFilterPredicate(FilterPredicate filterPredicate) {
|
public void visitFilterPredicate(FilterPredicate filterPredicate) {
|
||||||
if ( filterPredicate.getFilterFragment() != null ) {
|
assert StringHelper.isNotEmpty( filterPredicate.getFilterFragment() );
|
||||||
appendSql( filterPredicate.getFilterFragment() );
|
appendSql( filterPredicate.getFilterFragment() );
|
||||||
for (JdbcParameter jdbcParameter : filterPredicate.getJdbcParameters()) {
|
for ( FilterJdbcParameter filterJdbcParameter : filterPredicate.getFilterJdbcParameters() ) {
|
||||||
parameterBinders.add( (AbstractJdbcParameter) jdbcParameter );
|
parameterBinders.add( filterJdbcParameter.getBinder() );
|
||||||
jdbcParameters.addParameter( jdbcParameter );
|
jdbcParameters.addParameter( filterJdbcParameter.getParameter() );
|
||||||
}
|
filterJdbcParameters.add( filterJdbcParameter );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,8 @@ public class StandardSqlAstSelectTranslator
|
||||||
querySpec.getSelectClause().getSqlSelections(),
|
querySpec.getSelectClause().getSqlSelections(),
|
||||||
Collections.emptyList()
|
Collections.emptyList()
|
||||||
),
|
),
|
||||||
getAffectedTableNames()
|
getAffectedTableNames(),
|
||||||
|
filterJdbcParameters
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +94,8 @@ public class StandardSqlAstSelectTranslator
|
||||||
sqlAstSelect.getQuerySpec().getSelectClause().getSqlSelections(),
|
sqlAstSelect.getQuerySpec().getSelectClause().getSqlSelections(),
|
||||||
sqlAstSelect.getDomainResultDescriptors()
|
sqlAstSelect.getDomainResultDescriptors()
|
||||||
),
|
),
|
||||||
getAffectedTableNames()
|
getAffectedTableNames(),
|
||||||
|
filterJdbcParameters
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,13 +6,10 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.sql.ast.tree.predicate;
|
package org.hibernate.sql.ast.tree.predicate;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.hibernate.internal.FilterHelper;
|
import org.hibernate.internal.FilterJdbcParameter;
|
||||||
import org.hibernate.sql.ast.SqlAstWalker;
|
import org.hibernate.sql.ast.SqlAstWalker;
|
||||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
|
||||||
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a filter applied to an entity/collection.
|
* Represents a filter applied to an entity/collection.
|
||||||
|
@ -20,19 +17,15 @@ import org.hibernate.sql.exec.internal.JdbcParameterImpl;
|
||||||
* Note, we do not attempt to parse the filter
|
* Note, we do not attempt to parse the filter
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
|
* @author Nathan Xu
|
||||||
*/
|
*/
|
||||||
public class FilterPredicate implements Predicate {
|
public class FilterPredicate implements Predicate {
|
||||||
private final String filterFragment;
|
private final String filterFragment;
|
||||||
private final List<JdbcParameter> jdbcParameters;
|
private final List<FilterJdbcParameter> filterJdbcParameters;
|
||||||
private final List<FilterHelper.TypedValue> jdbcParameterTypedValues;
|
|
||||||
|
|
||||||
public FilterPredicate(String filterFragment, List<FilterHelper.TypedValue> jdbcParameterTypedValues) {
|
public FilterPredicate(String filterFragment, List<FilterJdbcParameter> filterJdbcParameters) {
|
||||||
this.filterFragment = filterFragment;
|
this.filterFragment = filterFragment;
|
||||||
jdbcParameters = new ArrayList<>( jdbcParameterTypedValues.size() );
|
this.filterJdbcParameters = filterJdbcParameters;
|
||||||
this.jdbcParameterTypedValues = jdbcParameterTypedValues;
|
|
||||||
for (int i = 0; i < jdbcParameterTypedValues.size(); i++) {
|
|
||||||
jdbcParameters.add( new JdbcParameterImpl( null ) );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -49,11 +42,7 @@ public class FilterPredicate implements Predicate {
|
||||||
return filterFragment;
|
return filterFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<JdbcParameter> getJdbcParameters() {
|
public List<FilterJdbcParameter> getFilterJdbcParameters() {
|
||||||
return jdbcParameters;
|
return filterJdbcParameters;
|
||||||
}
|
|
||||||
|
|
||||||
public List<FilterHelper.TypedValue> getJdbcParameterTypedValues() {
|
|
||||||
return jdbcParameterTypedValues;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,9 +10,6 @@ import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.HibernateException;
|
|
||||||
import org.hibernate.internal.FilterHelper;
|
|
||||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
|
||||||
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
import org.hibernate.metamodel.mapping.MappingModelExpressable;
|
||||||
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
import org.hibernate.query.sqm.sql.internal.DomainResultProducer;
|
||||||
import org.hibernate.sql.ast.SqlAstWalker;
|
import org.hibernate.sql.ast.SqlAstWalker;
|
||||||
|
@ -22,13 +19,9 @@ import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||||
import org.hibernate.sql.ast.tree.cte.CteConsumer;
|
import org.hibernate.sql.ast.tree.cte.CteConsumer;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
|
||||||
import org.hibernate.sql.ast.tree.from.FromClause;
|
import org.hibernate.sql.ast.tree.from.FromClause;
|
||||||
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
|
|
||||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||||
import org.hibernate.sql.ast.tree.predicate.PredicateContainer;
|
import org.hibernate.sql.ast.tree.predicate.PredicateContainer;
|
||||||
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
|
||||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
|
||||||
import org.hibernate.sql.results.graph.DomainResult;
|
import org.hibernate.sql.results.graph.DomainResult;
|
||||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||||
|
@ -45,7 +38,6 @@ public class QuerySpec implements SqlAstNode, PredicateContainer, Expression, Ct
|
||||||
private final SelectClause selectClause = new SelectClause();
|
private final SelectClause selectClause = new SelectClause();
|
||||||
|
|
||||||
private Predicate whereClauseRestrictions;
|
private Predicate whereClauseRestrictions;
|
||||||
private List<FilterPredicate> filterPredicates;
|
|
||||||
private List<SortSpecification> sortSpecifications;
|
private List<SortSpecification> sortSpecifications;
|
||||||
private Expression limitClauseExpression;
|
private Expression limitClauseExpression;
|
||||||
private Expression offsetClauseExpression;
|
private Expression offsetClauseExpression;
|
||||||
|
@ -85,13 +77,6 @@ public class QuerySpec implements SqlAstNode, PredicateContainer, Expression, Ct
|
||||||
this.whereClauseRestrictions = SqlAstTreeHelper.combinePredicates( this.whereClauseRestrictions, predicate );
|
this.whereClauseRestrictions = SqlAstTreeHelper.combinePredicates( this.whereClauseRestrictions, predicate );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addFilterPredicate(FilterPredicate filterPredicate) {
|
|
||||||
if ( filterPredicates == null ) {
|
|
||||||
filterPredicates = new ArrayList<>();
|
|
||||||
}
|
|
||||||
filterPredicates.add( filterPredicate );
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<SortSpecification> getSortSpecifications() {
|
public List<SortSpecification> getSortSpecifications() {
|
||||||
return sortSpecifications;
|
return sortSpecifications;
|
||||||
}
|
}
|
||||||
|
@ -170,32 +155,4 @@ public class QuerySpec implements SqlAstNode, PredicateContainer, Expression, Ct
|
||||||
descriptor
|
descriptor
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void bindFilterPredicateParameters(JdbcParameterBindings jdbcParameterBindings) {
|
|
||||||
if ( filterPredicates != null && !filterPredicates.isEmpty() ) {
|
|
||||||
for ( FilterPredicate filterPredicate : filterPredicates ) {
|
|
||||||
for ( int i = 0; i < filterPredicate.getJdbcParameters().size(); i++ ) {
|
|
||||||
final JdbcParameter parameter = filterPredicate.getJdbcParameters().get( i );
|
|
||||||
final FilterHelper.TypedValue parameterTypedValue = filterPredicate.getJdbcParameterTypedValues().get( i );
|
|
||||||
if ( !(parameterTypedValue.getType() instanceof JdbcMapping ) ) {
|
|
||||||
throw new HibernateException( String.format( "Filter parameter type [%s] did not implement JdbcMapping", parameterTypedValue.getType() ) );
|
|
||||||
}
|
|
||||||
jdbcParameterBindings.addBinding(
|
|
||||||
parameter,
|
|
||||||
new JdbcParameterBinding() {
|
|
||||||
@Override
|
|
||||||
public JdbcMapping getBindType() {
|
|
||||||
return (JdbcMapping) parameterTypedValue.getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getBindValue() {
|
|
||||||
return parameterTypedValue.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ package org.hibernate.sql.exec.spi;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.hibernate.internal.FilterJdbcParameter;
|
||||||
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
|
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -21,16 +23,19 @@ public class JdbcSelect implements JdbcOperation {
|
||||||
private final List<JdbcParameterBinder> parameterBinders;
|
private final List<JdbcParameterBinder> parameterBinders;
|
||||||
private final JdbcValuesMappingProducer jdbcValuesMappingProducer;
|
private final JdbcValuesMappingProducer jdbcValuesMappingProducer;
|
||||||
private final Set<String> affectedTableNames;
|
private final Set<String> affectedTableNames;
|
||||||
|
private final List<FilterJdbcParameter> filterJdbcParameters;
|
||||||
|
|
||||||
public JdbcSelect(
|
public JdbcSelect(
|
||||||
String sql,
|
String sql,
|
||||||
List<JdbcParameterBinder> parameterBinders,
|
List<JdbcParameterBinder> parameterBinders,
|
||||||
JdbcValuesMappingProducer jdbcValuesMappingProducer,
|
JdbcValuesMappingProducer jdbcValuesMappingProducer,
|
||||||
Set<String> affectedTableNames) {
|
Set<String> affectedTableNames,
|
||||||
|
List<FilterJdbcParameter> filterJdbcParameters) {
|
||||||
this.sql = sql;
|
this.sql = sql;
|
||||||
this.parameterBinders = parameterBinders;
|
this.parameterBinders = parameterBinders;
|
||||||
this.jdbcValuesMappingProducer = jdbcValuesMappingProducer;
|
this.jdbcValuesMappingProducer = jdbcValuesMappingProducer;
|
||||||
this.affectedTableNames = affectedTableNames;
|
this.affectedTableNames = affectedTableNames;
|
||||||
|
this.filterJdbcParameters = filterJdbcParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,4 +56,12 @@ public class JdbcSelect implements JdbcOperation {
|
||||||
public JdbcValuesMappingProducer getJdbcValuesMappingProducer() {
|
public JdbcValuesMappingProducer getJdbcValuesMappingProducer() {
|
||||||
return jdbcValuesMappingProducer;
|
return jdbcValuesMappingProducer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerFilterJdbcParameterBindings(JdbcParameterBindings jdbcParameterBindings) {
|
||||||
|
if ( CollectionHelper.isNotEmpty( filterJdbcParameters ) ) {
|
||||||
|
for ( FilterJdbcParameter filterJdbcParameter : filterJdbcParameters ) {
|
||||||
|
jdbcParameterBindings.addBinding( filterJdbcParameter.getParameter(), filterJdbcParameter.getBinding() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,300 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.criteria.filter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Filter;
|
||||||
|
import org.hibernate.annotations.FilterDef;
|
||||||
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScopeAware;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nathan Xu
|
||||||
|
*/
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = {
|
||||||
|
FilterBasicsTests.Client.class,
|
||||||
|
FilterBasicsTests.Account.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class FilterBasicsTests implements SessionFactoryScopeAware {
|
||||||
|
|
||||||
|
private SessionFactoryScope scope;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectSessionFactoryScope(SessionFactoryScope scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
|
||||||
|
// ensure query plan cache won't interfere
|
||||||
|
scope.getSessionFactory().getQueryEngine().getInterpretationCache().close();
|
||||||
|
|
||||||
|
Client client = new Client()
|
||||||
|
.setId( 1L )
|
||||||
|
.setName( "John Doe" );
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 1L )
|
||||||
|
.setType( AccountType.CREDIT )
|
||||||
|
.setAmount( 5000d )
|
||||||
|
.setRate( 1.25 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 2L )
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setAmount( 0d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( false )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setId( 3L )
|
||||||
|
.setAmount( 250d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
session.persist( client );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource( strings = { "true", "false" } )
|
||||||
|
void testLoadFilterOnEntity(boolean enableFilter) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
if ( enableFilter ) {
|
||||||
|
session.enableFilter( "activeAccount" ).setParameter( "active", true );
|
||||||
|
}
|
||||||
|
final CriteriaBuilder criteriaBuilder = scope.getSessionFactory().getCriteriaBuilder();
|
||||||
|
|
||||||
|
final CriteriaQuery<Account> criteriaQuery1 = createCriteriaQuery( criteriaBuilder, Account.class, "id", 1L );
|
||||||
|
Account account1 = session.createQuery( criteriaQuery1 ).uniqueResult();
|
||||||
|
assertThat( account1, notNullValue() );
|
||||||
|
|
||||||
|
final CriteriaQuery<Account> criteriaQuery2 = createCriteriaQuery( criteriaBuilder, Account.class, "id", 2L );
|
||||||
|
Account account2 = session.createQuery( criteriaQuery2 ).uniqueResult();
|
||||||
|
assertThat( account2, enableFilter ? nullValue() : notNullValue() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource( strings = { "true", "false" } )
|
||||||
|
void testLoadFilterOnCollectionField(boolean enableFilter) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
if ( enableFilter ) {
|
||||||
|
session.enableFilter( "activeAccount" ).setParameter( "active", true );
|
||||||
|
}
|
||||||
|
|
||||||
|
final CriteriaBuilder criteriaBuilder = scope.getSessionFactory().getCriteriaBuilder();
|
||||||
|
final CriteriaQuery<Client> criteriaQuery = createCriteriaQuery( criteriaBuilder, Client.class, "id", 1L );
|
||||||
|
final Client client = session.createQuery(criteriaQuery).uniqueResult();
|
||||||
|
|
||||||
|
if ( enableFilter ) {
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 2L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
session.createQuery( "delete from Account" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from Client" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AccountType {
|
||||||
|
DEBIT,
|
||||||
|
CREDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Client")
|
||||||
|
public static class Client {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@OneToMany(
|
||||||
|
mappedBy = "client",
|
||||||
|
cascade = CascadeType.ALL
|
||||||
|
)
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="active_status = :active"
|
||||||
|
)
|
||||||
|
private List<Account> accounts = new ArrayList<>();
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Account> getAccounts() {
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAccount(Account account) {
|
||||||
|
account.setClient( this );
|
||||||
|
this.accounts.add( account );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Account")
|
||||||
|
@FilterDef(
|
||||||
|
name="activeAccount",
|
||||||
|
parameters = @ParamDef(
|
||||||
|
name="active",
|
||||||
|
type="boolean"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="active_status = :active"
|
||||||
|
)
|
||||||
|
public static class Account {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Column(name = "account_type")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
|
private Double amount;
|
||||||
|
|
||||||
|
private Double rate;
|
||||||
|
|
||||||
|
@Column(name = "active_status")
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setClient(Client client) {
|
||||||
|
this.client = client;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setAmount(Double amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setRate(Double rate) {
|
||||||
|
this.rate = rate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setActive(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> CriteriaQuery<T> createCriteriaQuery(CriteriaBuilder criteriaBuilder, Class<T> entityClass, String idFieldName, Object idValue) {
|
||||||
|
final CriteriaQuery<T> criteria = criteriaBuilder.createQuery( entityClass );
|
||||||
|
Root<T> root = criteria.from( entityClass );
|
||||||
|
criteria.select( root );
|
||||||
|
criteria.where( criteriaBuilder.equal( root.get( idFieldName ), criteriaBuilder.literal( idValue ) ) );
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,218 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.criteria.filter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinTable;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.OrderColumn;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.FilterDef;
|
||||||
|
import org.hibernate.annotations.FilterJoinTable;
|
||||||
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
|
||||||
|
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.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nathan Xu
|
||||||
|
*/
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = {
|
||||||
|
FilterJoinTableTests.Client.class,
|
||||||
|
FilterJoinTableTests.Account.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class FilterJoinTableTests {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
|
||||||
|
// ensure query plan cache won't interfere
|
||||||
|
scope.getSessionFactory().getQueryEngine().getInterpretationCache().close();
|
||||||
|
|
||||||
|
Client client = new Client()
|
||||||
|
.setId( 1L )
|
||||||
|
.setName( "John Doe" );
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 1L )
|
||||||
|
.setType( AccountType.CREDIT )
|
||||||
|
.setAmount( 5000d )
|
||||||
|
.setRate( 1.25 / 100 )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 2L )
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setAmount( 0d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setId( 3L )
|
||||||
|
.setAmount( 250d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
);
|
||||||
|
|
||||||
|
session.persist( client );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testLoadFilterOnCollectionField(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
session.enableFilter( "firstAccounts" ).setParameter( "maxOrderId", 1);
|
||||||
|
|
||||||
|
final CriteriaBuilder criteriaBuilder = scope.getSessionFactory().getCriteriaBuilder();
|
||||||
|
final CriteriaQuery<Client> criteriaQuery = createCriteriaQuery( criteriaBuilder, Client.class, "id", 1L );
|
||||||
|
final Client client = session.createQuery( criteriaQuery ).uniqueResult();
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 2L ) ) ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AccountType {
|
||||||
|
DEBIT,
|
||||||
|
CREDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Client")
|
||||||
|
@FilterDef(
|
||||||
|
name="firstAccounts",
|
||||||
|
parameters=@ParamDef(
|
||||||
|
name="maxOrderId",
|
||||||
|
type="int"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public static class Client {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToMany(cascade = CascadeType.ALL)
|
||||||
|
@JoinTable
|
||||||
|
@OrderColumn(name = "order_id")
|
||||||
|
@FilterJoinTable(
|
||||||
|
name="firstAccounts",
|
||||||
|
condition="order_id <= :maxOrderId"
|
||||||
|
)
|
||||||
|
private List<Account> accounts = new ArrayList<>();
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Account> getAccounts() {
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAccount(Account account) {
|
||||||
|
this.accounts.add( account );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Account")
|
||||||
|
public static class Account {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "account_type")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
|
private Double amount;
|
||||||
|
|
||||||
|
private Double rate;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setAmount(Double amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setRate(Double rate) {
|
||||||
|
this.rate = rate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> CriteriaQuery<T> createCriteriaQuery(CriteriaBuilder criteriaBuilder, Class<T> entityClass, String idFieldName, Object idValue) {
|
||||||
|
final CriteriaQuery<T> criteria = criteriaBuilder.createQuery( entityClass );
|
||||||
|
Root<T> root = criteria.from( entityClass );
|
||||||
|
criteria.select( root );
|
||||||
|
criteria.where( criteriaBuilder.equal( root.get( idFieldName ), criteriaBuilder.literal( idValue ) ) );
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,278 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.criteria.filter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Filter;
|
||||||
|
import org.hibernate.annotations.FilterDef;
|
||||||
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScopeAware;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nathan Xu
|
||||||
|
*/
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = {
|
||||||
|
FilterOnJoinFetchedCollectionTests.Client.class,
|
||||||
|
FilterOnJoinFetchedCollectionTests.Account.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class FilterOnJoinFetchedCollectionTests implements SessionFactoryScopeAware {
|
||||||
|
|
||||||
|
private SessionFactoryScope scope;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectSessionFactoryScope(SessionFactoryScope scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
|
||||||
|
// ensure query plan cache won't interfere
|
||||||
|
scope.getSessionFactory().getQueryEngine().getInterpretationCache().close();
|
||||||
|
|
||||||
|
Client client = new Client()
|
||||||
|
.setId( 1L )
|
||||||
|
.setName( "John Doe" );
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 1L )
|
||||||
|
.setType( AccountType.CREDIT )
|
||||||
|
.setAmount( 5000d )
|
||||||
|
.setRate( 1.25 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 2L )
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setAmount( 0d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( false )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setId( 3L )
|
||||||
|
.setAmount( 250d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
session.persist( client );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource( strings = { "true", "false" } )
|
||||||
|
void testJoinFetchedCollectionField(boolean enableFilter) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
if ( enableFilter ) {
|
||||||
|
session.enableFilter( "activeAccount" ).setParameter( "active", true );
|
||||||
|
}
|
||||||
|
final CriteriaBuilder criteriaBuilder = scope.getSessionFactory().getCriteriaBuilder();
|
||||||
|
final CriteriaQuery<Client> criteriaQuery = createCriteriaQuery( criteriaBuilder, Client.class, "id", 1L );
|
||||||
|
final Client client = session.createQuery( criteriaQuery ).uniqueResult();
|
||||||
|
|
||||||
|
if ( enableFilter ) {
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 2L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
session.createQuery( "delete from Account" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from Client" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AccountType {
|
||||||
|
DEBIT,
|
||||||
|
CREDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Client")
|
||||||
|
public static class Client {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@OneToMany(
|
||||||
|
mappedBy = "client",
|
||||||
|
cascade = CascadeType.ALL
|
||||||
|
)
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="active_status = :active"
|
||||||
|
)
|
||||||
|
private List<Account> accounts = new ArrayList<>();
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Account> getAccounts() {
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAccount(Account account) {
|
||||||
|
account.setClient( this );
|
||||||
|
this.accounts.add( account );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Account")
|
||||||
|
@FilterDef(
|
||||||
|
name="activeAccount",
|
||||||
|
parameters = @ParamDef(
|
||||||
|
name="active",
|
||||||
|
type="boolean"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="active_status = :active"
|
||||||
|
)
|
||||||
|
public static class Account {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Column(name = "account_type")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
|
private Double amount;
|
||||||
|
|
||||||
|
private Double rate;
|
||||||
|
|
||||||
|
@Column(name = "active_status")
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setClient(Client client) {
|
||||||
|
this.client = client;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setAmount(Double amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setRate(Double rate) {
|
||||||
|
this.rate = rate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setActive(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> CriteriaQuery<T> createCriteriaQuery(CriteriaBuilder criteriaBuilder, Class<T> entityClass, String idFieldName, Object idValue) {
|
||||||
|
final CriteriaQuery<T> criteria = criteriaBuilder.createQuery( entityClass );
|
||||||
|
Root<T> root = criteria.from( entityClass );
|
||||||
|
criteria.select( root );
|
||||||
|
criteria.where( criteriaBuilder.equal( root.get( idFieldName ), criteriaBuilder.literal( idValue ) ) );
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,291 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.criteria.filter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Filter;
|
||||||
|
import org.hibernate.annotations.FilterDef;
|
||||||
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
import org.hibernate.annotations.SqlFragmentAlias;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScopeAware;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nathan Xu
|
||||||
|
*/
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = {
|
||||||
|
FilterWithSqlFragmentAliasTests.Client.class,
|
||||||
|
FilterWithSqlFragmentAliasTests.Account.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class FilterWithSqlFragmentAliasTests implements SessionFactoryScopeAware {
|
||||||
|
|
||||||
|
private SessionFactoryScope scope;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectSessionFactoryScope(SessionFactoryScope scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
// ensure query plan cache won't interfere
|
||||||
|
scope.getSessionFactory().getQueryEngine().getInterpretationCache().close();
|
||||||
|
|
||||||
|
Client client = new Client()
|
||||||
|
.setId( 1L )
|
||||||
|
.setName( "John Doe" );
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 1L )
|
||||||
|
.setType( AccountType.CREDIT )
|
||||||
|
.setAmount( 5000d )
|
||||||
|
.setRate( 1.25 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 2L )
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setAmount( 0d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( false )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setId( 3L )
|
||||||
|
.setAmount( 250d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
session.persist( client );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource( strings = { "true", "false" } )
|
||||||
|
void testLoadFilterOnCollectionField(boolean enableFilter) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
if ( enableFilter ) {
|
||||||
|
session.enableFilter( "activeAccount" ).setParameter( "active", true );
|
||||||
|
}
|
||||||
|
|
||||||
|
final CriteriaBuilder criteriaBuilder = scope.getSessionFactory().getCriteriaBuilder();
|
||||||
|
final CriteriaQuery<Client> criteriaQuery = createCriteriaQuery( criteriaBuilder, Client.class, "id", 1L );
|
||||||
|
final Client client = session.createQuery( criteriaQuery ).uniqueResult();
|
||||||
|
|
||||||
|
if ( enableFilter ) {
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 2L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
session.createQuery( "delete from Account" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from Client" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AccountType {
|
||||||
|
DEBIT,
|
||||||
|
CREDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Client")
|
||||||
|
@Table(name = "client")
|
||||||
|
public static class Client {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
|
@OneToMany(
|
||||||
|
mappedBy = "client",
|
||||||
|
cascade = CascadeType.ALL
|
||||||
|
)
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="{a}.active_status = :active",
|
||||||
|
aliases = {
|
||||||
|
@SqlFragmentAlias( alias = "a", table= "account")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private List<Account> accounts = new ArrayList<>();
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Account> getAccounts() {
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAccount(Account account) {
|
||||||
|
account.setClient( this );
|
||||||
|
this.accounts.add( account );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Account")
|
||||||
|
@Table(name = "account")
|
||||||
|
@FilterDef(
|
||||||
|
name="activeAccount",
|
||||||
|
parameters = @ParamDef(
|
||||||
|
name="active",
|
||||||
|
type="boolean"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public static class Account {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Column(name = "account_type")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
|
private Double amount;
|
||||||
|
|
||||||
|
private Double rate;
|
||||||
|
|
||||||
|
@Column(name = "active_status")
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setClient(Client client) {
|
||||||
|
this.client = client;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setAmount(Double amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setRate(Double rate) {
|
||||||
|
this.rate = rate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setActive(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> CriteriaQuery<T> createCriteriaQuery(CriteriaBuilder criteriaBuilder, Class<T> entityClass, String idFieldName, Object idValue) {
|
||||||
|
final CriteriaQuery<T> criteria = criteriaBuilder.createQuery( entityClass );
|
||||||
|
Root<T> root = criteria.from( entityClass );
|
||||||
|
criteria.select( root );
|
||||||
|
criteria.where( criteriaBuilder.equal( root.get( idFieldName ), criteriaBuilder.literal( idValue ) ) );
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,284 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.hql.filter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Filter;
|
||||||
|
import org.hibernate.annotations.FilterDef;
|
||||||
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScopeAware;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||||
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nathan Xu
|
||||||
|
*/
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = {
|
||||||
|
FilterBasicsTests.Client.class,
|
||||||
|
FilterBasicsTests.Account.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class FilterBasicsTests implements SessionFactoryScopeAware {
|
||||||
|
|
||||||
|
private SessionFactoryScope scope;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectSessionFactoryScope(SessionFactoryScope scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
// ensure query plan cache won't interfere
|
||||||
|
scope.getSessionFactory().getQueryEngine().getInterpretationCache().close();
|
||||||
|
|
||||||
|
Client client = new Client()
|
||||||
|
.setId( 1L )
|
||||||
|
.setName( "John Doe" );
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 1L )
|
||||||
|
.setType( AccountType.CREDIT )
|
||||||
|
.setAmount( 5000d )
|
||||||
|
.setRate( 1.25 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 2L )
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setAmount( 0d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( false )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setId( 3L )
|
||||||
|
.setAmount( 250d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
session.persist( client );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource( strings = { "true", "false" } )
|
||||||
|
void testLoadFilterOnEntity(boolean enableFilter) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
if ( enableFilter ) {
|
||||||
|
session.enableFilter( "activeAccount" ).setParameter( "active", true );
|
||||||
|
}
|
||||||
|
final String hqlString = "select a from Account a where a.id = :id";
|
||||||
|
final Account account1 = session.createQuery( hqlString, Account.class )
|
||||||
|
.setParameter( "id", 1L ).uniqueResult();
|
||||||
|
final Account account2 = session.createQuery( hqlString, Account.class )
|
||||||
|
.setParameter( "id", 2L ).uniqueResult();
|
||||||
|
assertThat( account1, notNullValue() );
|
||||||
|
assertThat( account2, enableFilter ? nullValue() : notNullValue() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource( strings = { "true", "false" } )
|
||||||
|
void testLoadFilterOnCollectionField(boolean enableFilter) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
if ( enableFilter ) {
|
||||||
|
session.enableFilter( "activeAccount" ).setParameter( "active", true );
|
||||||
|
}
|
||||||
|
Client client = session.createQuery( "select c from Client c where c.id = :id", Client.class )
|
||||||
|
.setParameter( "id", 1L ).uniqueResult();
|
||||||
|
|
||||||
|
if ( enableFilter ) {
|
||||||
|
assertThat( client.getAccounts().stream().map(Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertThat( client.getAccounts().stream().map(Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 2L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
session.createQuery( "delete from Account" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from Client" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AccountType {
|
||||||
|
DEBIT,
|
||||||
|
CREDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Client")
|
||||||
|
public static class Client {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@OneToMany(
|
||||||
|
mappedBy = "client",
|
||||||
|
cascade = CascadeType.ALL
|
||||||
|
)
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="active_status = :active"
|
||||||
|
)
|
||||||
|
private List<Account> accounts = new ArrayList<>();
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Account> getAccounts() {
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAccount(Account account) {
|
||||||
|
account.setClient( this );
|
||||||
|
this.accounts.add( account );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Account")
|
||||||
|
@FilterDef(
|
||||||
|
name="activeAccount",
|
||||||
|
parameters = @ParamDef(
|
||||||
|
name="active",
|
||||||
|
type="boolean"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="active_status = :active"
|
||||||
|
)
|
||||||
|
public static class Account {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Column(name = "account_type")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
|
private Double amount;
|
||||||
|
|
||||||
|
private Double rate;
|
||||||
|
|
||||||
|
@Column(name = "active_status")
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setClient(Client client) {
|
||||||
|
this.client = client;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setAmount(Double amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setRate(Double rate) {
|
||||||
|
this.rate = rate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setActive(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.hql.filter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinTable;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.OrderColumn;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.FilterDef;
|
||||||
|
import org.hibernate.annotations.FilterJoinTable;
|
||||||
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
|
||||||
|
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.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nathan Xu
|
||||||
|
*/
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = {
|
||||||
|
FilterJoinTableTests.Client.class,
|
||||||
|
FilterJoinTableTests.Account.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class FilterJoinTableTests {
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
Client client = new Client()
|
||||||
|
.setId( 1L )
|
||||||
|
.setName( "John Doe" );
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 1L )
|
||||||
|
.setType( AccountType.CREDIT )
|
||||||
|
.setAmount( 5000d )
|
||||||
|
.setRate( 1.25 / 100 )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 2L )
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setAmount( 0d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setId( 3L )
|
||||||
|
.setAmount( 250d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
);
|
||||||
|
|
||||||
|
session.persist( client );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testFilterJoinableOnCollectionField(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
session.enableFilter( "firstAccounts" ).setParameter( "maxOrderId", 1 );
|
||||||
|
final Client client = session.createQuery( "select c from Client c where c.id = :id", Client.class )
|
||||||
|
.setParameter( "id", 1L ).uniqueResult();
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 2L ) ) ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AccountType {
|
||||||
|
DEBIT,
|
||||||
|
CREDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Client")
|
||||||
|
@FilterDef(
|
||||||
|
name="firstAccounts",
|
||||||
|
parameters=@ParamDef(
|
||||||
|
name="maxOrderId",
|
||||||
|
type="int"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public static class Client {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@ManyToMany(cascade = CascadeType.ALL)
|
||||||
|
@JoinTable
|
||||||
|
@OrderColumn(name = "order_id")
|
||||||
|
@FilterJoinTable(
|
||||||
|
name="firstAccounts",
|
||||||
|
condition="order_id <= :maxOrderId"
|
||||||
|
)
|
||||||
|
private List<Account> accounts = new ArrayList<>();
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Account> getAccounts() {
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAccount(Account account) {
|
||||||
|
this.accounts.add( account );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Account")
|
||||||
|
public static class Account {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Column(name = "account_type")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
|
private Double amount;
|
||||||
|
|
||||||
|
private Double rate;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setAmount(Double amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setRate(Double rate) {
|
||||||
|
this.rate = rate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.hql.filter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Filter;
|
||||||
|
import org.hibernate.annotations.FilterDef;
|
||||||
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScopeAware;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nathan Xu
|
||||||
|
*/
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = {
|
||||||
|
FilterOnJoinFetchedCollectionTests.Client.class,
|
||||||
|
FilterOnJoinFetchedCollectionTests.Account.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class FilterOnJoinFetchedCollectionTests implements SessionFactoryScopeAware {
|
||||||
|
|
||||||
|
private SessionFactoryScope scope;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectSessionFactoryScope(SessionFactoryScope scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
Client client = new Client()
|
||||||
|
.setId( 1L )
|
||||||
|
.setName( "John Doe" );
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 1L )
|
||||||
|
.setType( AccountType.CREDIT )
|
||||||
|
.setAmount( 5000d )
|
||||||
|
.setRate( 1.25 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 2L )
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setAmount( 0d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( false )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setId( 3L )
|
||||||
|
.setAmount( 250d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
session.persist( client );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource( strings = { "true", "false" } )
|
||||||
|
void testLoadFilterOnCollectionField(boolean enableFilter) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
if ( enableFilter ) {
|
||||||
|
session.enableFilter( "activeAccount" ).setParameter( "active", true );
|
||||||
|
}
|
||||||
|
final Client client = session.createQuery( "select c from Client c join fetch c.accounts where c.id = :id", Client.class )
|
||||||
|
.setParameter( "id", 1L ).uniqueResult();
|
||||||
|
|
||||||
|
if ( enableFilter ) {
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 2L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
session.createQuery( "delete from Account" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from Client" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AccountType {
|
||||||
|
DEBIT,
|
||||||
|
CREDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Client")
|
||||||
|
public static class Client {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@OneToMany(
|
||||||
|
mappedBy = "client",
|
||||||
|
cascade = CascadeType.ALL
|
||||||
|
)
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="active_status = :active"
|
||||||
|
)
|
||||||
|
private List<Account> accounts = new ArrayList<>();
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Account> getAccounts() {
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAccount(Account account) {
|
||||||
|
account.setClient( this );
|
||||||
|
this.accounts.add( account );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Account")
|
||||||
|
@FilterDef(
|
||||||
|
name="activeAccount",
|
||||||
|
parameters = @ParamDef(
|
||||||
|
name="active",
|
||||||
|
type="boolean"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="active_status = :active"
|
||||||
|
)
|
||||||
|
public static class Account {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Column(name = "account_type")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
|
private Double amount;
|
||||||
|
|
||||||
|
private Double rate;
|
||||||
|
|
||||||
|
@Column(name = "active_status")
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setClient(Client client) {
|
||||||
|
this.client = client;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setAmount(Double amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setRate(Double rate) {
|
||||||
|
this.rate = rate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setActive(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,279 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||||
|
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.orm.test.query.hql.filter;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import javax.persistence.CascadeType;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.EnumType;
|
||||||
|
import javax.persistence.Enumerated;
|
||||||
|
import javax.persistence.FetchType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
|
import org.hibernate.annotations.Filter;
|
||||||
|
import org.hibernate.annotations.FilterDef;
|
||||||
|
import org.hibernate.annotations.ParamDef;
|
||||||
|
import org.hibernate.annotations.SqlFragmentAlias;
|
||||||
|
|
||||||
|
import org.hibernate.testing.orm.junit.DomainModel;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||||
|
import org.hibernate.testing.orm.junit.SessionFactoryScopeAware;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Nathan Xu
|
||||||
|
*/
|
||||||
|
@DomainModel(
|
||||||
|
annotatedClasses = {
|
||||||
|
FilterWithSqlFragmentAliasTests.Client.class,
|
||||||
|
FilterWithSqlFragmentAliasTests.Account.class
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@SessionFactory
|
||||||
|
public class FilterWithSqlFragmentAliasTests implements SessionFactoryScopeAware {
|
||||||
|
|
||||||
|
private SessionFactoryScope scope;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void injectSessionFactoryScope(SessionFactoryScope scope) {
|
||||||
|
this.scope = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
// ensure query plan cache won't interfere
|
||||||
|
scope.getSessionFactory().getQueryEngine().getInterpretationCache().close();
|
||||||
|
|
||||||
|
Client client = new Client()
|
||||||
|
.setId( 1L )
|
||||||
|
.setName( "John Doe" )
|
||||||
|
.setType( AccountType.DEBIT );
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 1L )
|
||||||
|
.setType( AccountType.CREDIT )
|
||||||
|
.setAmount( 5000d )
|
||||||
|
.setRate( 1.25 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setId( 2L )
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setAmount( 0d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( false )
|
||||||
|
);
|
||||||
|
|
||||||
|
client.addAccount(
|
||||||
|
new Account()
|
||||||
|
.setType( AccountType.DEBIT )
|
||||||
|
.setId( 3L )
|
||||||
|
.setAmount( 250d )
|
||||||
|
.setRate( 1.05 / 100 )
|
||||||
|
.setActive( true )
|
||||||
|
);
|
||||||
|
session.persist( client );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@ValueSource( strings = { "true", "false" } )
|
||||||
|
void testSqlFragmentAlias(boolean enableFilter) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
if ( enableFilter ) {
|
||||||
|
session.enableFilter( "activeAccount" ).setParameter( "active", true );
|
||||||
|
}
|
||||||
|
Client client = session.createQuery( "select c from Client c where c.id = :id", Client.class )
|
||||||
|
.setParameter( "id", 1L ).uniqueResult();
|
||||||
|
|
||||||
|
if ( enableFilter ) {
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assertThat( client.getAccounts().stream().map( Account::getId ).collect( Collectors.toSet() ),
|
||||||
|
equalTo( new HashSet<>( Arrays.asList( 1L, 2L, 3L ) ) ) );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
void tearDown(SessionFactoryScope scope) {
|
||||||
|
scope.inTransaction( session -> {
|
||||||
|
session.createQuery( "delete from Account" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from Client" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum AccountType {
|
||||||
|
DEBIT,
|
||||||
|
CREDIT
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Client")
|
||||||
|
@Table(name = "client")
|
||||||
|
public static class Client {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
|
@OneToMany(
|
||||||
|
mappedBy = "client",
|
||||||
|
cascade = CascadeType.ALL
|
||||||
|
)
|
||||||
|
@Filter(
|
||||||
|
name="activeAccount",
|
||||||
|
condition="{a}.active_status = :active",
|
||||||
|
aliases = {
|
||||||
|
@SqlFragmentAlias( alias = "a", table= "account")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
private List<Account> accounts = new ArrayList<>();
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Account> getAccounts() {
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAccount(Account account) {
|
||||||
|
account.setClient( this );
|
||||||
|
this.accounts.add( account );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Account")
|
||||||
|
@Table(name = "account")
|
||||||
|
@FilterDef(
|
||||||
|
name="activeAccount",
|
||||||
|
parameters = @ParamDef(
|
||||||
|
name="active",
|
||||||
|
type="boolean"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
public static class Account {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
private Client client;
|
||||||
|
|
||||||
|
@Column(name = "account_type")
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
private AccountType type;
|
||||||
|
|
||||||
|
private Double amount;
|
||||||
|
|
||||||
|
private Double rate;
|
||||||
|
|
||||||
|
@Column(name = "active_status")
|
||||||
|
private boolean active;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client getClient() {
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setClient(Client client) {
|
||||||
|
this.client = client;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AccountType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setType(AccountType type) {
|
||||||
|
this.type = type;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getAmount() {
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setAmount(Double amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setRate(Double rate) {
|
||||||
|
this.rate = rate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Account setActive(boolean active) {
|
||||||
|
this.active = active;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue