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