implement @where and @whereJoinTable

This commit is contained in:
Nathan Xu 2020-04-22 15:41:09 -04:00 committed by Steve Ebersole
parent f919358e0c
commit 9ef62f1fb8
20 changed files with 1238 additions and 84 deletions

View File

@ -975,7 +975,7 @@ public abstract class CollectionBinder {
FilterJoinTable simpleFilterJoinTable = property.getAnnotation( FilterJoinTable.class );
if ( simpleFilterJoinTable != null ) {
if ( hasAssociationTable ) {
collection.addManyToManyFilter(simpleFilterJoinTable.name(), simpleFilterJoinTable.condition(),
collection.addFilter(simpleFilterJoinTable.name(), simpleFilterJoinTable.condition(),
simpleFilterJoinTable.deduceAliasInjectionPoints(),
toAliasTableMap(simpleFilterJoinTable.aliases()), toAliasEntityMap(simpleFilterJoinTable.aliases()));
}
@ -990,7 +990,7 @@ public abstract class CollectionBinder {
if ( filterJoinTables != null ) {
for (FilterJoinTable filter : filterJoinTables.value()) {
if ( hasAssociationTable ) {
collection.addManyToManyFilter(filter.name(), filter.condition(),
collection.addFilter(filter.name(), filter.condition(),
filter.deduceAliasInjectionPoints(),
toAliasTableMap(filter.aliases()), toAliasEntityMap(filter.aliases()));
}

View File

@ -19,16 +19,11 @@ 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;
/**
@ -94,7 +89,7 @@ public class FilterHelper {
return aliasTableMap.size() == 1 && aliasTableMap.containsKey( null );
}
public boolean isAffectedBy(Map enabledFilters) {
public boolean isAffectedBy(Map<String, Filter> enabledFilters) {
for ( String filterName : filterNames ) {
if ( enabledFilters.containsKey( filterName ) ) {
return true;
@ -103,13 +98,13 @@ public class FilterHelper {
return false;
}
public String render(FilterAliasGenerator aliasGenerator, Map enabledFilters) {
public String render(FilterAliasGenerator aliasGenerator, Map<String, Filter> enabledFilters) {
StringBuilder buffer = new StringBuilder();
render( buffer, aliasGenerator, enabledFilters );
return buffer.toString();
}
public void render(StringBuilder buffer, FilterAliasGenerator aliasGenerator, Map enabledFilters) {
public void render(StringBuilder buffer, FilterAliasGenerator aliasGenerator, Map<String, Filter> enabledFilters) {
if ( CollectionHelper.isEmpty( filterNames ) ) {
return;
}
@ -137,11 +132,11 @@ public class FilterHelper {
);
}
else if ( isTableFromPersistentClass( aliasTableMap ) ) {
return condition.replace( "{alias}", aliasGenerator.getAlias( aliasTableMap.get( null ) ) );
return StringHelper.replace( condition, "{alias}", aliasGenerator.getAlias( aliasTableMap.get( null ) ) );
}
else {
for ( Map.Entry<String, String> entry : aliasTableMap.entrySet() ) {
condition = condition.replace(
condition = StringHelper.replace( condition,
"{" + entry.getKey() + "}",
aliasGenerator.getAlias( entry.getValue() )
);
@ -151,22 +146,13 @@ public class FilterHelper {
}
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() );
}
final String filterFragment = joinable.filterFragment( alias, loadQueryInfluencers.getEnabledFilters() );
if ( StringHelper.isNotEmpty( filterFragment ) ) {
return doCreateFilterPredicate( filterFragment, loadQueryInfluencers.getEnabledFilters() );
}
else {
return null;
}
return null;
}
private static FilterPredicate doCreateFilterPredicate(String filterFragment, Map<String, Filter> enabledFilters) {

View File

@ -227,11 +227,6 @@ public class LoaderSelectBuilder {
rootQuerySpec.getFromClause().addRoot( rootTableGroup );
sqlAstCreationState.getFromClauseAccess().registerTableGroup( rootNavigablePath, rootTableGroup );
if ( loadable instanceof PluralAttributeMapping ) {
applyFiltering( rootQuerySpec, rootTableGroup, (PluralAttributeMapping) loadable );
applyOrdering( rootTableGroup, (PluralAttributeMapping) loadable );
}
if ( partsToSelect != null && !partsToSelect.isEmpty() ) {
domainResults = new ArrayList<>( partsToSelect.size() );
for ( ModelPart part : partsToSelect ) {
@ -282,6 +277,12 @@ public class LoaderSelectBuilder {
sqlAstCreationState
);
if ( loadable instanceof PluralAttributeMapping ) {
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) loadable;
applyFiltering( rootQuerySpec, rootTableGroup, pluralAttributeMapping );
applyOrdering( rootTableGroup, pluralAttributeMapping );
}
if ( orderByFragments != null ) {
orderByFragments.forEach(
(orderByFragment, tableGroup) -> orderByFragment.apply( rootQuerySpec, tableGroup, sqlAstCreationState )
@ -380,22 +381,20 @@ public class LoaderSelectBuilder {
}
private void applyFiltering(QuerySpec querySpec, TableGroup tableGroup, PluralAttributeMapping pluralAttributeMapping) {
if ( loadQueryInfluencers.hasEnabledFilters() ) {
final Joinable joinable = pluralAttributeMapping
.getCollectionDescriptor()
.getCollectionType()
.getAssociatedJoinable( creationContext.getSessionFactory() );
assert joinable instanceof AbstractCollectionPersister;
final String tableExpression = joinable.getTableName();
final String tableAlias = tableGroup.resolveTableReference( tableExpression ).getIdentificationVariable();
final Predicate filterPredicate = FilterHelper.createFilterPredicate(
loadQueryInfluencers,
joinable,
tableAlias
);
if ( filterPredicate != null ) {
querySpec.applyPredicate( filterPredicate );
}
final Joinable joinable = pluralAttributeMapping
.getCollectionDescriptor()
.getCollectionType()
.getAssociatedJoinable( creationContext.getSessionFactory() );
assert joinable instanceof AbstractCollectionPersister;
final String tableExpression = joinable.getTableName();
final String tableAlias = tableGroup.resolveTableReference( tableExpression ).getIdentificationVariable();
final Predicate filterPredicate = FilterHelper.createFilterPredicate(
loadQueryInfluencers,
joinable,
tableAlias
);
if ( filterPredicate != null ) {
querySpec.applyPredicate( filterPredicate );
}
}
@ -511,15 +510,16 @@ public class LoaderSelectBuilder {
fetches.add( fetch );
if ( fetchable instanceof PluralAttributeMapping && fetchTiming == FetchTiming.IMMEDIATE && joined ) {
final PluralAttributeMapping pluralAttributeMapping = (PluralAttributeMapping) fetchable;
applyFiltering(
querySpec,
creationState.getFromClauseAccess().getTableGroup( fetchablePath ),
( (PluralAttributeMapping) fetchable )
pluralAttributeMapping
);
applyOrdering(
querySpec,
fetchablePath,
( (PluralAttributeMapping) fetchable ),
pluralAttributeMapping,
creationState
);
}
@ -590,10 +590,6 @@ public class LoaderSelectBuilder {
rootQuerySpec.getFromClause().addRoot( rootTableGroup );
sqlAstCreationState.getFromClauseAccess().registerTableGroup( rootNavigablePath, rootTableGroup );
// NOTE : no need to check - we are explicitly processing a plural-attribute
applyFiltering( rootQuerySpec, rootTableGroup, attributeMapping );
applyOrdering( rootTableGroup, attributeMapping );
// generate and apply the restriction
applySubSelectRestriction(
rootQuerySpec,
@ -603,6 +599,10 @@ public class LoaderSelectBuilder {
sqlAstCreationState
);
// NOTE : no need to check - we are explicitly processing a plural-attribute
applyFiltering( rootQuerySpec, rootTableGroup, attributeMapping );
applyOrdering( rootTableGroup, attributeMapping );
// register the jdbc-parameters
subselect.getLoadingJdbcParameters().forEach( jdbcParameterConsumer );

View File

@ -16,7 +16,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import org.hibernate.AssertionFailure;
import org.hibernate.FetchMode;
@ -1781,13 +1780,15 @@ public abstract class AbstractCollectionPersister
public abstract boolean isManyToMany();
@Override
public String getManyToManyFilterFragment(String alias, Map enabledFilters) {
public String getManyToManyFilterFragment(String alias, Map<String, Filter> enabledFilters) {
StringBuilder buffer = new StringBuilder();
manyToManyFilterHelper.render( buffer, elementPersister.getFilterAliasGenerator(alias), enabledFilters );
if ( manyToManyWhereString != null ) {
buffer.append( " and " )
.append( StringHelper.replace( manyToManyWhereTemplate, Template.TEMPLATE, alias ) );
if ( buffer.length() > 0 ) {
buffer.append( " and " );
}
buffer.append( StringHelper.replace( manyToManyWhereTemplate, Template.TEMPLATE, alias ) );
}
return buffer.toString();
@ -1917,15 +1918,15 @@ public abstract class AbstractCollectionPersister
}
protected String filterFragment(String alias) throws MappingException {
return hasWhere() ? " and " + getSQLWhereString( alias ) : "";
return hasWhere() ? getSQLWhereString( alias ) : "";
}
protected String filterFragment(String alias, Set<String> treatAsDeclarations) throws MappingException {
return hasWhere() ? " and " + getSQLWhereString( alias ) : "";
return hasWhere() ? getSQLWhereString( alias ) : "";
}
@Override
public String filterFragment(String alias, Map enabledFilters) throws MappingException {
public String filterFragment(String alias, Map<String, Filter> enabledFilters) throws MappingException {
StringBuilder sessionFilterFragment = new StringBuilder();
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator(alias), enabledFilters );
@ -1935,7 +1936,7 @@ public abstract class AbstractCollectionPersister
@Override
public String filterFragment(
String alias,
Map enabledFilters,
Map<String, Filter> enabledFilters,
Set<String> treatAsDeclarations) {
StringBuilder sessionFilterFragment = new StringBuilder();
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator(alias), enabledFilters );

View File

@ -12,6 +12,7 @@ import java.sql.SQLException;
import java.util.Comparator;
import java.util.Map;
import org.hibernate.Filter;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.NotYetImplementedFor6Exception;
@ -169,7 +170,7 @@ public interface CollectionPersister extends CollectionDefinition {
*/
boolean isManyToMany();
String getManyToManyFilterFragment(String alias, Map enabledFilters);
String getManyToManyFilterFragment(String alias, Map<String, Filter> enabledFilters);
/**
* Is this an "indexed" collection? (list or map)

View File

@ -4037,14 +4037,14 @@ public abstract class AbstractEntityPersister
}
@Override
public String filterFragment(String alias, Map enabledFilters) throws MappingException {
public String filterFragment(String alias, Map<String, Filter> enabledFilters) throws MappingException {
final StringBuilder sessionFilterFragment = new StringBuilder();
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator( alias ), enabledFilters );
return sessionFilterFragment.append( filterFragment( alias ) ).toString();
}
@Override
public String filterFragment(String alias, Map enabledFilters, Set<String> treatAsDeclarations) {
public String filterFragment(String alias, Map<String, Filter> enabledFilters, Set<String> treatAsDeclarations) {
final StringBuilder sessionFilterFragment = new StringBuilder();
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator( alias ), enabledFilters );
return sessionFilterFragment.append( filterFragment( alias, treatAsDeclarations ) ).toString();

View File

@ -8,6 +8,7 @@ package org.hibernate.persister.entity;
import java.util.Map;
import java.util.Set;
import org.hibernate.Filter;
import org.hibernate.MappingException;
/**
@ -78,12 +79,12 @@ public interface Joinable {
/**
* Get the where clause filter, given a query alias and considering enabled session filters
*/
public String filterFragment(String alias, Map enabledFilters) throws MappingException;
public String filterFragment(String alias, Map<String, Filter> enabledFilters) throws MappingException;
/**
* Get the where clause filter, given a query alias and considering enabled session filters
*/
public String filterFragment(String alias, Map enabledFilters, Set<String> treatAsDeclarations) throws MappingException;
public String filterFragment(String alias, Map<String, Filter> enabledFilters, Set<String> treatAsDeclarations) throws MappingException;
public String oneToManyFilterFragment(String alias) throws MappingException;

View File

@ -982,9 +982,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
@Override
protected String filterFragment(String alias) {
return hasWhere()
? " and " + getSQLWhereString( generateFilterConditionAlias( alias ) )
: "";
return hasWhere() ? getSQLWhereString( generateFilterConditionAlias( alias ) ) : "";
}
@Override

View File

@ -8,6 +8,7 @@ package org.hibernate.persister.entity;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -568,11 +569,12 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
@Override
protected String filterFragment(String alias) throws MappingException {
String result = discriminatorFilterFragment( alias );
if ( hasWhere() ) {
result += " and " + getSQLWhereString( alias );
return discriminatorFilterFragment( alias ) + " and " + getSQLWhereString( alias );
}
else {
return "";
}
return result;
}
private String discriminatorFilterFragment(String alias) throws MappingException {
@ -594,11 +596,12 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
@Override
protected String filterFragment(String alias, Set<String> treatAsDeclarations) {
String result = discriminatorFilterFragment( alias, treatAsDeclarations );
if ( hasWhere() ) {
result += " and " + getSQLWhereString( alias );
return discriminatorFilterFragment( alias, treatAsDeclarations ) + " and " + getSQLWhereString( alias );
}
else {
return "";
}
return result;
}
private String discriminatorFilterFragment(String alias, Set<String> treatAsDeclarations) {

View File

@ -329,9 +329,7 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
@Override
protected String filterFragment(String name) {
return hasWhere()
? " and " + getSQLWhereString( name )
: "";
return hasWhere() ? getSQLWhereString( name ) : "";
}
@Override

View File

@ -0,0 +1,172 @@
/*
* 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.loading.filter;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.hibernate.annotations.WhereJoinTable;
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.is;
import static org.junit.Assert.assertThat;
/**
* @author Nathan Xu
*/
@DomainModel(
annotatedClasses = {
WhereJoinTableTests.Book.class,
WhereJoinTableTests.Reader.class
}
)
@SessionFactory
public class WhereJoinTableTests {
@BeforeEach
void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.doWork( connection -> {
try ( Statement statement = connection.createStatement() ) {
statement.executeUpdate(
"ALTER TABLE Book_Reader ADD created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
);
}
} );
final Book book = new Book();
book.setId( 1L );
book.setTitle( "High-Performance Java Persistence" );
book.setAuthor( "Vad Mihalcea" );
session.persist( book );
final Reader reader1 = new Reader();
reader1.setId( 1L );
reader1.setName( "John Doe" );
session.persist( reader1 );
final Reader reader2 = new Reader();
reader2.setId( 2L );
reader2.setName( "John Doe Jr." );
session.persist( reader2 );
} );
}
@Test
void testWhereJoinTable(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.doWork( connection -> {
try ( Statement statement = connection.createStatement() ) {
statement.executeUpdate(
"INSERT INTO Book_Reader " +
" (book_id, reader_id) " +
"VALUES " +
" (1, 1) "
);
statement.executeUpdate(
"INSERT INTO Book_Reader " +
" (book_id, reader_id, created_on) " +
"VALUES " +
" (1, 2, DATEADD( 'DAY', -10, CURRENT_TIMESTAMP() )) "
);
}}
);
final Book book = session.find( Book.class, 1L );
assertThat( book.getCurrentWeekReaders().size(), is( 1 ) );
} );
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private String author;
@ManyToMany
@JoinTable(
name = "Book_Reader",
joinColumns = @JoinColumn(name = "book_id"),
inverseJoinColumns = @JoinColumn(name = "reader_id")
)
@WhereJoinTable( clause = "created_on > DATEADD( 'DAY', -7, CURRENT_TIMESTAMP() )")
private List<Reader> currentWeekReaders = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public List<Reader> getCurrentWeekReaders() {
return currentWeekReaders;
}
}
@Entity(name = "Reader")
public static class Reader {
@Id
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@ -0,0 +1,210 @@
/*
* 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.loading.filter;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
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.Where;
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.is;
import static org.junit.Assert.assertThat;
/**
* @author Nathan Xu
*/
@DomainModel(
annotatedClasses = {
WhereTests.Client.class,
WhereTests.Account.class
}
)
@SessionFactory
public class WhereTests {
@BeforeEach
void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
Client client = new Client();
client.setId( 1L );
client.setName( "John Doe" );
session.persist( client );
Account account1 = new Account( );
account1.setId( 1L );
account1.setType( AccountType.CREDIT );
account1.setAmount( 5000d );
account1.setRate( 1.25 / 100 );
account1.setActive( true );
account1.setClient( client );
client.getCreditAccounts().add( account1 );
session.persist( account1 );
Account account2 = new Account( );
account2.setId( 2L );
account2.setType( AccountType.DEBIT );
account2.setAmount( 0d );
account2.setRate( 1.05 / 100 );
account2.setActive( false );
account2.setClient( client );
client.getDebitAccounts().add( account2 );
session.persist( account2 );
Account account3 = new Account( );
account3.setType( AccountType.DEBIT );
account3.setId( 3L );
account3.setAmount( 250d );
account3.setRate( 1.05 / 100 );
account3.setActive( true );
account3.setClient( client );
client.getDebitAccounts().add( account3 );
session.persist( account3 );
} );
}
@Test
void testWhere(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final Client client = session.find( Client.class, 1L );
assertThat( client.getCreditAccounts().size(), is( 1 ) );
assertThat( client.getDebitAccounts().size(), is( 1 ) );
} );
}
public enum AccountType {
DEBIT,
CREDIT
}
@Entity(name = "Client")
public static class Client {
@Id
private Long id;
private String name;
@Where( clause = "account_type = 'DEBIT'")
@OneToMany(mappedBy = "client")
private List<Account> debitAccounts = new ArrayList<>();
@Where( clause = "account_type = 'CREDIT'")
@OneToMany(mappedBy = "client")
private List<Account> creditAccounts = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Account> getDebitAccounts() {
return debitAccounts;
}
public List<Account> getCreditAccounts() {
return creditAccounts;
}
}
@Entity(name = "Account")
@Where( clause = "active = true" )
public static class Account {
@Id
private Long id;
@ManyToOne
private Client client;
@Column(name = "account_type")
@Enumerated(EnumType.STRING)
private AccountType type;
private Double amount;
private Double rate;
private boolean active;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
public AccountType getType() {
return type;
}
public void setType(AccountType type) {
this.type = type;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public Double getRate() {
return rate;
}
public void setRate(Double rate) {
this.rate = rate;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
}

View File

@ -104,7 +104,7 @@ public class FilterBasicsTests implements SessionFactoryScopeAware {
@ParameterizedTest
@ValueSource( strings = { "true", "false" } )
void testLoadFilterOnEntity(boolean enableFilter) {
void testFilterOnEntity(boolean enableFilter) {
scope.inTransaction( session -> {
if ( enableFilter ) {
session.enableFilter( "activeAccount" ).setParameter( "active", true );
@ -123,7 +123,7 @@ public class FilterBasicsTests implements SessionFactoryScopeAware {
@ParameterizedTest
@ValueSource( strings = { "true", "false" } )
void testLoadFilterOnCollectionField(boolean enableFilter) {
void testFilterOnCollectionField(boolean enableFilter) {
scope.inTransaction( session -> {
if ( enableFilter ) {
session.enableFilter( "activeAccount" ).setParameter( "active", true );

View File

@ -22,6 +22,7 @@ import javax.persistence.ManyToMany;
import javax.persistence.OrderColumn;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Root;
import org.hibernate.annotations.FilterDef;
@ -211,6 +212,7 @@ public class FilterJoinTableTests {
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 );
root.fetch( "accounts", JoinType.INNER );
criteria.select( root );
criteria.where( criteriaBuilder.equal( root.get( idFieldName ), criteriaBuilder.literal( idValue ) ) );
return criteria;

View File

@ -0,0 +1,182 @@
/*
* 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.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.hibernate.annotations.WhereJoinTable;
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.is;
import static org.junit.Assert.assertThat;
/**
* @author Nathan Xu
*/
@DomainModel(
annotatedClasses = {
WhereJoinTableTests.Book.class,
WhereJoinTableTests.Reader.class
}
)
@SessionFactory
public class WhereJoinTableTests {
@BeforeEach
void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.doWork( connection -> {
try ( Statement statement = connection.createStatement() ) {
statement.executeUpdate(
"ALTER TABLE Book_Reader ADD created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
);
}
} );
final Book book = new Book();
book.setId( 1L );
book.setTitle( "High-Performance Java Persistence" );
book.setAuthor( "Vad Mihalcea" );
session.persist( book );
final Reader reader1 = new Reader();
reader1.setId( 1L );
reader1.setName( "John Doe" );
session.persist( reader1 );
final Reader reader2 = new Reader();
reader2.setId( 2L );
reader2.setName( "John Doe Jr." );
session.persist( reader2 );
} );
}
@Test
void testWhereJoinTable(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.doWork( connection -> {
try ( Statement statement = connection.createStatement() ) {
statement.executeUpdate(
"INSERT INTO Book_Reader " +
" (book_id, reader_id) " +
"VALUES " +
" (1, 1) "
);
statement.executeUpdate(
"INSERT INTO Book_Reader " +
" (book_id, reader_id, created_on) " +
"VALUES " +
" (1, 2, DATEADD( 'DAY', -10, CURRENT_TIMESTAMP() )) "
);
}}
);
final CriteriaBuilder criteriaBuilder = scope.getSessionFactory().getCriteriaBuilder();
final CriteriaQuery<Book> criteriaQuery = createCriteriaQuery( criteriaBuilder, Book.class, "id", 1L );
final Book book = session.createQuery( criteriaQuery ).uniqueResult();
assertThat( book.getCurrentWeekReaders().size(), is( 1 ) );
} );
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private String author;
@ManyToMany
@JoinTable(
name = "Book_Reader",
joinColumns = @JoinColumn(name = "book_id"),
inverseJoinColumns = @JoinColumn(name = "reader_id")
)
@WhereJoinTable( clause = "created_on > DATEADD( 'DAY', -7, CURRENT_TIMESTAMP() )")
private List<Reader> currentWeekReaders = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public List<Reader> getCurrentWeekReaders() {
return currentWeekReaders;
}
}
@Entity(name = "Reader")
public static class Reader {
@Id
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
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;
}
}

View File

@ -0,0 +1,221 @@
/*
* 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.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
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.Where;
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.is;
import static org.junit.Assert.assertThat;
/**
* @author Nathan Xu
*/
@DomainModel(
annotatedClasses = {
WhereTests.Client.class,
WhereTests.Account.class
}
)
@SessionFactory
public class WhereTests {
@BeforeEach
void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
Client client = new Client();
client.setId( 1L );
client.setName( "John Doe" );
session.persist( client );
Account account1 = new Account( );
account1.setId( 1L );
account1.setType( AccountType.CREDIT );
account1.setAmount( 5000d );
account1.setRate( 1.25 / 100 );
account1.setActive( true );
account1.setClient( client );
client.getCreditAccounts().add( account1 );
session.persist( account1 );
Account account2 = new Account( );
account2.setId( 2L );
account2.setType( AccountType.DEBIT );
account2.setAmount( 0d );
account2.setRate( 1.05 / 100 );
account2.setActive( false );
account2.setClient( client );
client.getDebitAccounts().add( account2 );
session.persist( account2 );
Account account3 = new Account( );
account3.setType( AccountType.DEBIT );
account3.setId( 3L );
account3.setAmount( 250d );
account3.setRate( 1.05 / 100 );
account3.setActive( true );
account3.setClient( client );
client.getDebitAccounts().add( account3 );
session.persist( account3 );
} );
}
@Test
void testWhere(SessionFactoryScope scope) {
scope.inTransaction( session -> {
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.getCreditAccounts().size(), is( 1 ) );
assertThat( client.getDebitAccounts().size(), is( 1 ) );
} );
}
public enum AccountType {
DEBIT,
CREDIT
}
@Entity(name = "Client")
public static class Client {
@Id
private Long id;
private String name;
@Where( clause = "account_type = 'DEBIT'")
@OneToMany(mappedBy = "client")
private List<Account> debitAccounts = new ArrayList<>();
@Where( clause = "account_type = 'CREDIT'")
@OneToMany(mappedBy = "client")
private List<Account> creditAccounts = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Account> getDebitAccounts() {
return debitAccounts;
}
public List<Account> getCreditAccounts() {
return creditAccounts;
}
}
@Entity(name = "Account")
@Where( clause = "active = true" )
public static class Account {
@Id
private Long id;
@ManyToOne
private Client client;
@Column(name = "account_type")
@Enumerated(EnumType.STRING)
private AccountType type;
private Double amount;
private Double rate;
private boolean active;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
public AccountType getType() {
return type;
}
public void setType(AccountType type) {
this.type = type;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public Double getRate() {
return rate;
}
public void setRate(Double rate) {
this.rate = rate;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
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;
}
}

View File

@ -100,7 +100,7 @@ public class FilterBasicsTests implements SessionFactoryScopeAware {
@ParameterizedTest
@ValueSource( strings = { "true", "false" } )
void testLoadFilterOnEntity(boolean enableFilter) {
void testFilterOnEntity(boolean enableFilter) {
scope.inTransaction( session -> {
if ( enableFilter ) {
session.enableFilter( "activeAccount" ).setParameter( "active", true );
@ -117,7 +117,7 @@ public class FilterBasicsTests implements SessionFactoryScopeAware {
@ParameterizedTest
@ValueSource( strings = { "true", "false" } )
void testLoadFilterOnCollectionField(boolean enableFilter) {
void testFilterOnCollectionField(boolean enableFilter) {
scope.inTransaction( session -> {
if ( enableFilter ) {
session.enableFilter( "activeAccount" ).setParameter( "active", true );

View File

@ -0,0 +1,169 @@
/*
* 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.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import org.hibernate.annotations.WhereJoinTable;
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.is;
import static org.junit.Assert.assertThat;
/**
* @author Nathan Xu
*/
@DomainModel(
annotatedClasses = {
WhereJoinTableTests.Book.class,
WhereJoinTableTests.Reader.class
}
)
@SessionFactory
public class WhereJoinTableTests {
@BeforeEach
void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.doWork( connection -> {
try ( Statement statement = connection.createStatement() ) {
statement.executeUpdate(
"ALTER TABLE Book_Reader ADD created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP"
);
}
} );
final Book book = new Book();
book.setId( 1L );
book.setTitle( "High-Performance Java Persistence" );
book.setAuthor( "Vad Mihalcea" );
session.persist( book );
final Reader reader1 = new Reader();
reader1.setId( 1L );
reader1.setName( "John Doe" );
session.persist( reader1 );
final Reader reader2 = new Reader();
reader2.setId( 2L );
reader2.setName( "John Doe Jr." );
session.persist( reader2 );
} );
}
@Test
void testWhereJoinTable(SessionFactoryScope scope) {
scope.inTransaction( session -> {
session.doWork( connection -> {
try ( Statement statement = connection.createStatement() ) {
statement.executeUpdate(
"INSERT INTO Book_Reader " +
" (book_id, reader_id) " +
"VALUES " +
" (1, 1) "
);
statement.executeUpdate(
"INSERT INTO Book_Reader " +
" (book_id, reader_id, created_on) " +
"VALUES " +
" (1, 2, DATEADD( 'DAY', -10, CURRENT_TIMESTAMP() )) "
);
}}
);
final Book book = session.createQuery( "select b from Book b where b.id = :id", Book.class)
.setParameter( "id", 1L ).uniqueResult();
assertThat( book.getCurrentWeekReaders().size(), is( 1 ) );
} );
}
@Entity(name = "Book")
public static class Book {
@Id
private Long id;
private String title;
private String author;
@ManyToMany
@JoinTable(
name = "Book_Reader",
joinColumns = @JoinColumn(name = "book_id"),
inverseJoinColumns = @JoinColumn(name = "reader_id")
)
@WhereJoinTable( clause = "created_on > DATEADD( 'DAY', -7, CURRENT_TIMESTAMP() )")
private List<Reader> currentWeekReaders = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public List<Reader> getCurrentWeekReaders() {
return currentWeekReaders;
}
}
@Entity(name = "Reader")
public static class Reader {
@Id
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@ -0,0 +1,209 @@
/*
* 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.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.annotations.Where;
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.is;
import static org.junit.Assert.assertThat;
/**
* @author Nathan Xu
*/
@DomainModel(
annotatedClasses = {
WhereTests.Client.class,
WhereTests.Account.class
}
)
@SessionFactory
public class WhereTests {
@BeforeEach
void setUp(SessionFactoryScope scope) {
scope.inTransaction( session -> {
Client client = new Client();
client.setId( 1L );
client.setName( "John Doe" );
session.persist( client );
Account account1 = new Account( );
account1.setId( 1L );
account1.setType( AccountType.CREDIT );
account1.setAmount( 5000d );
account1.setRate( 1.25 / 100 );
account1.setActive( true );
account1.setClient( client );
client.getCreditAccounts().add( account1 );
session.persist( account1 );
Account account2 = new Account( );
account2.setId( 2L );
account2.setType( AccountType.DEBIT );
account2.setAmount( 0d );
account2.setRate( 1.05 / 100 );
account2.setActive( false );
account2.setClient( client );
client.getDebitAccounts().add( account2 );
session.persist( account2 );
Account account3 = new Account( );
account3.setType( AccountType.DEBIT );
account3.setId( 3L );
account3.setAmount( 250d );
account3.setRate( 1.05 / 100 );
account3.setActive( true );
account3.setClient( client );
client.getDebitAccounts().add( account3 );
session.persist( account3 );
} );
}
@Test
void testWhere(SessionFactoryScope scope) {
scope.inTransaction( session -> {
final String hqlString = "select c from Client c where c.id = :id";
final Client client = session.createQuery( hqlString, Client.class ).setParameter( "id", 1L ).uniqueResult();
assertThat( client.getCreditAccounts().size(), is( 1 ) );
assertThat( client.getDebitAccounts().size(), is( 1 ) );
} );
}
public enum AccountType {
DEBIT,
CREDIT
}
@Entity(name = "Client")
public static class Client {
@Id
private Long id;
private String name;
@Where( clause = "account_type = 'DEBIT'")
@OneToMany(mappedBy = "client")
private List<Account> debitAccounts = new ArrayList<>();
@Where( clause = "account_type = 'CREDIT'")
@OneToMany(mappedBy = "client")
private List<Account> creditAccounts = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Account> getDebitAccounts() {
return debitAccounts;
}
public List<Account> getCreditAccounts() {
return creditAccounts;
}
}
@Entity(name = "Account")
@Where( clause = "active = true" )
public static class Account {
@Id
private Long id;
@ManyToOne
private Client client;
@Column(name = "account_type")
@Enumerated(EnumType.STRING)
private AccountType type;
private Double amount;
private Double rate;
private boolean active;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
public AccountType getType() {
return type;
}
public void setType(AccountType type) {
this.type = type;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public Double getRate() {
return rate;
}
public void setRate(Double rate) {
this.rate = rate;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
}
}

View File

@ -16,6 +16,7 @@ import java.util.Map;
import java.util.function.Consumer;
import org.hibernate.EntityMode;
import org.hibernate.Filter;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
@ -806,7 +807,7 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
public String getManyToManyFilterFragment(String alias, Map enabledFilters) {
public String getManyToManyFilterFragment(String alias, Map<String, Filter> enabledFilters) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}