Руководство по обновлению до Spring Boot 3.0 для Spring Data JPA и Querydsl

В прошлом году я написал две статьи о JPA Criteria и Querydsl (см. статьи Введение и Метамодель). С конца прошлого года вышла новая крупная версия Spring Boot 3. Этот релиз основан на Spring Framework 6 с несколькими значительными изменениями и проблемами, которые следует учитывать при обновлении.

Цель этой статьи — выделить эти изменения при обновлении проекта sat-jpa (проект SAT). Используемые здесь технологии:

  1. Spring Boot 3.0.2,
  2. Hibernate 6.1.6.Final
  3. Spring Data JPA 3.0.1 и
  4. Querydsl 5.0.0.

Spring Framework 6.0.4

Spring Framework 6 имеет множество изменений (см. Что нового в Spring Framework 6.x), ключевые изменения:

  1. Переход на базовый уровень Java до Java 17 (по-прежнему последняя версия Java LTS на момент написания этой статьи) — то есть это минимальная версия Java, которую мы должны использовать.
  2. Переход на базовый уровень Jakarta до Jakarta EE 9+.

Помимо этого, в Spring MVC, используемом в проекте SAT, произошло лишь одно незначительное изменение.

Spring MVC 6.0.4

Все методы в классе ResponseEntityExceptionHandler изменили свои сигнатуры. Аргумент status был изменен с типа HttpStatus на тип HttpStatusCode (см. изменение в классе CityExceptionHandler).

HttpStatus в Spring MVC 5

Java

 

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception,
		HttpHeaders headers, HttpStatus status, WebRequest request) {
	return buildResponse(BAD_REQUEST, exception);
}

HttpStatusCode в Spring MVC 6

Java

 

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception, 
        HttpHeaders headers, HttpStatusCode status, WebRequest request) {
	return buildResponse(BAD_REQUEST, exception);
}

Spring Boot 3.0.2

Spring Boot 3 содержит много изменений (см. Заметки о выпуске Spring Boot 3.0); наиболее важными являются эти:

  • Java 17 (определяется Spring Framework).
  • Обновление до Spring Framework 6 (см. выше).
  • Увеличить до Hibernate 6 (см. ниже).
  • Миграция от JavaEE к зависимостям Jakarta EE — это не важно в нашем случае, так как мы не полагаемся на enterprise Java, но это может повлиять на многие зависимости (например, servlet API, валидация, устойчивость, JMS и т. д.).
  • Незначительные изменения в Spring MVC 6 в классе ResponseEntityExceptionHandler (см. следующую часть).

Начнем с простых изменений Jakarta EE. Таким образом, мы сможем сосредоточиться на устойчивости после этого.

Зависимости Jakarta EE

Значительным изменением является миграция от Java EE к зависимостям Jakarta EE. Spring Framework 6 установил базовый уровень как Jakarta EE 9 (см. Что нового в Spring Framework 6.x), но Spring Boot 3 уже использует Jakarta EE 10 (см. Заметки о выпуске Spring Boot 3.0) для многих API (например, Servlet или JPA — назовем некоторые технологии).

В результате, все классы, использующие другой класс из пакета jakarta, должны перейти к тому же классу из пакета javax (см., например, аннотации PostConstruct или Entity).

Импорты Javax

Java

 

import javax.annotation.PostConstruct;
import javax.persistence.Entity;

Импорты Jakarta

Java

 

import jakarta.annotation.PostConstruct;
import jakarta.persistence.Entity;

Hibernate 6.1.6

Еще одно значительное изменение в Spring Boot 3 – это обновление с Hibernate 5.6 до Hibernate 6.1. Подробности о Hibernate 6.1 можно найти здесь или в заметках о выпуске.

Честно говоря, я не обратил внимания на это изменение до тех пор, пока не пришлось исправить один не проходящий тест из-за разного размера результата (см. исправление в классе CountryRepositoryCustomTests). Реализация с новым Hibernate 6 возвращает меньше сущностей, чем раньше.

Стоит упомянуть два изменения здесь, основанные на этом расследовании. Начнем с логирования.

Ведение журнала

Логгеры Hibernate были изменены с org.hibernate.type на org.hibernate.orm.jdbc (см. эту ссылку и эту ссылку).

Примечание: все доступные параметры конфигурации можно найти в классе org.hibernate.cfg.AvailableSettings.

Hibernate 5

Был один логгер для скрытых и извлеченных значений.

Properties files

 

logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

Hibernate 6

Hibernate 6 изменил основную пакет и разделил их на два разных логгера для различения операции – какое значение следует логировать.

Properties files

 

logging.level.org.hibernate.orm.jdbc.bind=trace
logging.level.org.hibernate.orm.jdbc.extract=trace

Семантическая модель запроса

После устранения проблем с ведением журнала было подтверждено, что Hibernate действительно извлекает два записи из БД:

Plain Text

 

2023-01-25T08:40:18.819+01:00  INFO 6192 --- [           main] c.g.a.s.j.c.CountryRepositoryCustomTests : Started CountryRepositoryCustomTests in 4.678 seconds (process running for 5.745)
Hibernate: select c2_0.id,c2_0.name from city c1_0 join country c2_0 on c2_0.id=c1_0.country_id where c1_0.name like ? escape '!' and c1_0.state like ? escape '!'
2023-01-25T08:40:19.221+01:00 TRACE 6192 --- [           main] org.hibernate.orm.jdbc.extract           : extracted value ([1] : [BIGINT]) - [3]
2023-01-25T08:40:19.222+01:00 TRACE 6192 --- [           main] org.hibernate.orm.jdbc.extract           : extracted value ([2] : [VARCHAR]) - [USA]
2023-01-25T08:40:19.240+01:00 TRACE 6192 --- [           main] org.hibernate.orm.jdbc.extract           : extracted value ([1] : [BIGINT]) - [3]
2023-01-25T08:40:19.241+01:00 TRACE 6192 --- [           main] org.hibernate.orm.jdbc.extract           : extracted value ([2] : [VARCHAR]) - [USA]

Однако тест получает только одну сущность от Hibernate, как видно на этих скриншотах отладки:

Изменение поведения вызвано новой Семантической моделью запроса с автоматическим удалением дубликатов (см. Семантическую модель запроса часть), введенной с Hibernate 6 (см. строку 178 в классе org.hibernate.sql.results.spi.ListResultsConsumer). Теперь Hibernate 6 возвращает результат с удаленными дубликатами.

Генератор JPA Hibernate

Зависимость Hibernate Jpamodelgen Maven, управляемая Spring Boot Dependencies, была перенесена из пакета org.hibernate в пакет org.hibernate.orm. См.:

Spring Boot 2.7.5

XML

 

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-jpamodelgen</artifactId>
</dependency>

Spring Boot 3.0.2

XML

 

<dependency>
	<groupId>org.hibernate.orm</groupId>
	<artifactId>hibernate-jpamodelgen</artifactId>
</dependency>

Liquibase

Для завершения основных зависимостей устойчивости, Liquibase был обновлен с версии 4.9.1 до 4.17.2. Значительных различий нет, за исключением использования зависимости JAX-B. Зависимость должна использовать JAX-B от Jakarta вместо Javax (см. следующую ссылку).

Spring Boot 2.7.5

XML

 

<dependency>
	<groupId>org.liquibase</groupId>
	<artifactId>liquibase-core</artifactId>
</dependency>

Spring Boot 3.0.2

XML

 

<dependency>
	<groupId>org.liquibase</groupId>
	<artifactId>liquibase-core</artifactId>
	<exclusions>
		<exclusion> <!-- due to SB 3.0 switch to Jakarta -->
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>jakarta.xml.bind</groupId>
	<artifactId>jakarta.xml.bind-api</artifactId>
</dependency>

Spring Data JPA 3.0.1

Spring Boot 3.0.2 зависит от Spring Data Release Train 2022.0.1 (см. Заметки о выпуске Spring Data 2022.0 – Turing), где используется Spring Data JPA 3.0.1 с этими ключевыми изменениями (см. заметки о выпуске):

  1. Переключение базовой линии Java на Java 17,
  2. Переключение базовой линии Jakarta на Jakarta EE 10 и
  3. Поднятие до Hibernate 6

Примечание: ранее используемая версия (в нашем случае) была 2.7.5.

Querydsl 5.0.0

Версия Querydsl не изменилась, но она подверглась влиянию аналогичному тому, что было у Liquibase. Зависимости теперь должны использоваться от Jakarta вместо Javax. Следовательно, зависимость Querydsl должна использовать классификатор jakarta вместо старого классификатора jpa (см. это ссылка).

Spring Boot 2.7.5

XML

 

<dependency>
	<groupId>com.querydsl</groupId>
	<artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
	<groupId>com.querydsl</groupId>
	<artifactId>querydsl-apt</artifactId>
	<version>${querydsl.version}</version>
	<classifier>jpa</classifier>
	<scope>provided</scope>
</dependency>

Spring Boot 3.0.2

XML

 

<dependency>
	<groupId>com.querydsl</groupId>
	<artifactId>querydsl-jpa</artifactId>
	<version>${querydsl.version}</version>
	<classifier>jakarta</classifier>
</dependency>
<dependency>
	<groupId>com.querydsl</groupId>
	<artifactId>querydsl-apt</artifactId>
	<scope>provided</scope>
	<version>${querydsl.version}</version>
	<classifier>jakarta</classifier>
</dependency>

Заключение

В данной статье рассматривается обновление до последней версии Spring Boot 3.0.2 для JPA и Querydsl (на момент написания статьи). Статья началась с изменений в Spring Framework и Spring Boot. Далее были рассмотрены все изменения в Hibernate и связанных технологиях. В конце упомянуты незначительные изменения, связанные с Spring Data 3.0.1 и Querydsl 5.0.0.

Полный исходный код, продемонстрированный выше, доступен в моем GitHub репозитории.

Примечание: также существует предыдущая статья, Руководство по обновлению до Spring Data Elasticsearch 5.0, посвященная аналогичному обновлению Elasticsearch с использованием Spring Boot 3.

Source:
https://dzone.com/articles/upgrade-guide-to-spring-boot-3-for-spring-data-jpa-3-and-querydsl-5