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已經為許多API(例如,Servlet或JPA——僅舉幾例)使用了Jakarta EE 10(參見Spring Boot 3.0發行說明)。

因此,所有使用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