HHH-10991 - Wrong order parameter binding when filters are used in conjunction with component type parameters and subqueries
This commit is contained in:
parent
9b9becb903
commit
7c361fee3a
|
@ -26,6 +26,7 @@ import org.hibernate.internal.util.EntityPrinter;
|
|||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.query.internal.QueryParameterBindingsImpl;
|
||||
import org.hibernate.transform.ResultTransformer;
|
||||
import org.hibernate.type.ComponentType;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -535,7 +536,6 @@ public final class QueryParameters {
|
|||
StringBuilder result = new StringBuilder();
|
||||
List parameters = new ArrayList();
|
||||
List parameterTypes = new ArrayList();
|
||||
|
||||
int positionalIndex = 0;
|
||||
while ( tokens.hasMoreTokens() ) {
|
||||
final String token = tokens.nextToken();
|
||||
|
@ -564,12 +564,26 @@ public final class QueryParameters {
|
|||
}
|
||||
}
|
||||
else {
|
||||
result.append( token );
|
||||
if ( "?".equals( token ) && positionalIndex < getPositionalParameterValues().length ) {
|
||||
final Type type = getPositionalParameterTypes()[positionalIndex];
|
||||
if ( type.isComponentType() ) {
|
||||
// should process tokens till reaching the number of "?" corresponding to the
|
||||
// numberOfParametersCoveredBy of the compositeType
|
||||
int paramIndex = 1;
|
||||
final int numberOfParametersCoveredBy = getNumberOfParametersCoveredBy( ((ComponentType) type).getSubtypes() );
|
||||
while ( paramIndex < numberOfParametersCoveredBy ) {
|
||||
final String nextToken = tokens.nextToken();
|
||||
if ( "?".equals( nextToken ) ) {
|
||||
paramIndex++;
|
||||
}
|
||||
result.append( nextToken );
|
||||
}
|
||||
}
|
||||
parameters.add( getPositionalParameterValues()[positionalIndex] );
|
||||
parameterTypes.add( getPositionalParameterTypes()[positionalIndex] );
|
||||
parameterTypes.add( type );
|
||||
positionalIndex++;
|
||||
}
|
||||
result.append( token );
|
||||
}
|
||||
}
|
||||
processedPositionalParameterValues = parameters.toArray();
|
||||
|
@ -578,6 +592,19 @@ public final class QueryParameters {
|
|||
}
|
||||
}
|
||||
|
||||
private int getNumberOfParametersCoveredBy(Type[] subtypes) {
|
||||
int numberOfParameters = 0;
|
||||
for ( Type type : subtypes ) {
|
||||
if ( type.isComponentType() ) {
|
||||
numberOfParameters = numberOfParameters + getNumberOfParametersCoveredBy( ((ComponentType) type).getSubtypes() );
|
||||
}
|
||||
else {
|
||||
numberOfParameters++;
|
||||
}
|
||||
}
|
||||
return numberOfParameters;
|
||||
}
|
||||
|
||||
public String getFilteredSQL() {
|
||||
return processedSQL;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* 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.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.EmbeddedId;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Table;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Criteria;
|
||||
import org.hibernate.annotations.Filter;
|
||||
import org.hibernate.annotations.FilterDef;
|
||||
import org.hibernate.annotations.ParamDef;
|
||||
import org.hibernate.criterion.DetachedCriteria;
|
||||
import org.hibernate.criterion.Projections;
|
||||
import org.hibernate.criterion.Property;
|
||||
import org.hibernate.criterion.Restrictions;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
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
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-10991")
|
||||
public class CriteriaQueryWithAppliedFilterTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
private final static Identifier STUDENT_ID = new Identifier( 2, new Identifier2( 4, 5L ) );
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {Student.class};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareTest() throws Exception {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
final Student student = new Student();
|
||||
student.setId( STUDENT_ID );
|
||||
student.setName( "dre" );
|
||||
student.setStatus( "active" );
|
||||
student.setAge( 21 );
|
||||
student.setAddress( new Address( "London", "Lollard St" ) );
|
||||
session.save( student );
|
||||
|
||||
final Student student2 = new Student();
|
||||
student2.setId( new Identifier( 4, new Identifier2( 4, 6L ) ) );
|
||||
student2.setName( "Livia" );
|
||||
student2.setStatus( "active" );
|
||||
student2.setAge( 27 );
|
||||
student2.setAddress( new Address( "London", "Oxford St" ) );
|
||||
session.save( student2 );
|
||||
});
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
session.createQuery( "delete from Student" ).executeUpdate();
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubquery() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
final Criteria query = session.createCriteria( Student.class );
|
||||
query.add( Restrictions.eq( "name", "dre" ) );
|
||||
|
||||
final DetachedCriteria inner = DetachedCriteria.forClass( Student.class );
|
||||
inner.setProjection( Projections.min( "age" ) );
|
||||
|
||||
query.add( Property.forName( "age" ).eq( inner ) );
|
||||
query.add( Restrictions.eq( "name", "dre" ) );
|
||||
final List list = query.list();
|
||||
|
||||
assertThat( list.size(), is( 1 ) );
|
||||
});
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
session.enableFilter( "statusFilter" ).setParameter( "status", "deleted" );
|
||||
|
||||
final Criteria query = session.createCriteria( Student.class );
|
||||
query.add( Restrictions.eq( "name", "dre" ) );
|
||||
|
||||
final DetachedCriteria inner = DetachedCriteria.forClass( Student.class );
|
||||
inner.setProjection( Projections.min( "age" ) );
|
||||
|
||||
query.add( Property.forName( "age" ).eq( inner ) );
|
||||
query.add( Restrictions.eq( "name", "dre" ) );
|
||||
final List list = query.list();
|
||||
|
||||
assertThat( list.size(), is( 0 ) );
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubqueryWithRestrictionsOnComponentTypes() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
session.enableFilter( "statusFilter" ).setParameter( "status", "active" );
|
||||
|
||||
final Criteria query = session.createCriteria( Student.class );
|
||||
query.add( Restrictions.eq( "id", STUDENT_ID ) );
|
||||
|
||||
final DetachedCriteria subSelect = DetachedCriteria.forClass( Student.class );
|
||||
subSelect.setProjection( Projections.max( "age" ) );
|
||||
subSelect.add( Restrictions.eq( "id", STUDENT_ID ) );
|
||||
|
||||
query.add( Property.forName( "age" ).eq( subSelect ) );
|
||||
final List list = query.list();
|
||||
|
||||
assertThat( list.size(), is( 1 ) );
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubqueryWithRestrictionsOnComponentTypes2() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
session.enableFilter( "statusFilter" ).setParameter( "status", "active" );
|
||||
|
||||
final Criteria query = session.createCriteria( Student.class );
|
||||
query.add( Restrictions.eq( "id", STUDENT_ID ) );
|
||||
|
||||
final DetachedCriteria subSelect = DetachedCriteria.forClass( Student.class );
|
||||
subSelect.setProjection( Projections.max( "age" ) );
|
||||
subSelect.add( Restrictions.eq( "address", new Address( "London", "Lollard St" ) ) );
|
||||
subSelect.add( Restrictions.eq( "id", STUDENT_ID ) );
|
||||
|
||||
query.add( Property.forName( "age" ).eq( subSelect ) );
|
||||
final List list = query.list();
|
||||
|
||||
assertThat( list.size(), is( 1 ) );
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRestrictionsOnComponentTypes() {
|
||||
doInHibernate( this::sessionFactory, session -> {
|
||||
session.enableFilter( "statusFilter" ).setParameter( "status", "active" );
|
||||
|
||||
final Criteria query = session.createCriteria( Student.class );
|
||||
query.add( Restrictions.eq( "id", STUDENT_ID ) );
|
||||
query.add( Restrictions.eq( "address", new Address( "London", "Lollard St" ) ) );
|
||||
query.add( Restrictions.eq( "name", "dre" ) );
|
||||
|
||||
final List list = query.list();
|
||||
|
||||
assertThat( list.size(), is( 1 ) );
|
||||
});
|
||||
}
|
||||
|
||||
@FilterDef(
|
||||
name = "statusFilter",
|
||||
parameters = {
|
||||
@ParamDef(
|
||||
name = "status", type = "string"
|
||||
)
|
||||
}
|
||||
)
|
||||
@Filter(name = "statusFilter", condition = "STATUS = :status ")
|
||||
@Entity(name = "Student")
|
||||
@Table(name = "STUDENT")
|
||||
public static class Student {
|
||||
|
||||
@EmbeddedId
|
||||
private Identifier id;
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
|
||||
@Column(name = "STATUS")
|
||||
private String status;
|
||||
|
||||
@Embedded
|
||||
private Address address;
|
||||
|
||||
public void setId(Identifier id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public void setAddress(Address address) {
|
||||
this.address = address;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Identifier implements Serializable {
|
||||
|
||||
private Integer id1;
|
||||
|
||||
@Embedded
|
||||
private Identifier2 id2;
|
||||
|
||||
public Identifier() {
|
||||
}
|
||||
|
||||
public Identifier(Integer id1, Identifier2 id2) {
|
||||
this.id1 = id1;
|
||||
this.id2 = id2;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Identifier2 implements Serializable {
|
||||
|
||||
private Integer id3;
|
||||
|
||||
private Long id4;
|
||||
|
||||
public Identifier2() {
|
||||
}
|
||||
|
||||
public Identifier2(Integer id1, Long id2) {
|
||||
this.id3 = id1;
|
||||
this.id4 = id2;
|
||||
}
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
public static class Address implements Serializable {
|
||||
|
||||
private String city;
|
||||
|
||||
private String street;
|
||||
|
||||
public Address() {
|
||||
}
|
||||
|
||||
public Address(String city, String street) {
|
||||
this.city = city;
|
||||
this.street = street;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue