Spring Boot 3.0升级指南:针对Spring Data JPA与Querydsl的优化

去年,我撰写了关于JPA Criteria和Querydsl的两篇文章(参见引言元模型文章)。自去年底以来,Spring Boot迎来了3.0版本的重要更新。此版本依托于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以上。

此外,SAT项目中使用的Spring MVC仅有小幅改动。

Spring MVC 6.0.4。

ResponseEntityExceptionHandler 类中的所有方法都变更了签名。status 参数由 HttpStatus 类型改为 HttpStatusCode 类型(参见 CityExceptionHandler 类的改动)。

Spring MVC 5 中的 HttpStatus

Java

 

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

Spring MVC 6 中的 HttpStatusCode

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 Release Notes),其中最关键的几项包括:

  • 采用 Java 17(由 Spring Framework 规定)。
  • 升级至 Spring Framework 6(如上所述)。
  • 升级至Hibernate 6(见下文)。
  • 从JavaEE迁移到Jakarta EE依赖——这对我们来说并不重要,因为我们在此并不依赖企业级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包中相同的类(例如,参见PostConstructEntity注解)。

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

在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引入的语义查询模型与自动去重(参见语义查询模型部分)引起的(参见org.hibernate.sql.results.spi.ListResultsConsumer类中的第178行)。现在,Hibernate 6返回的是去重后的结果。

Hibernate JPA生成器

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依赖的使用差异外,无显著变化。该依赖应采用Jakarta的JAX-B而非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升级指南,专注于Spring Boot 3下的Elasticsearch类似升级。

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