diff --git a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java index 266da8f2f5..636db6ad79 100644 --- a/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/internal/util/StringHelper.java @@ -182,10 +182,16 @@ public final class StringHelper { // there is already a right-parenthesis; we assume there will be a matching right-parenthesis. // 2) "... IN ?1", we assume that "?1" needs to be enclosed in parentheses, because there // is no left-parenthesis. + + // We need to check the placeholder is not used in `Order By FIELD(...)` (HHH-10502) + // Examples: + // " ... Order By FIELD(id,?1)", after expand parameters, the sql is "... Order By FIELD(id,?,?,?)" boolean encloseInParens = actuallyReplace && encloseInParensIfNecessary - && !( getLastNonWhitespaceCharacter( beforePlaceholder ) == '(' ); + && !( getLastNonWhitespaceCharacter( beforePlaceholder ) == '(' ) && + !( getLastNonWhitespaceCharacter( beforePlaceholder ) == ',' && getFirstNonWhitespaceCharacter( + afterPlaceholder ) == ')' ); StringBuilder buf = new StringBuilder( beforePlaceholder ); if ( encloseInParens ) { buf.append( '(' ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/ordered/HqlOrderByIdsTest.java b/hibernate-core/src/test/java/org/hibernate/test/ordered/HqlOrderByIdsTest.java new file mode 100644 index 0000000000..4cf4edf71e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/ordered/HqlOrderByIdsTest.java @@ -0,0 +1,103 @@ +/* + * 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 . + */ +package org.hibernate.test.ordered; + +import java.util.Arrays; +import java.util.List; +import javax.persistence.Entity; +import javax.persistence.Id; + +import org.hibernate.dialect.MySQL5Dialect; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; + +/** + * @author Vlad Mihalcea + */ +public class HqlOrderByIdsTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Person.class + }; + } + + @Test + @TestForIssue( jiraKey = "HHH-10502" ) + @RequiresDialect( value = MySQL5Dialect.class ) + public void testLifecycle() { + doInJPA( this::entityManagerFactory, entityManager -> { + Person person1 = new Person(); + person1.setId( 1L ); + person1.setName( "John" ); + + Person person2 = new Person(); + person2.setId( 2L ); + person2.setName( "Doe" ); + + Person person3 = new Person(); + person3.setId( 3L ); + person3.setName( "J" ); + + Person person4 = new Person(); + person4.setId( 4L ); + person4.setName( "D" ); + + entityManager.persist( person1 ); + entityManager.persist( person2 ); + entityManager.persist( person3 ); + entityManager.persist( person4 ); + } ); + doInJPA( this::entityManagerFactory, entityManager -> { + List persons = entityManager.createQuery( + "SELECT p " + + "FROM Person p " + + "WHERE p.id IN (:ids) " + + "ORDER BY FIELD(id, :ids) ", Person.class) + .setParameter( "ids" , Arrays.asList(3L, 1L, 2L)) + .getResultList(); + + assertEquals(3, persons.size()); + int index = 0; + assertEquals( Long.valueOf( 3L ), persons.get( index++ ).getId() ); + assertEquals( Long.valueOf( 1L ), persons.get( index++ ).getId() ); + assertEquals( Long.valueOf( 2L ), persons.get( index++ ).getId() ); + } ); + } + + @Entity(name = "Person") + public static class Person { + + @Id + private Long id; + + private String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + } +}