Spring Data JPA: CriteriaQuery для получения сущностей с максимальным значением для каждого уникального внешнего ключа - PullRequest
0 голосов
/ 25 июня 2019

Класс Event:

@Entity
public class Event {

    @Id
    private Integer id;

    @ManyToOne(cascade = CascadeType.ALL)
    private Company company;

    @Column
    private Long time;

    ...

}

Я хочу иметь класс EventFilter (реализующий Specification), который будет выдавать CriteriaQuery для выбора сущностей так же, как следующий SQL-запрос:

SELECT *
FROM events e1
WHERE e1.time = (
    SELECT MAX(time)
    FROM events e2
    WHERE e1.company_id = c2.company_id
)

Отфильтрованный результат будет содержать только события с уникальным Company и максимальным time значением для компании.

Это класс EventFilter с тем, что я закончил:

public class EventFilter implements Specification<Event> {

    @Override
    public Predicate toPredicate(Root<Event> root, CriteriaQuery<?> q, CriteriaBuilder cb) {
        Subquery<Long> subquery = q.subquery(Long.class);
        Root<Event> subRoot = subquery.from(Event.class);
        subquery.select(cb.max(root.get("time")))
                .where(cb.equal(root.get("company"), subRoot.get("company")));
        return cb.equal(root.get("time"), subquery);
    }

}

Когда вызывается EventRepository#findAll(EventFilter filter), результаты не фильтруются вообще. Пожалуйста, помогите мне правильно реализовать эту логику.

1 Ответ

0 голосов
/ 25 июня 2019

После проверки оператора SQL, сгенерированного Hibernate, я обнаружил ошибку: root использовалось вместо subRoot. Правильное тело метода:

Subquery<Long> sub = q.subquery(Long.class);     
Root<Event> subRoot = sub.from(Event.class);     
sub.select(cb.max(subRoot.get("time")))
   .where(cb.equal(root.get("company"), subRoot.get("company")));
return cb.equal(root.get("time"), sub);
...