Рубрики
Без рубрики

Как вернуть результат карты из запроса JPA или Hibernate

Узнайте, как можно вернуть результат Java-карты при выполнении запроса JPA с помощью либо getResultStream, либо Hibernate ResultTransformer.

Автор оригинала: Vlad Mihalcea.

Вступление

В этой статье я собираюсь показать вам, как вы можете вернуть результат Java-карты при выполнении запроса JPA. Я решил написать эту статью после ответа на аналогичный вопрос о StackOverflow .

Как вернуть результат #Java Map при выполнении запроса JPA. https://t.co/8zhtAr4jTN pic.twitter.com/G09xmtS9Xh

Модель предметной области

Предполагая, что мы используем следующую сущность Post , которая имеет атрибут CreatedOn типа LocalDate Java:

И мы сохранили следующую Запись сущность в нашей базе данных:

entityManager.persist(
    new Post()
        .setId(1L)
        .setTitle(
            "High-Performance Java Persistence eBook " +
            "has been released!"
        )
        .setCreatedOn(LocalDate.of(2016, 8, 30))
);

entityManager.persist(
    new Post()
        .setId(2L)
        .setTitle(
            "High-Performance Java Persistence paperback " +
            "has been released!"
        )
        .setCreatedOn(LocalDate.of(2016, 10, 12))
);

entityManager.persist(
    new Post()
        .setId(3L)
        .setTitle(
            "High-Performance Java Persistence Mach 1 video course " +
            "has been released!"
        )
        .setCreatedOn(LocalDate.of(2018, 1, 30))
);

entityManager.persist(
    new Post()
        .setId(4L)
        .setTitle(
            "High-Performance Java Persistence Mach 2 video course " +
            "has been released!"
        )
        .setCreatedOn(LocalDate.of(2018, 5, 8))
);

entityManager.persist(
    new Post()
        .setId(5L)
        .setTitle(
            "Hypersistence Optimizer " +
            "has been released!"
        )
        .setCreatedOn(LocalDate.of(2019, 3, 19))
);

Подсчет сообщений, опубликованных за год

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

select 
    YEAR(p.createdOn) as year, 
    count(p) as postCount 
from 
    Post p 
group by 
    YEAR(p.createdOn)

Традиционно метод getResultList JPA Запрос использовался всякий раз, когда результирующий набор содержал несколько строк. Однако мы не хотим возвращать Список при выполнении этого запроса. Поскольку у нас есть проекция двух столбцов, где первый из них уникален, нам лучше вместо этого вернуть Карту .

Возврат результата карты с помощью JPA-запроса getResultStream

Как я объяснил в этой статье , вы можете использовать JPA 2.2 getResultStream для преобразования Списка<Кортеж> результата в Карту<Целое число, Целое число> :

Map postCountByYearMap = entityManager
.createQuery(
    "select " +
    "   YEAR(p.createdOn) as year, " +
    "   count(p) as postCount " +
    "from " +
    "   Post p " +
    "group by " +
    "   YEAR(p.createdOn)", Tuple.class)
.getResultStream()
.collect(
    Collectors.toMap(
        tuple -> ((Number) tuple.get("year")).intValue(),
        tuple -> ((Number) tuple.get("postCount")).intValue()
    )
);

Метод Collectors.toMap возвращает Collector , который возвращает Хэш-карту с ключом, отображенным первой предоставленной лямбда – функцией, и значением, отображенным второй лямбда-функцией.

Возврат результата карты с помощью JPA-запроса getResultList

Если вы используете JPA 2.1 или более старые версии, но ваше приложение работает на Java 8 или более новой версии, вы можете использовать getResultList и преобразовать Список<Кортеж> в поток Java 8:

Map postCountByYearMap = entityManager
.createQuery(
    "select " +
    "   YEAR(p.createdOn) as year, " +
    "   count(p) as postCount " +
    "from " +
    "   Post p " +
    "group by " +
    "   YEAR(p.createdOn)", Tuple.class)
.getResultList()
.stream()
.collect(
    Collectors.toMap(
        tuple -> ((Number) tuple.get("year")).intValue(),
        tuple -> ((Number) tuple.get("postCount")).intValue()
    )
);

Возврат результата карты с помощью преобразователя результатов, зависящего от режима гибернации

Другой вариант-использовать класс Map ResultTransformer , предоставляемый проектом Hibernate Types с открытым исходным кодом:

Map postCountByYearMap = (Map) entityManager
.createQuery(
    "select " +
    "   YEAR(p.createdOn) as year, " +
    "   count(p) as postCount " +
    "from " +
    "   Post p " +
    "group by " +
    "   YEAR(p.createdOn)")
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(
    new MapResultTransformer()
)
.getSingleResult();

Преобразователь результатов карты подходит для проектов, все еще работающих на Java 6 или использующих более старые версии Hibernate.

Вывод

Как JPA, так и Hibernate обеспечивают большую гибкость, когда дело доходит до преобразования результирующего набора данного запроса JPG, будь то JPQL, API критериев или собственный SQL-запрос.

Если заданная ГРУППА JPA ПО запросу возвращает только два столбца, один из которых уникален, очень удобно возвращать результат в виде карты Java. Для этого вы можете использовать либо функцию потока Java, либо преобразователь результатов, зависящий от режима гибернации .