如何使用分页从Spring Data JPA中的SELECT子句按别名对投影进行排序?

Lyn*_*ynx 7 java spring-data-jpa

我创建了这两个实体来证明我的问题:

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<>();
}
Run Code Online (Sandbox Code Playgroud)

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我想编写一个查询,显示来自OwnerEntity的信息和一个加入的ChildEntity.我创建了一个投影:

OwnerEntityProjection.java:

public interface OwnerEntityProjection {

    Long getId();

    String getName();

}
Run Code Online (Sandbox Code Playgroud)

我的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);

}
Run Code Online (Sandbox Code Playgroud)

现在,当我运行这个简单的测试:

@Test
public void findAllPaginatedTest() {
    Pageable pageRequest = new PageRequest(0, 3, Sort.Direction.ASC, "name");
    Slice<OwnerEntityProjection> OwnerEntityProjectionsPaginated =
            ownerEntityRepository.findAllPaginated(pageRequest);
}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

org.hibernate.QueryException: could not resolve property: name of: com.example.domain.OwnerEntity
Run Code Online (Sandbox Code Playgroud)

我还在日志中检查了生成的JQPL:

... order by ownerEntity.name asc
Run Code Online (Sandbox Code Playgroud)

正如您所见,Spring Data Jpa从FROM子句附加了第一个实体别名.我发现如果我将PageRequest更改为:

new PageRequest(0, 3, Sort.Direction.ASC, "childEntities.name");
Run Code Online (Sandbox Code Playgroud)

它没有错误,但我不想将排序属性传递给存储库,其中包含JPQL查询中的别名.我想传递存储库方法返回的投影中直接存在的属性.如果我在JPQL查询中手动编写ORDER BY,如下所示:

... ORDER BY name ASC
Run Code Online (Sandbox Code Playgroud)

然后这个查询也运行没有任何错误,因为我可以从ORDER BY子句引用SELECT子句中的别名.有没有办法告诉Spring Data Jpa执行排序而不添加FROM或JOIN子句中的别名?像这样的东西:

new PageRequest(0, 3, Sort.Direction.ASC, "name") ===> ORDER BY name asc
Run Code Online (Sandbox Code Playgroud)

Dar*_*idl 6

这是Spring Data中的错误:别名检测不正确。这里也有报道。

在该方法的QueryUtilsapplySorting,仅检测到(外部)联接别名和带有一对括号的函数别名。属性的简单别名当前不起作用。

一种解决方法是JpaSort.unsafe在构建时在括号中使用别名PageRequest,例如

PageRequest.of(0, 3, JpaSort.unsafe(Sort.Direction.ASC, "(name)"))
Run Code Online (Sandbox Code Playgroud)

顾名思义,这在基于用户输入进行动态排序时是不安全的,因此只能用于硬编码排序。