JPA Спецификация сортировка по родному SQL полю - PullRequest
0 голосов
/ 04 марта 2020

Я использую 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 содержит поле 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"));

, но он не работает должным образом. Entity:

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

Я не могу заменить это на собственный SQL, потому что эта спецификация поиска содержит более 30 операторов if / else, и для переписывания кода потребуется много времени.

Как я могу решить эту проблему? Заранее спасибо за ваши ответы.

1 Ответ

0 голосов
/ 05 марта 2020

Наконец-то я нашел решение. Как мы знаем, ORM работает с базой данных через сущности. Все мои сущности имеют строковые (UUID) значения в качестве идентификаторов. Итак, я добавил следующий код в мой 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;
}

Как видите, здесь я добавил простое поле, содержащее идентификатор дочернего объекта. Важно пометить это поле как 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 query:

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

Надеюсь, это кому-нибудь пригодится

...