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

      

+5


source to share


1 answer


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.

0


source







All Articles