diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/CriteriaUpdateImpl.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/CriteriaUpdateImpl.java index 3e4b9a0afd..682c9d9e05 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/CriteriaUpdateImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/CriteriaUpdateImpl.java @@ -8,6 +8,7 @@ package org.hibernate.query.criteria.internal; import java.util.ArrayList; import java.util.List; +import javax.persistence.Parameter; import javax.persistence.criteria.CriteriaUpdate; import javax.persistence.criteria.Expression; import javax.persistence.criteria.Path; @@ -69,9 +70,15 @@ public class CriteriaUpdateImpl extends AbstractManipulationCriteriaQuery @SuppressWarnings("unchecked") public CriteriaUpdate set(String attributeName, Object value) { final Path attributePath = getRoot().get( attributeName ); - final Expression valueExpression = value == null - ? criteriaBuilder().nullLiteral( attributePath.getJavaType() ) - : criteriaBuilder().literal( value ); + final Expression valueExpression; + if ( value instanceof Expression ) { + valueExpression = (Expression) value; + } + else { + valueExpression = value == null + ? criteriaBuilder().nullLiteral( attributePath.getJavaType() ) + : criteriaBuilder().literal( value ); + } addAssignment( attributePath, valueExpression ); return this; } diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/CriteriaCompiler.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/CriteriaCompiler.java index e00e8ad5b2..b41adfa637 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/CriteriaCompiler.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/compile/CriteriaCompiler.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.persistence.Parameter; import javax.persistence.TypedQuery; import javax.persistence.criteria.ParameterExpression; @@ -22,6 +23,7 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.Stack; import org.hibernate.internal.util.collections.StandardStack; import org.hibernate.query.criteria.LiteralHandlingMode; +import org.hibernate.query.criteria.internal.expression.ParameterExpressionImpl; import org.hibernate.query.criteria.internal.expression.function.FunctionExpression; import org.hibernate.query.spi.QueryImplementor; import org.hibernate.sql.ast.Clause; @@ -107,8 +109,9 @@ public class CriteriaCompiler implements Serializable { ); } else { + final String name = generateParameterName(); parameterInfo = new ExplicitParameterInfo( - generateParameterName(), + name, null, criteriaQueryParameter.getJavaType() ); @@ -132,6 +135,9 @@ public class CriteriaCompiler implements Serializable { } public void bind(TypedQuery typedQuery) { + if ( literal instanceof Parameter ) { + return; + } typedQuery.setParameter( parameterName, literal ); } }; diff --git a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/expression/ParameterExpressionImpl.java b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/expression/ParameterExpressionImpl.java index 3bac2a93cc..7875341724 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/expression/ParameterExpressionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/query/criteria/internal/expression/ParameterExpressionImpl.java @@ -23,7 +23,7 @@ import org.hibernate.query.criteria.internal.compile.RenderingContext; public class ParameterExpressionImpl extends ExpressionImpl implements ParameterExpression, Serializable { - private final String name; + private String name; private final Integer position; public ParameterExpressionImpl( @@ -75,6 +75,9 @@ public class ParameterExpressionImpl @Override public String render(RenderingContext renderingContext) { final ExplicitParameterInfo parameterInfo = renderingContext.registerExplicitParameter( this ); + if ( name == null && position == null ) { + name = parameterInfo.getName(); + } return parameterInfo.render(); } } diff --git a/hibernate-core/src/test/java/org/hibernate/jpa/test/query/CriteriaUpdateWithParametersTest.java b/hibernate-core/src/test/java/org/hibernate/jpa/test/query/CriteriaUpdateWithParametersTest.java new file mode 100644 index 0000000000..c1cda6c51a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/jpa/test/query/CriteriaUpdateWithParametersTest.java @@ -0,0 +1,100 @@ +package org.hibernate.jpa.test.query; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.orm.junit.EntityManagerFactoryScope; +import org.hibernate.testing.orm.junit.Jpa; +import org.junit.jupiter.api.Test; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Query; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaUpdate; +import javax.persistence.criteria.ParameterExpression; +import javax.persistence.criteria.Root; +import javax.persistence.metamodel.EntityType; + +@Jpa( + annotatedClasses = CriteriaUpdateWithParametersTest.Person.class +) +@TestForIssue( jiraKey = "HHH-15113") +public class CriteriaUpdateWithParametersTest { + + @Test + public void testCriteriaUpdate(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + final CriteriaUpdate criteriaUpdate = criteriaBuilder.createCriteriaUpdate( Person.class ); + final Root root = criteriaUpdate.from( Person.class ); + + final ParameterExpression intValueParameter = criteriaBuilder.parameter( Integer.class ); + final ParameterExpression stringValueParameter = criteriaBuilder.parameter( String.class ); + + final EntityType personEntityType = entityManager.getMetamodel().entity( Person.class ); + + criteriaUpdate.set( root.get( personEntityType.getSingularAttribute( "age", Integer.class ) ), intValueParameter ); + + criteriaUpdate.where( criteriaBuilder.equal( + root.get( personEntityType.getSingularAttribute( "name", String.class ) ), + stringValueParameter + ) ); + + final Query query = entityManager.createQuery( criteriaUpdate ); + query.setParameter( intValueParameter, 9 ); + query.setParameter( stringValueParameter, "Luigi" ); + + query.executeUpdate(); + } + ); + } + + @Test + public void testCriteriaUpdate2(EntityManagerFactoryScope scope) { + scope.inTransaction( + entityManager -> { + final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); + final CriteriaUpdate criteriaUpdate = criteriaBuilder.createCriteriaUpdate( Person.class ); + final Root root = criteriaUpdate.from( Person.class ); + + final ParameterExpression intValueParameter = criteriaBuilder.parameter( Integer.class ); + final ParameterExpression stringValueParameter = criteriaBuilder.parameter( String.class ); + + criteriaUpdate.set( "age", intValueParameter ); + criteriaUpdate.where( criteriaBuilder.equal( root.get( "name" ), stringValueParameter ) ); + + final Query query = entityManager.createQuery( criteriaUpdate ); + query.setParameter( intValueParameter, 9 ); + query.setParameter( stringValueParameter, "Luigi" ); + + query.executeUpdate(); + } + ); + } + + @Entity(name = "Person") + public static class Person { + + @Id + private String id; + + private String name; + + private Integer age; + + public Person() { + } + + public String getId() { + return id; + } + + public String getName() { + return name; + } + + public Integer getAge() { + return age; + } + } +}