Агрегация JPA по агрегированному результату подзапроса - PullRequest
0 голосов
/ 11 октября 2019

У меня есть следующая сущность JPA (методы получения, установки и несоответствующие поля опущены):

@Entity
@Table(name = "transaction")
public class Transaction {

@Id
@GeneratedValue
@Column(name = "id")
private Long id;

@Column(name = "start_date", nullable = false)
private Date startDate;

}

Моя цель - реализовать запросы с использованием JPQL или критерия API, который будет возвращать среднее количество транзакций в день имаксимальное количество транзакций в день.

Собственные запросы SQL (база данных MySQL), дающие желаемый результат, выглядят так:

select max(cnt) from (
select date(start_date) start_date, count(t.id) cnt 
from transaction t 
group by date(t.start_date)
) t;

select avg(cnt) from (
select date(start_date) start_date, count(t.id) cnt 
from transaction t 
group by date(t.start_date)
) t;

К сожалению, использование собственных запросов SQL не рекомендуется, и JPQL не позволяетиспользование подзапросов в предложении where.

Заранее спасибо.

Добавление:

Я начал со следующего запроса данных Spring:

@Query("select max(cnt) from ("
+ "select date(t.startDate) startDate, count(t.id) cnt "
+ "from Transaction t "
+ "group by date(t.startDate))")

Но этоочевидно, не сработало:

 org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: ( near line 1, column 22 [select max(cnt) from (select date(t.startDate) startDate, count(t.id) cnt from Transaction t group by date(startDate))]

Я могу представить, что с помощью сортировки и ограничения выходных данных можно управлять поиском max, но это не поможет для avg.

1 Ответ

0 голосов
/ 13 октября 2019

Есть две причины, почему невозможно написать то, что вы хотите, в JPQL (или Критерии):

  • Подзапрос в FROM, как вы указываете
  • Функция даты - MySQL /Специфичный для Hibernate.

Для этого запроса вы можете запросить только переносимость (оставьте родной SQL как можно более стандартным).

Один из вариантов - использовать FluentJPA , которыйисключает встроенные строки и тесно интегрируется с Java и JPA. Поэтому запрос будет выглядеть так:

public int getAvgCount() {

    FluentQuery query = FluentJPA.SQL(() -> {

        DailyCount daily = subQuery((Transaction t) -> {

            Date date = alias(DATE(t.getStartDate()), DailyCount::getDate);
            int count = alias(COUNT(t.getId()), DailyCount::getCount);

            SELECT(date, count);
            FROM(t);
            GROUP(BY(date));
        });

        SELECT(AVG(daily.getCount())); // or MAX
        FROM(daily);
    });

    return query.createQuery(em, Integer.class).getSingleResult();
}

Объявление типа:

@Tuple
@Getter // lombok
public class DailyCount {
    private Integer count;
    private Date date;
}

Результирующий SQL:

SELECT AVG(q0.count)
FROM (SELECT DATE(t0.start_date) AS date, COUNT(t0.id) AS count
FROM transaction t0
GROUP BY  DATE(t0.start_date)) q0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...