Skip to content

Commit

Permalink
implement @filter for HQL/Criteria
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanQingyangXu authored and sebersole committed Apr 21, 2020
1 parent 709e7d4 commit 686a519
Show file tree
Hide file tree
Showing 24 changed files with 2,338 additions and 192 deletions.
Expand Up @@ -18,16 +18,13 @@
import javax.persistence.OrderColumn;

import org.hibernate.Session;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.FilterJoinTable;
import org.hibernate.annotations.ParamDef;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;

import org.junit.Test;

import org.jboss.logging.Logger;

import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;

Expand Down Expand Up @@ -118,10 +115,6 @@ public enum AccountType {
type="int"
)
)
@Filter(
name="firstAccounts",
condition="order_id <= :maxOrderId"
)
public static class Client {

@Id
Expand Down
Expand Up @@ -18,11 +18,13 @@
import javax.persistence.ManyToOne;
import javax.persistence.NoResultException;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.Session;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.ParamDef;
import org.hibernate.annotations.SqlFragmentAlias;
import org.hibernate.annotations.Where;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;

Expand Down Expand Up @@ -56,7 +58,8 @@ public void testLifecycle() {
//tag::pc-filter-persistence-example[]
Client client = new Client()
.setId( 1L )
.setName( "John Doe" );
.setName( "John Doe" )
.setType( AccountType.DEBIT );

client.addAccount(
new Account()
Expand Down Expand Up @@ -186,7 +189,7 @@ public void testLifecycle() {

Client client = entityManager.find( Client.class, 1L );

assertEquals( 2, client.getAccounts().size() );
assertEquals( 1, client.getAccounts().size() );
//end::pc-filter-collection-query-example[]
} );
}
Expand All @@ -198,20 +201,27 @@ public enum AccountType {

//tag::pc-filter-Client-example[]
@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="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<>( );

Expand All @@ -235,6 +245,15 @@ public Client setName(String name) {
return this;
}

public AccountType getType() {
return type;
}

public Client setType(AccountType type) {
this.type = type;
return this;
}

public List<Account> getAccounts() {
return accounts;
}
Expand All @@ -249,6 +268,7 @@ public void addAccount(Account account) {

//tag::pc-filter-Account-example[]
@Entity(name = "Account")
@Table(name = "account")
@FilterDef(
name="activeAccount",
parameters = @ParamDef(
Expand Down
Expand Up @@ -13,13 +13,22 @@
import java.util.regex.Pattern;

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.internal.util.StringHelper;
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.ast.spi.SqlAstCreationContext;
import org.hibernate.sql.ast.tree.predicate.FilterPredicate;
import org.hibernate.type.Type;

import static org.hibernate.internal.util.StringHelper.join;
import static org.hibernate.internal.util.StringHelper.safeInterning;

/**
Expand All @@ -31,7 +40,7 @@
*/
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[] filterConditions;
Expand Down Expand Up @@ -141,49 +150,30 @@ else if ( isTableFromPersistentClass( aliasTableMap ) ) {
}
}

public static class TypedValue {
private final Type type;
private final Object value;

public TypedValue(Type type, Object value) {
this.type = type;
this.value = value;
}

public Type getType() {
return type;
}

public Object getValue() {
return value;
}
}

public static class TransformResult {
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 FilterPredicate createFilterPredicate(LoadQueryInfluencers loadQueryInfluencers, Joinable joinable, String alias) {
if ( loadQueryInfluencers.hasEnabledFilters() ) {
final String filterFragment;
if ( joinable instanceof AbstractCollectionPersister && ( (AbstractCollectionPersister) joinable ).isManyToMany() ) {
filterFragment = ( (AbstractCollectionPersister) joinable ).getManyToManyFilterFragment(
alias,
loadQueryInfluencers.getEnabledFilters()
);
}
else {
filterFragment = joinable.filterFragment( alias, loadQueryInfluencers.getEnabledFilters() );
}
if ( ! StringHelper.isEmptyOrWhiteSpace( filterFragment ) ) {
return doCreateFilterPredicate( filterFragment, loadQueryInfluencers.getEnabledFilters() );
}
}
return null;
}

public static TransformResult transformToPositionalParameters(String filterFragment, Map<String, Filter> enabledFilters) {
private static FilterPredicate doCreateFilterPredicate(String filterFragment, Map<String, Filter> enabledFilters) {
final Matcher matcher = FILTER_PARAMETER_PATTERN.matcher( filterFragment );
final StringBuilder sb = new StringBuilder();
int pos = 0;
final List<TypedValue> parameters = new ArrayList<>( matcher.groupCount() );
final List<FilterJdbcParameter> parameters = new ArrayList<>( matcher.groupCount() );
while( matcher.find() ) {
sb.append( filterFragment, pos, matcher.start() );
pos = matcher.end();
Expand All @@ -192,16 +182,19 @@ public static TransformResult transformToPositionalParameters(String filterFragm
final String parameterName = matcher.group( 2 );
final FilterImpl enabledFilter = (FilterImpl) enabledFilters.get( filterName );
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 );
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 );
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() );
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;
}
};
}
}
Expand Up @@ -163,8 +163,7 @@ private void batchLoad(
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );

final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( keyJdbcCount * smallBatchLength );

sqlAst.getQuerySpec().bindFilterPredicateParameters( jdbcParameterBindings );
jdbcSelect.registerFilterJdbcParameterBindings( jdbcParameterBindings );

final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();

Expand Down
Expand Up @@ -99,6 +99,7 @@ public PersistentCollection load(Object key, SharedSessionContractImplementor se
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );

final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( keyJdbcCount );
jdbcSelect.registerFilterJdbcParameterBindings( jdbcParameterBindings );

final Iterator<JdbcParameter> paramItr = jdbcParameters.iterator();

Expand Down Expand Up @@ -127,8 +128,6 @@ public Object getBindValue() {
);
assert !paramItr.hasNext();

sqlAst.getQuerySpec().bindFilterPredicateParameters( jdbcParameterBindings );

jdbcServices.getJdbcSelectExecutor().list(
jdbcSelect,
jdbcParameterBindings,
Expand Down

0 comments on commit 686a519

Please sign in to comment.