JPAstreamer – сущности JPA как поток Java в Spring Boot

JPAstreamer — это библиотека для выражения запросов JPA/Hibernate/Spring с использованием стандартных потоков Java. Она помогает извлекать и обрабатывать записи базы данных таким же образом, как мы обрабатываем POJO.

Например, мы можем извлечь всех неактивных пользователей из базы данных, используя следующий код:

List<User> inactiveUsers = jpaStreamer.stream(User.class).filter(u -> !u.getActive()).collect(Collectors.toList());

Внутри себя JPAstreamer сгенерирует оптимизированный SQL-запрос, который будет запущен в базе данных с использованием источника данных/менеджера сущностей, настроенного в приложении.

Для демонстрации мы будем использовать следующий класс сущности User:

@Data@AllArgsConstructor@NoArgsConstructor@Entitypublic class User {@Id@GeneratedValue(strategy = GenerationType.SEQUENCE)private Long id;private String name;private LocalDate dateOfBirth;private Boolean active;}

1. Начало работы с JPAstreamer

В типичном приложении Spring boot мы можем импортировать и настроить JPAstreamer, импортировав зависимости jpastreamer-core и spring-boot-jpastreamer-autoconfigure.

Зависимость jpastreamer-core транзитивно импортирует все необходимые зависимости и подмодули. Spring-boot-jpastreamer-autoconfigure помогает в автоматической настройке JPAstreamer в приложениях Spring boot, используя его автоконфигурацию, когда у нас уже есть spring-boot-starter-data-jpa в classpath.

<dependency><groupId>com.speedment.jpastreamer</groupId><artifactId>jpastreamer-core</artifactId><version>1.1.4</version></dependency><dependency><groupId>com.speedment.jpastreamer.integration.spring</groupId><artifactId>spring-boot-jpastreamer-autoconfigure</artifactId><version>1.1.4</version></dependency>

Обратите внимание, что в настоящее время JPAstreamer работает с Spring boot 2. Из-за несовместимости с пространством имен 'jakarta.*' он не будет работать с Spring boot 3. Вы можете проверить трекер ошибок на предмет последних обновлений.

2. Инициализация JPAStreamer

В приложении Spring boot автоконфигурация автоматически инициализирует класс JPAStreamer, который предоставляет полностью типобезопасный API для написания запросов JPA. Мы можем автоматически связать его в классе службы или тестовом классе следующим образом:

@Servicepublic class UserService {@AutowiredJPAStreamer jpaStreamer;...}

Мы также можем инициализировать JPAstreamer с именем единицы сохранения. В следующем коде «persistence-unit-name» следует заменить на имя вашей единицы сохранения.

JPAStreamer jpaStreamer = JPAStreamer.of("persistence-unit-name");

За кулисами JPAStreamer автоматически генерирует метамодель процессором аннотаций, который работает во время компиляции, как только JPAstreamer установлен. Процессор аннотаций проверяет все классы, аннотированные @Entity. Например, 'User.class' генерирует эквивалент 'User$.class' в папке '/target' проекта.

Затем во время выполнения используется потоковый рендерер, который преобразует потоковые операции в запросы JPA для повышения производительности.

3. Получение потока из таблицы

Для потоковой передачи сущностей из таблицы мы должны использовать stream(entity-class). Получив поток записей, мы можем обрабатывать записи аналогично стандартным потокам Java.

jpaStreamer.stream(User.class)//other stream operations...

Не забудьте закрыть поток, чтобы освободить все ресурсы, потенциально удерживаемые JPAstreamer.

jpaStreamer.close();

4. Работа с потоками сущностей

Как только у нас есть поток сущностей, мы можем выполнять все виды промежуточных и конечных операций, как мы делаем в стандартных потоках Java. Прочитайте официальную страницу для полного списка поддерживаемых функций.

4.1 Фильтрация с помощью предикатов

В сгенерированном классе метаданных каждое поле создается как ComparableField, и мы можем применять встроенные компараторы для фильтрации элементов потока.

Например, чтобы найти всех пользователей в базе данных, у которых поле dateOfBirth имеет значение null, мы можем использовать следующий оператор:

List<User> usersWithoutDateOfBirth = jpaStreamer.stream(User.class).filter(User$.dateOfBirth.isNull()).collect(Collectors.toList());

В следующей таблице перечислены несколько предикатов для нашего понимания. Вы можете прочитать обо всех поддерживаемых предикатах в официальном руководстве. Обратите внимание, что мы можем создать составной предикат, объединив несколько предикатов с помощью методов and() и or().

Предикат Описание
isNull, isNotNull поле равно null или не равно null.
равно, неравный поле равно/не равно параметру.
меньшеИлиРавно, большеИлиРавно, меньшеЧем, большеЧем поле меньше / больше / равно параметру.
между, неМежду поле находится между / не между началом(включительно) и концом(исключая).
в, неВ параметр массива содержит/не содержит поле.
isEmpty, isNotEmpty строка пустая / не пустая.
начинается с, не начинается с, начинается сIgnoreCase, не начинается сIgnoreCase строка начинается с / не начинается с указанного параметра с учетом / без учета регистра.
endsWith, notEndsWith, endsWithIgnoreCase, notEndsWithIgnoreCase строка заканчивается на / не заканчивается на указанный параметр с учетом / без учета регистра.
содержит, не содержит, содержитIgnoreCase, не содержитIgnoreCase строка содержит/не содержит указанный параметр с учетом/без учета регистра.
отрицать отрицает заданный предикат.
и, или возвращает составной предикат, представляющий собой замкнутую логическую операцию И/ИЛИ двух заданных предикатов.

Обратите внимание, что список поддерживаемых предикатов зависит от типа данных поля. Например, isNull() и isNotNull() применимы только для полей непримитивного типа.

4.2 Сортировка

Мы можем использовать sorted() интерфейса Stream и передать необходимый компаратор для сортировки сущностей. Для обратной сортировки используйте компаратор reverse().

jpaStreamer.stream(User.class).sorted(User$.dateOfBirth.reversed())...

4.3.Пагинация

Библиотека также поддерживает функцию пагинации, похожую на простые запросы критериев JPA. Номера страниц начинаются с 0. В следующем операторе мы извлекаем страницу номер 2, тогда как размер страницы равен 10.

int page = 2;int PAGE_SIZE = 10;List<User> users = jpaStreamer.stream(User.class)skip(page * PAGE_SIZE).limit(PAGE_SIZE).toList();

5. Транзакции

Работая с сущностями в потоке, мы также можем обновлять сущности, используя ссылку EntityManager. Необходимо обеспечить, чтобы эти обновления происходили внутри границ транзакции. Мы можем начинать и заканчивать транзакции, используя простые методы JPA следующим образом:

@AutowiredEntityManagerFactory emf;public void updateUsers() {EntityManager em = emf.createEntityManager();JPAStreamer jpaStreamer = JPAStreamer.of(emf);try {em.getTransaction().begin();jpaStreamer.stream(User.class).filter(User$.active.equal(false)).forEach(u -> {u.setActive(true));em.merge(u);}em.getTransaction().commit();} catch(Exception e) {em.getTransaction().rollback();}}

6. Заключение

В этом руководстве по JPAstreamer мы изучили основы библиотеки. Мы научились импортировать и настраивать JPAstreamer в приложении Spring boot, которое уже имеет зависимость spring-boot-starter-data-jpa. Мы также научились создавать и автоматически подключать экземпляр JPAstreamer и передавать потоковые данные из таблицы с помощью различных потоковых операций.

Исходный код на Github

Прокрутить вверх