diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/Product.java b/src/test/java/org/springframework/data/jpa/domain/sample/Product.java index d63e78dfbe..59804719a6 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/Product.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/Product.java @@ -8,8 +8,8 @@ public class Product { @Id @GeneratedValue private Long id; - public Long getId() { return id; } + String name; } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java index fd88c5e476..5a84d9df47 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/QueryUtilsIntegrationTests.java @@ -18,6 +18,7 @@ import static java.util.Collections.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import static org.springframework.data.jpa.repository.query.QueryUtils.*; import java.util.Collections; import java.util.List; @@ -38,6 +39,7 @@ import javax.persistence.criteria.From; import javax.persistence.criteria.Join; import javax.persistence.criteria.JoinType; +import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import javax.persistence.spi.PersistenceProvider; import javax.persistence.spi.PersistenceProviderResolver; @@ -46,7 +48,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; - import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.jpa.domain.sample.Category; @@ -139,8 +140,8 @@ void createsLeftJoinForNonOptionalToOneWithNestedOptional() { CriteriaQuery query = builder.createQuery(InvoiceItem.class); Root root = query.from(InvoiceItem.class); - QueryUtils - .toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), false); + QueryUtils.toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), + false); assertThat(getInnerJoins(root)).hasSize(1); // join invoice Join rootInnerJoin = getInnerJoins(root).iterator().next(); @@ -162,8 +163,8 @@ void reusesLeftJoinForNonOptionalToOneWithNestedOptional() { root.join("invoice", JoinType.LEFT).join("order", JoinType.LEFT); // when navigating through a path with nested optionals - QueryUtils - .toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), false); + QueryUtils.toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), + false); // assert that existing joins are reused and no additional joins are created assertThat(getInnerJoins(root)).isEmpty(); // no inner join invoice @@ -185,8 +186,8 @@ void reusesInnerJoinForNonOptionalToOneWithNestedOptional() { // given an existing inner join an nested optional root.join("invoice").join("order"); - QueryUtils - .toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), false); + QueryUtils.toExpressionRecursively(root, PropertyPath.from("invoice.order.customer.name", InvoiceItem.class), + false); // assert that no useless left joins are created assertThat(getInnerJoins(root)).hasSize(1); // join invoice @@ -307,7 +308,7 @@ void toOrdersCanSortByJoinColumn() { Sort sort = Sort.by(Direction.ASC, "age"); - List orders = QueryUtils.toOrders(sort, join, builder); + List orders = toOrders(sort, join, builder); assertThat(orders).hasSize(1); } @@ -329,6 +330,29 @@ void demonstrateDifferentBehavorOfGetJoin() { assertThat(root.getJoins()).hasSize(getNumberOfJoinsAfterCreatingAPath()); } + @Test // GH-2253 + void orderByMustReuseAJoin() { + + CriteriaBuilder builder = em.getCriteriaBuilder(); + CriteriaQuery query = builder.createQuery(Category.class); + Root root = query.from(Category.class); + + // this represents what is done by the specification in the issue + // + query.distinct(true); // this does not belong into a specification + root.fetch("product", JoinType.LEFT); + final Predicate idEquals1 = builder.equal(root.get("id"), 1L); + query.where(idEquals1); + + // Also order by a field of the entity referenced by the fetch from the "Specification" + Sort sort = Sort.by(Direction.ASC, "product.name"); + + query.orderBy(toOrders(sort, root, builder)); + + assertThat(root.getJoins()).hasSize(1); + + } + int getNumberOfJoinsAfterCreatingAPath() { return 0; } @@ -357,6 +381,7 @@ static class Merchant { @SuppressWarnings("unused") static class Address { @Id String id; + String name; @OneToOne(mappedBy = "address") Merchant merchant; }