HHH-10027 - Dynamic filter parameter can be bound in wrong order when applied to OneToMany collections
This commit is contained in:
parent
d62e9f44ca
commit
c5e5287790
|
@ -11,6 +11,7 @@ import java.util.Collections;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hibernate.QueryException;
|
||||
import org.hibernate.engine.internal.JoinSequence;
|
||||
|
@ -23,6 +24,7 @@ import org.hibernate.hql.spi.QueryTranslator;
|
|||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.param.DynamicFilterParameterSpecification;
|
||||
import org.hibernate.param.ParameterSpecification;
|
||||
import org.hibernate.persister.collection.QueryableCollection;
|
||||
import org.hibernate.persister.entity.DiscriminatorMetadata;
|
||||
|
@ -677,24 +679,35 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa
|
|||
|
||||
|
||||
// ParameterContainer impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
private List<ParameterSpecification> embeddedParameters;
|
||||
private List<ParameterSpecification> embeddedParameters = new ArrayList<>( );
|
||||
|
||||
@Override
|
||||
public void addEmbeddedParameter(ParameterSpecification specification) {
|
||||
if ( embeddedParameters == null ) {
|
||||
embeddedParameters = new ArrayList<ParameterSpecification>();
|
||||
}
|
||||
embeddedParameters.add( specification );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEmbeddedParameters() {
|
||||
return embeddedParameters != null && ! embeddedParameters.isEmpty();
|
||||
return !embeddedParameters.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParameterSpecification[] getEmbeddedParameters() {
|
||||
return embeddedParameters.toArray( new ParameterSpecification[ embeddedParameters.size() ] );
|
||||
final List<ParameterSpecification> parameterSpecification = getParameterSpecification();
|
||||
return parameterSpecification.toArray( new ParameterSpecification[ parameterSpecification.size() ] );
|
||||
}
|
||||
|
||||
private List<ParameterSpecification> getParameterSpecification() {
|
||||
List<ParameterSpecification> parameterSpecifications =
|
||||
embeddedParameters.stream()
|
||||
.filter( o -> o instanceof DynamicFilterParameterSpecification )
|
||||
.collect( Collectors.toList() );
|
||||
|
||||
parameterSpecifications.addAll(
|
||||
embeddedParameters.stream()
|
||||
.filter( o -> ! (o instanceof DynamicFilterParameterSpecification ) )
|
||||
.collect( Collectors.toList() ) );
|
||||
return parameterSpecifications;
|
||||
}
|
||||
|
||||
public ParameterSpecification getIndexCollectionSelectorParamSpec() {
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* 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.test.filter;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.annotations.Filter;
|
||||
import org.hibernate.annotations.FilterDef;
|
||||
import org.hibernate.annotations.FilterDefs;
|
||||
import org.hibernate.annotations.Filters;
|
||||
import org.hibernate.annotations.ParamDef;
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class OneToManyWithDynamicFilterTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {ArticleRevision.class, ArticleTrading.class};
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
ArticleTrading articleTrading = new ArticleTrading();
|
||||
articleTrading.setClassifier( "no_classification" );
|
||||
articleTrading.setPartyId( 2 );
|
||||
articleTrading.setDeletionTimestamp( Timestamp.valueOf( "9999-12-31 00:00:00" ) );
|
||||
articleTrading.setDeleted( true );
|
||||
|
||||
ArticleRevision revision = new ArticleRevision();
|
||||
revision.addArticleTradings( articleTrading );
|
||||
revision.setDeletionTimestamp( Timestamp.valueOf( "9999-12-31 00:00:00" ) );
|
||||
revision.setDeleted( true );
|
||||
session.save( revision );
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForIssue() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
final org.hibernate.Filter enableFilter = session.enableFilter( "aliveOnly" );
|
||||
enableFilter.setParameter( "aliveTimestamp", Timestamp.valueOf( "9999-12-31 00:00:00" ) );
|
||||
enableFilter.setParameter( "deleted", true );
|
||||
enableFilter.validate();
|
||||
|
||||
final Query query = session.createQuery( "select a.id from ArticleRevision as a " +
|
||||
"left join a.articleTradings as t " +
|
||||
"with ( (t.partyId = :p_0) and (t.classifier = :p_1) )" );
|
||||
query.setParameter( "p_0", 1L );
|
||||
query.setParameter( "p_1", "no_classification" );
|
||||
final List list = query.list();
|
||||
assertThat( list.size(), is( 1 ) );
|
||||
|
||||
} );
|
||||
}
|
||||
|
||||
@Entity(name = "ArticleRevision")
|
||||
@Table(name = "REVISION")
|
||||
@FilterDefs({
|
||||
@FilterDef(name = "aliveOnly", parameters = {
|
||||
@ParamDef(name = "aliveTimestamp", type = "timestamp"),
|
||||
@ParamDef(name = "deleted", type = "boolean")
|
||||
}, defaultCondition = "DELETION_TIMESTAMP = :aliveTimestamp and DELETED = :deleted")
|
||||
})
|
||||
@Filters({@Filter(name = "aliveOnly", condition = "DELETION_TIMESTAMP = :aliveTimestamp and DELETED = :deleted")})
|
||||
public static class ArticleRevision {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@Column(name = "DELETION_TIMESTAMP")
|
||||
private Timestamp deletionTimestamp;
|
||||
|
||||
@Column(name = "DELETED")
|
||||
private boolean deleted;
|
||||
|
||||
@OneToMany(mappedBy = "articleRevision", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@Filter(name = "aliveOnly")
|
||||
private Set<ArticleTrading> articleTradings = new HashSet<ArticleTrading>();
|
||||
|
||||
public void setDeletionTimestamp(Timestamp deletionTimestamp) {
|
||||
this.deletionTimestamp = deletionTimestamp;
|
||||
}
|
||||
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
public void addArticleTradings(ArticleTrading articleTrading) {
|
||||
this.articleTradings.add( articleTrading );
|
||||
articleTrading.setArticleRevision( this );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "ArticleTrading")
|
||||
@Table(name = "TRADING")
|
||||
public static class ArticleTrading {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "articleRevision", nullable = false)
|
||||
private ArticleRevision articleRevision;
|
||||
|
||||
private long partyId;
|
||||
|
||||
private String classifier;
|
||||
|
||||
@Column(name = "DELETED")
|
||||
private boolean deleted;
|
||||
|
||||
@Column(name = "DELETION_TIMESTAMP")
|
||||
protected Timestamp deletionTimestamp;
|
||||
|
||||
public void setArticleRevision(ArticleRevision articleRevision) {
|
||||
this.articleRevision = articleRevision;
|
||||
}
|
||||
|
||||
public void setPartyId(long partyId) {
|
||||
this.partyId = partyId;
|
||||
}
|
||||
|
||||
public void setClassifier(String classifier) {
|
||||
this.classifier = classifier;
|
||||
}
|
||||
|
||||
public void setDeletionTimestamp(Timestamp deletionTimestamp) {
|
||||
this.deletionTimestamp = deletionTimestamp;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue