CriteriaQuery#getParameters() should not return parameters internally created because value handling mode is bind

This commit is contained in:
Andrea Boriero 2022-01-22 19:54:37 +01:00 committed by Steve Ebersole
parent 09299e1f41
commit 5ae3d1e81e
6 changed files with 204 additions and 51 deletions

View File

@ -274,8 +274,6 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder {
@Override
<T> JpaParameterExpression<T> parameter(Class<T> paramClass, String name);
<T> JpaParameterExpression<T> parameter(Class<T> paramClass, T value);
@Override
JpaExpression<String> concat(Expression<String> x, Expression<String> y);

View File

@ -72,6 +72,7 @@ import org.hibernate.query.sqm.tree.domain.SqmPluralValuedSimplePath;
import org.hibernate.query.sqm.tree.domain.SqmSetJoin;
import org.hibernate.query.sqm.tree.domain.SqmSingularJoin;
import org.hibernate.query.sqm.tree.expression.JpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.ValueBindJpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmBinaryArithmetic;
import org.hibernate.query.sqm.tree.expression.SqmCaseSearched;
import org.hibernate.query.sqm.tree.expression.SqmCaseSimple;
@ -996,16 +997,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
@Override
public <T> JpaCriteriaParameter<T> parameter(Class<T> paramClass, String name) {
return (JpaCriteriaParameter<T>) parameter( paramClass, name, null );
}
@Override
public <T> JpaParameterExpression<T> parameter(Class<T> paramClass, T value) {
return parameter( paramClass, null, value );
}
@SuppressWarnings("unchecked")
private <T> JpaParameterExpression<T> parameter(Class<T> paramClass, String name, T value) {
final BasicType<T> basicType = getTypeConfiguration().getBasicTypeForJavaType( paramClass );
if ( basicType == null ) {
final BindableType<T> parameterType;
@ -1019,13 +1011,12 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return new JpaCriteriaParameter<>(
name,
parameterType,
value,
true,
this
);
}
else {
return new JpaCriteriaParameter<>( name, basicType, value, false, this );
return new JpaCriteriaParameter<>( name, basicType, false, this );
}
}
@ -1419,7 +1410,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return literal( value, typeInferenceSource );
}
return new JpaCriteriaParameter<>(
return new ValueBindJpaCriteriaParameter<>(
resolveInferredParameterType( value, typeInferenceSource, getTypeConfiguration() ),
value,
this
@ -1456,7 +1447,7 @@ public class SqmCriteriaNodeBuilder implements NodeBuilder, SqmCreationContext,
return literal( value );
}
else {
return new JpaCriteriaParameter<>(
return new ValueBindJpaCriteriaParameter<>(
queryEngine.getTypeConfiguration().getSessionFactory().resolveParameterBindType( value ),
value,
this

View File

@ -33,16 +33,8 @@ public class JpaCriteriaParameter<T>
extends AbstractSqmExpression<T>
implements SqmParameter<T>, QueryParameterImplementor<T>, DomainResultProducer<T> {
private final String name;
private final T value;
private boolean allowsMultiValuedBinding;
public JpaCriteriaParameter(
BindableType<T> type,
boolean allowsMultiValuedBinding,
NodeBuilder nodeBuilder) {
this( null, type, allowsMultiValuedBinding, nodeBuilder );
}
public JpaCriteriaParameter(
String name,
BindableType<T> type,
@ -50,19 +42,6 @@ public class JpaCriteriaParameter<T>
NodeBuilder nodeBuilder) {
super( toSqmType( type, nodeBuilder ), nodeBuilder );
this.name = name;
this.value = null;
this.allowsMultiValuedBinding = allowsMultiValuedBinding;
}
public JpaCriteriaParameter(
String name,
BindableType<T> type,
T value,
boolean allowsMultiValuedBinding,
NodeBuilder nodeBuilder) {
super( toSqmType( type, nodeBuilder ), nodeBuilder );
this.name = name;
this.value = value;
this.allowsMultiValuedBinding = allowsMultiValuedBinding;
}
@ -75,19 +54,13 @@ public class JpaCriteriaParameter<T>
);
}
public JpaCriteriaParameter(BindableType<T> type, T value, NodeBuilder nodeBuilder) {
super( toSqmType( type, nodeBuilder ), nodeBuilder );
this.name = null;
this.value = value;
}
@Override
public String getName() {
return name;
}
public T getValue() {
return value;
return null;
}
@Override
@ -159,13 +132,8 @@ public class JpaCriteriaParameter<T>
@Override
public void appendHqlString(StringBuilder sb) {
if ( getName() == null ) {
sb.append( value );
}
else {
sb.append( ':' );
sb.append( getName() );
}
sb.append( ':' );
sb.append( getName() );
}
@Override

View File

@ -0,0 +1,37 @@
/*
* 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.query.sqm.tree.expression;
import org.hibernate.query.BindableType;
import org.hibernate.query.sqm.NodeBuilder;
/**
* It is a JpaCriteriaParameter created from a value when ValueHandlingMode is equal to BIND
*
* @see org.hibernate.query.criteria.ValueHandlingMode
*/
public class ValueBindJpaCriteriaParameter<T> extends JpaCriteriaParameter<T>{
private final T value;
public ValueBindJpaCriteriaParameter(
BindableType<T> type,
T value,
NodeBuilder nodeBuilder) {
super( null, type, false, nodeBuilder );
this.value = value;
}
public T getValue() {
return value;
}
@Override
public void appendHqlString(StringBuilder sb) {
sb.append( value );
}
}

View File

@ -10,6 +10,8 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.Expression;
import jakarta.persistence.criteria.Order;
@ -26,6 +28,7 @@ import org.hibernate.query.sqm.SemanticQueryWalker;
import org.hibernate.query.sqm.SqmQuerySource;
import org.hibernate.query.sqm.internal.SqmUtil;
import org.hibernate.query.sqm.tree.SqmStatement;
import org.hibernate.query.sqm.tree.expression.ValueBindJpaCriteriaParameter;
import org.hibernate.query.sqm.tree.expression.SqmParameter;
import org.hibernate.query.sqm.tree.from.SqmFromClause;
import org.hibernate.query.sqm.tree.jpa.ParameterCollector;
@ -177,7 +180,10 @@ public class SqmSelectStatement<T> extends AbstractSqmSelectQuery<T> implements
//
// for a "finalized" set of parameters, use `#resolveParameters` instead
assert querySource == SqmQuerySource.CRITERIA;
return (Set<ParameterExpression<?>>) (Set<?>) getSqmParameters();
final Set<ParameterExpression<?>> sqmParameters = (Set<ParameterExpression<?>>) (Set<?>) getSqmParameters();
return sqmParameters.stream()
.filter( parameterExpression -> !( parameterExpression instanceof ValueBindJpaCriteriaParameter ) )
.collect( Collectors.toSet() );
}
@Override

View File

@ -0,0 +1,153 @@
/*
* 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.jpa.compliance;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Join;
import jakarta.persistence.criteria.ParameterExpression;
import jakarta.persistence.criteria.Root;
import jakarta.persistence.metamodel.EntityType;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Jpa(
annotatedClasses = {
CriteriaGetParametersTest.Person.class,
CriteriaGetParametersTest.Address.class
}
// ,
// properties = @Setting(name = AvailableSettings.JPA_QUERY_COMPLIANCE, value = "true")
)
public class CriteriaGetParametersTest {
@BeforeEach
public void setUp(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager -> {
entityManager.persist( new Person( 1, "Andrea", 5 ) );
entityManager.persist( new Person( 2, "Andrea", 35 ) );
}
);
}
@AfterEach
public void tearDown(EntityManagerFactoryScope scope) {
scope.inTransaction(
entityManager ->
entityManager.createQuery( "delete from Person" ).executeUpdate()
);
}
@Test
public void testGetParameters(EntityManagerFactoryScope scope) {
scope.inEntityManager(
entityManager -> {
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<Person> query = criteriaBuilder.createQuery( Person.class );
final Root<Person> person = query.from( Person.class );
query.select( person );
query.where( criteriaBuilder.equal( person.get( "age" ), 30 ) );
final Set<ParameterExpression<?>> parameters = query.getParameters();
entityManager.createQuery( query ).getResultList();
assertThat( parameters, notNullValue() );
assertTrue( parameters.isEmpty() );
}
);
}
@Test
public void likeExpStringExpTest(EntityManagerFactoryScope scope) {
scope.inEntityManager(
entityManager -> {
CriteriaBuilder cbuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Person> cquery = cbuilder.createQuery( Person.class );
Root<Person> customer = cquery.from( Person.class );
final EntityType<Person> Person_ = entityManager.getMetamodel().entity( Person.class );
Join<Person, Address> a = customer.join( Person_.getCollection( "addresses", Address.class ) );
cquery.where( cbuilder.like( a.get( "street" ), "sh\\_ll",
cbuilder.literal( '\\' )
) );
cquery.select( customer );
TypedQuery<Person> tquery = entityManager.createQuery( cquery );
tquery.setMaxResults( 5 );
List<Person> clist = tquery.getResultList();
}
);
}
@Entity(name = "Person")
@Table(name = "PERSON_TABLE")
public static class Person {
@Id
private Integer id;
private String name;
private Integer age;
@ManyToMany
private Collection<Address> addresses;
Person() {
}
public Person(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public Collection<Address> getAddresses() {
return addresses;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
}
@Entity
public static class Address {
@Id
private Integer id;
private String street;
}
}