Получение результата запроса в виде Map в Hibernate

Иногда удобно извлечь из базы данные в вид Map. Например, если запрос содержит group by. Ниже рассмотрим, как это сделать.

Модель

Допустим,  в таблице хранятся доходы — сумма дохода и дата дохода. Доходы относятся к разным годам, а мы хотим посчитать общую сумму дохода за каждый год.

Сущность Income, в которой хранятся доходы:

@Data
@NoArgsConstructor
@Entity
public class Income {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private long id;
    private LocalDate localDate;
    private int sum;
}

JPQL-запрос с group by

JPQL-запрос для получения общей суммы дохода по каждому году будет таким:

select year(i.localDate) as year, sum(i.sum) as sum from Income i group by year

Обычно Query возвращает List:

List<Tuple> list=em.createQuery("select year(i.localDate) as year, sum(i.sum) as sum from Income i group by year").getResultList();

Но нам надо получить Map. Есть несколько способов.

С помощью getResultStream()

Map<Integer, Integer> map=em.createQuery("select year(i.localDate) as year, sum(i.sum) as sum from Income i group by year", Tuple.class)
        .getResultStream()
        .collect(
                Collectors.toMap(
                        tuple -> ((Number) tuple.get("year")).intValue(),
                        tuple -> ((Number) tuple.get("sum")).intValue()
                )
        );

getResultList() в Stream

Либо ResultList преобразуем в Stream:

Map<Integer, Integer> map=em.createQuery("select year(i.localDate) as year, sum(i.sum) as sum from Income i group by year", Tuple.class)
        .getResultList()
        .stream()
        .collect(
                Collectors.toMap(
                        tuple -> ((Number) tuple.get("year")).intValue(),
                        tuple -> ((Number) tuple.get("sum")).intValue()
                )
        );

С помощью ResultTransformer из библиотеки

Есть также библиотека hibernate-types, в которой реализован специальный MapResultTransformer. Его тоже можно использовать:

Map<Integer, Integer> map= (Map<Integer, Integer>) em.createQuery("select year(i.localDate) as year, sum(i.sum) as sum from Income i group by year")
        .unwrap(org.hibernate.query.Query.class)
        .setResultTransformer(
                new MapResultTransformer<>()
        )
        .getSingleResult();
Другой пример реализации ResultTransformer рассмотрен в статье о получении проекций.

Исходный код

Исходный код примера есть на GitHub.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *