diff --git a/hibernate-core/src/test/java/org/hibernate/test/hql/size/WhereClauseOrderBySizeTest.java b/hibernate-core/src/test/java/org/hibernate/test/hql/size/WhereClauseOrderBySizeTest.java new file mode 100644 index 0000000000..81d7170b07 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/hql/size/WhereClauseOrderBySizeTest.java @@ -0,0 +1,195 @@ +/* + * 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.hql.size; + +import org.hibernate.annotations.ResultCheckStyle; +import org.hibernate.annotations.SQLDelete; +import org.hibernate.annotations.Where; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +import org.hibernate.testing.TestForIssue; +import org.junit.After; +import org.junit.Test; + +import javax.persistence.CascadeType; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.TypedQuery; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.hibernate.testing.transaction.TransactionUtil.doInJPA; +import static org.junit.Assert.assertEquals; + +@TestForIssue(jiraKey = "HHH-14585") +public class WhereClauseOrderBySizeTest extends BaseEntityManagerFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Person.class, Book.class }; + } + + @Test + public void testSizeAsOrderByExpression() { + doInJPA( + this::entityManagerFactory, + entityManager -> { + // initial situation: Alice has 1 book, Bob none + final Person alice = new Person( "Alice" ); + entityManager.persist( alice ); + + final Book book1 = new Book(); + book1.setOwner( alice ); + entityManager.persist( book1 ); + + final Person bob = new Person( "Bob" ); + entityManager.persist( bob ); + + final TypedQuery orderByBroken = entityManager.createQuery( + "SELECT p FROM Person p ORDER BY size(p.books) DESC", + Person.class + ); + final TypedQuery orderByWorking = entityManager.createQuery( + "SELECT p FROM Person p ORDER BY p.books.size DESC", + Person.class + ); + + List dbPeopleBroken = orderByBroken.getResultList(); + List dbPeopleWorking = orderByWorking.getResultList(); + assertEquals( Arrays.asList( alice, bob ), dbPeopleWorking ); + assertEquals( dbPeopleWorking, dbPeopleBroken ); + + // add 2 books to Bob + final Book book2 = new Book(); + book2.setOwner( bob ); + entityManager.persist( book2 ); + + final Book book3 = new Book(); + book3.setOwner( bob ); + entityManager.persist( book3 ); + + dbPeopleBroken = orderByBroken.getResultList(); + dbPeopleWorking = orderByWorking.getResultList(); + assertEquals( Arrays.asList( bob, alice ), dbPeopleWorking ); + assertEquals( dbPeopleWorking, dbPeopleBroken ); + + // remove (soft-deleting) both Bob's books + entityManager.remove( book2 ); + entityManager.remove( book3 ); + + // result lists are not equal anymore + dbPeopleBroken = orderByBroken.getResultList(); + dbPeopleWorking = orderByWorking.getResultList(); + assertEquals( Arrays.asList( alice, bob ), dbPeopleWorking ); + assertEquals( dbPeopleWorking, dbPeopleBroken ); + } + ); + } + + @After + public void cleanupDatabase() { + doInJPA( + this::entityManagerFactory, + entityManager -> { + for ( Book book : entityManager.createQuery( "from Book", Book.class ).getResultList() ) { + entityManager.remove( book ); + } + for ( Person person : entityManager.createQuery( "from Person", Person.class ).getResultList() ) { + entityManager.remove( person ); + } + } + ); + } + + @Entity(name = "Person") + public static class Person { + @Id + @GeneratedValue + private Long id; + private String name; + @OneToMany(mappedBy = "owner", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) + private List books = new ArrayList<>(); + + public Person(String name) { + this.name = 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; + } + + public List getBooks() { + return books; + } + + public void setBooks(List books) { + this.books = books; + } + + @Override + public String toString() { + return "Person{" + + "id=" + id + + ", name='" + name + '\'' + + '}'; + } + } + + @Entity(name = "Book") + @SQLDelete(sql = "UPDATE Book SET deleted = true WHERE id = ?", check = ResultCheckStyle.COUNT) + @Where(clause = "deleted = false") + public static class Book { + @Id + @GeneratedValue + private Long id; + private Boolean deleted = false; + @ManyToOne + private Person owner; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Boolean getDeleted() { + return deleted; + } + + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } + + public Person getOwner() { + return owner; + } + + public void setOwner(Person owner) { + this.owner = owner; + } + } +}