From 5ae3d1e81ec7dac0a74ec9cda904380c7d48f6f6 Mon Sep 17 00:00:00 2001 From: Andrea Boriero Date: Sat, 22 Jan 2022 19:54:37 +0100 Subject: [PATCH] CriteriaQuery#getParameters() should not return parameters internally created because value handling mode is bind --- .../criteria/HibernateCriteriaBuilder.java | 2 - .../sqm/internal/SqmCriteriaNodeBuilder.java | 17 +- .../tree/expression/JpaCriteriaParameter.java | 38 +---- .../ValueBindJpaCriteriaParameter.java | 37 +++++ .../sqm/tree/select/SqmSelectStatement.java | 8 +- .../compliance/CriteriaGetParametersTest.java | 153 ++++++++++++++++++ 6 files changed, 204 insertions(+), 51 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaGetParametersTest.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java index 64d6e06bff..a04b86cc80 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/HibernateCriteriaBuilder.java @@ -274,8 +274,6 @@ public interface HibernateCriteriaBuilder extends CriteriaBuilder { @Override JpaParameterExpression parameter(Class paramClass, String name); - JpaParameterExpression parameter(Class paramClass, T value); - @Override JpaExpression concat(Expression x, Expression y); diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java index 1c1b1de195..d74b61d050 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/internal/SqmCriteriaNodeBuilder.java @@ -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 JpaCriteriaParameter parameter(Class paramClass, String name) { - return (JpaCriteriaParameter) parameter( paramClass, name, null ); - } - @Override - public JpaParameterExpression parameter(Class paramClass, T value) { - return parameter( paramClass, null, value ); - } - - @SuppressWarnings("unchecked") - private JpaParameterExpression parameter(Class paramClass, String name, T value) { final BasicType basicType = getTypeConfiguration().getBasicTypeForJavaType( paramClass ); if ( basicType == null ) { final BindableType 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 diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java index d3de97f24a..a4586b383f 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/JpaCriteriaParameter.java @@ -33,16 +33,8 @@ public class JpaCriteriaParameter extends AbstractSqmExpression implements SqmParameter, QueryParameterImplementor, DomainResultProducer { private final String name; - private final T value; private boolean allowsMultiValuedBinding; - public JpaCriteriaParameter( - BindableType type, - boolean allowsMultiValuedBinding, - NodeBuilder nodeBuilder) { - this( null, type, allowsMultiValuedBinding, nodeBuilder ); - } - public JpaCriteriaParameter( String name, BindableType type, @@ -50,19 +42,6 @@ public class JpaCriteriaParameter NodeBuilder nodeBuilder) { super( toSqmType( type, nodeBuilder ), nodeBuilder ); this.name = name; - this.value = null; - this.allowsMultiValuedBinding = allowsMultiValuedBinding; - } - - public JpaCriteriaParameter( - String name, - BindableType 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 ); } - public JpaCriteriaParameter(BindableType 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 @Override public void appendHqlString(StringBuilder sb) { - if ( getName() == null ) { - sb.append( value ); - } - else { - sb.append( ':' ); - sb.append( getName() ); - } + sb.append( ':' ); + sb.append( getName() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java new file mode 100644 index 0000000000..3da601f996 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/expression/ValueBindJpaCriteriaParameter.java @@ -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 extends JpaCriteriaParameter{ + private final T value; + + public ValueBindJpaCriteriaParameter( + BindableType 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 ); + } + +} diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java index 074fcdb227..1e273fc5e5 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/select/SqmSelectStatement.java @@ -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 extends AbstractSqmSelectQuery implements // // for a "finalized" set of parameters, use `#resolveParameters` instead assert querySource == SqmQuerySource.CRITERIA; - return (Set>) (Set) getSqmParameters(); + final Set> sqmParameters = (Set>) (Set) getSqmParameters(); + return sqmParameters.stream() + .filter( parameterExpression -> !( parameterExpression instanceof ValueBindJpaCriteriaParameter ) ) + .collect( Collectors.toSet() ); } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaGetParametersTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaGetParametersTest.java new file mode 100644 index 0000000000..8db9ad6ad3 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/compliance/CriteriaGetParametersTest.java @@ -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 query = criteriaBuilder.createQuery( Person.class ); + final Root person = query.from( Person.class ); + + query.select( person ); + query.where( criteriaBuilder.equal( person.get( "age" ), 30 ) ); + + final Set> 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 cquery = cbuilder.createQuery( Person.class ); + Root customer = cquery.from( Person.class ); + final EntityType Person_ = entityManager.getMetamodel().entity( Person.class ); + Join a = customer.join( Person_.getCollection( "addresses", Address.class ) ); + cquery.where( cbuilder.like( a.get( "street" ), "sh\\_ll", + cbuilder.literal( '\\' ) + ) ); + cquery.select( customer ); + TypedQuery tquery = entityManager.createQuery( cquery ); + tquery.setMaxResults( 5 ); + List 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
addresses; + + Person() { + } + + public Person(Integer id, String name, Integer age) { + this.id = id; + this.name = name; + this.age = age; + } + + + public Collection
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; + } +}