HHH-10991 - Wrong order parameter binding when filters are used in conjunction with component type parameters and subqueries

This commit is contained in:
Andrea Boriero 2016-07-26 16:36:39 +02:00 committed by Vlad Mihalcea
parent 9b9becb903
commit 7c361fee3a
2 changed files with 289 additions and 3 deletions

View File

@ -26,6 +26,7 @@ import org.hibernate.internal.util.EntityPrinter;
import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.query.internal.QueryParameterBindingsImpl; import org.hibernate.query.internal.QueryParameterBindingsImpl;
import org.hibernate.transform.ResultTransformer; import org.hibernate.transform.ResultTransformer;
import org.hibernate.type.ComponentType;
import org.hibernate.type.Type; import org.hibernate.type.Type;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -535,7 +536,6 @@ public final class QueryParameters {
StringBuilder result = new StringBuilder(); StringBuilder result = new StringBuilder();
List parameters = new ArrayList(); List parameters = new ArrayList();
List parameterTypes = new ArrayList(); List parameterTypes = new ArrayList();
int positionalIndex = 0; int positionalIndex = 0;
while ( tokens.hasMoreTokens() ) { while ( tokens.hasMoreTokens() ) {
final String token = tokens.nextToken(); final String token = tokens.nextToken();
@ -564,12 +564,26 @@ public final class QueryParameters {
} }
} }
else { else {
result.append( token );
if ( "?".equals( token ) && positionalIndex < getPositionalParameterValues().length ) { 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] ); parameters.add( getPositionalParameterValues()[positionalIndex] );
parameterTypes.add( getPositionalParameterTypes()[positionalIndex] ); parameterTypes.add( type );
positionalIndex++; positionalIndex++;
} }
result.append( token );
} }
} }
processedPositionalParameterValues = parameters.toArray(); 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() { public String getFilteredSQL() {
return processedSQL; return processedSQL;
} }

View File

@ -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;
}
}
}