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

18

我创建了这两个实体来展示我的问题:

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;
    }
}

我希望编写一个查询,可以显示OwnerEntity和一个已连接的ChildEntity的信息。我创建了一个投影:

OwnerEntityProjection.java:

public interface OwnerEntityProjection {

    Long getId();

    String getName();

}

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

}

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

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

我得到了以下错误:

org.hibernate.QueryException: could not resolve property: name of: com.example.domain.OwnerEntity

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

... order by ownerEntity.name asc

您可以看到Spring Data Jpa在FROM子句中添加了第一个实体别名。我发现如果我将PageRequest更改为:

new PageRequest(0, 3, Sort.Direction.ASC, "childEntities.name");

它能够正常运行,但我不想在JPQL查询中传递具有别名的排序属性。我想传递直接存在于仓库方法返回的投影中的属性。如果我手动在JPQL查询中编写ORDER BY,就像这样:

... ORDER BY name ASC

然后,这个查询也可以正常运行,因为我可以在ORDER BY子句中引用SELECT子句中的别名。 有没有办法告诉Spring Data Jpa在排序时不需要在FROM或JOIN子句中附加别名?类似于这样:

new PageRequest(0, 3, Sort.Direction.ASC, "name") ===> ORDER BY name asc

4
你解决了吗?我也面临同样的问题。 - VJ.
3个回答

23

这是Spring Data中的一个错误:别名没有正确被检测到。此问题已经在此处报告。

applySorting方法的QueryUtils中,只能检测到具有恰好一对括号的(外部)联接别名和函数别名。目前无法使用属性的简单别名。

解决此问题的一种方法是,在构建PageRequest时使用带有括号的别名来使用JpaSort.unsafe,例如:

PageRequest.of(0, 3, JpaSort.unsafe(Sort.Direction.ASC, "(name)"))

正如其名称所示,这是不安全的,因为它基于用户输入进行动态排序,所以它只应该用于硬编码排序。


1
感谢您提供的解决方案。它救了我的一天。 - Manak Bhardwaj
1
这是非常棒的信息,你救了我。 - PAA
这个findAll(spec, pageable)不起作用。它是关于字段类型的验证:MessageEntity类型中没有名为length(field)的属性! - Дима Годиков

2
在可分页控制器的入口点上下文中,我将Dario Seidl的答案改编为以下内容:
public static Pageable parenthesisEncapsulation(final Pageable pageable) {

    Sort sort = Sort.by(Collections.emptyList());
    for (final Sort.Order order : pageable.getSort()) {
        if (order.getProperty().matches("^\\(.*\\)$")) {
            sort = sort.and(JpaSort.unsafe(order.getDirection(), order.getProperty()));
        } else {
            sort = sort.and(Sort.by(order.getDirection(), order.getProperty()));
        }
    }
    return PageRequest
        .of(pageable.getPageNumber(), pageable.getPageSize(), sort);
}

我可以这样给我的控制器排序指令:

/myroute?sort=(unsafesortalias),desc&sort=safesortfield,asc


0
    String orderSort=page.getSort().toString();                
    Pageable page1 = 
    PageRequest.of(page.getPageNumber(),page.getPageSize(),
    JpaSort.unsafe(Sort.Direction.valueOf
    (orderSort.substring(page.getSort().toString().indexOf(':')+2,
    orderSort.length())),
    orderSort.substring(0,page.getSort().toString().indexOf(')')+1)));

1
虽然这段代码可能解决了问题,但是包括解释它如何以及为什么解决了问题将有助于提高您的帖子质量,并可能导致更多的赞。请记住,您正在回答未来读者的问题,而不仅仅是现在提问的人。请[编辑]您的答案以添加解释并指出适用的限制和假设。 - Yunnosch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接