How to sort projection by alias from SELECT clause in Spring Data JPA paginated?
I created these two questions to demonstrate my problem:
OwnerEntity.java:
@Entity
public class OwnerEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Size(min = 1)
@OneToMany(mappedBy = "ownerEntity", cascade = CascadeType.ALL)
private Set<ChildEntity> childEntities = new HashSet<>();
}
ChildEntity.java:
@Entity
public class ChildEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@NotNull
@ManyToOne(optional = false)
private OwnerEntity ownerEntity;
public ChildEntity() {
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public OwnerEntity getOwnerEntity() {
return ownerEntity;
}
public void setOwnerEntity(OwnerEntity ownerEntity) {
this.ownerEntity = ownerEntity;
}
}
I want to write a query that will display information from OwnerEntity and one associated ChildEntity. I created a projection:
OwnerEntityProjection.java:
public interface OwnerEntityProjection {
Long getId();
String getName();
}
My JpaRepository:
public interface OwnerEntityRepository extends JpaRepository<OwnerEntity, Long> {
@Query(" SELECT " +
" ownerEntity.id AS id, " +
" childEntities.name AS name " +
" FROM OwnerEntity ownerEntity " +
" JOIN ownerEntity.childEntities childEntities ")
// There must be also WHERE clause, but for demonstration it is omitted
Slice<OwnerEntityProjection> findAllPaginated(Pageable pageRequest);
}
Now when I run this simple test:
@Test
public void findAllPaginatedTest() {
Pageable pageRequest = new PageRequest(0, 3, Sort.Direction.ASC, "name");
Slice<OwnerEntityProjection> OwnerEntityProjectionsPaginated =
ownerEntityRepository.findAllPaginated(pageRequest);
}
I got the following error:
org.hibernate.QueryException: could not resolve property: name of: com.example.domain.OwnerEntity
I also checked the generated JQPL in the logs:
... order by ownerEntity.name asc
As you can see Spring Data Jpa adds the first entity alias from the FROM clause. I found that if I changed the PageRequest to this:
new PageRequest(0, 3, Sort.Direction.ASC, "childEntities.name");
It works without error, but I don't want to pass the ordering property to the repository with the aliases that are found somewhere in the JPQL query. I want to pass a property directly present in the projection that the repository method returns. If I write the ORDER BY manually in a JPQL query like this:
... ORDER BY name ASC
Then this query also runs without error because I can refer to aliases in the SELECT clause from the ORDER BY clause. Is there a way to tell Spring Data Jpa to fulfill the order without adding aliases from FROM or JOIN clauses? Something like that:
new PageRequest(0, 3, Sort.Direction.ASC, "name") ===> ORDER BY name asc
source to share
This is a bug in Spring Data: aliases were not detected correctly. It has also been reported here .
In QueryUtils , a method containsapplySorting
only (external) join aliases and function aliases with exactly one pair of parentheses. A simple alias for a property does not currently work.
One workaround for this is to use JpaSort.unsafe
with parenthesized aliases when constructing PageRequest
, for example
PageRequest.of(0, 3, JpaSort.unsafe(Sort.Direction.ASC, "(name)"))
It's not safe, as the name suggests, when sorting dynamically based on user input, so it should only be used for hard-coded sorting.
source to share