JPA 规范按本机 SQL 字段排序

JPA Specification sort by native SQL field

我正在使用 Spring Boot v1.5.3 在我的代码中,我有一个包含很多条件的搜索操作。

public Page<ParentObject> search(Pageable pageable) {
    Specification<ParentObject> specification = (root, cq, cb) -> {
        Predicate p = cb.and(
            cb.equals(root.get("child").get("id"), "someValue"),
            // a lot of predicates appended by conditions
            );
        return p;
    };
    Sort newSort = pageable.getSort().and(new Sort(Sort.Direction.ASC, "child.id"));
    pageable = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), newSort)
    Page<ParentObject> result = parentObjectRepository.findAll(specification, pageable);
    return result;
}

问题是我的 parent table 包含带索引的 child_id 字段。我希望 SQL 像:

SELECT .... FROM parent p INNER JOIN child c ON c.id = p.child_id WHERE ... 
ORDER BY p.child_id ASC;

但结果是:

SELECT .... FROM parent p INNER JOIN child c ON c.id = p.child_id WHERE ... 
ORDER BY c.id ASC;

注意 ORDER BY 子句。如果我有 c.id 索引不涉及并且搜索很慢。如果我有 ORDER BY p.child_id 它工作得更快。 我尝试使用

Sort newSort = pageable.getSort().and(new Sort(Sort.Direction.ASC, "child"));

但它没有按预期工作。 实体:

public class ParentObject {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "child_id", referencedColumnName = "id")
    private ChildObject child;
}

我不能用原生的SQL替换这个,因为这个搜索规范包含30+if/else个语句,重写代码会花很多时间

我该如何解决这个问题?预先感谢您的回答。

终于找到了解决办法。正如我们所知,ORM 通过实体与数据库一起工作。 我所有的实体都有 String (UUID) 值作为 id。因此,我将以下代码添加到我的 ParentObject 中:

public class ParentObject {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "child_id", referencedColumnName = "id")
    private ChildObject child;

    @Column(name = "child_id", insertable = false, updatable = false)
    private String childId;
}

如您所见,我在这里添加了包含子对象 ID 的简单字段。将此字段标记为 insertable = false, updatable = false 很重要,我们无法在代码中更改此字段,这是只读字段。但这使我们可以按 parent.child_id 而不是 child.id 对结果进行排序。如果我们需要替换子对象,只需要调用 setChild(newValue).

最后,我的搜索方法现在看起来像:

public Page<ParentObject> search(Pageable pageable) {
    Specification<ParentObject> specification = (root, cq, cb) -> {
        Predicate p = cb.and(
            cb.equals(root.get("child").get("id"), "someValue"),
            // a lot of predicates appended by conditions
            );
        return p;
    };
     // IMPORTANT change in next line
    Sort newSort = pageable.getSort().and(new Sort(Sort.Direction.ASC, "childId"));
    pageable = new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), newSort)
    Page<ParentObject> result = parentObjectRepository.findAll(specification, pageable);
    return result;
}

结果我有以下 SQL 查询:

SELECT .... FROM parent p INNER JOIN child c ON c.id = p.child_id WHERE ... 
ORDER BY p.child_id ASC;

希望这对某人有用