Guida all’aggiornamento a Spring Boot 3.0 per Spring Data JPA e Querydsl

L’anno scorso, ho scritto due articoli su JPA Criteria e Querydsl (vedi gli articoli Introduzione e Metamodel). Dalla fine dell’anno scorso, è stato rilasciato un nuovo importante aggiornamento di Spring Boot 3. Questo rilascio si basa su Spring Framework 6 con diverse modifiche significative e problemi che dobbiamo prendere in considerazione durante l’aggiornamento.

Lo scopo di questo articolo è evidenziare queste modifiche durante l’aggiornamento del progetto sat-jpa (progetto SAT). Le tecnologie utilizzate qui sono:

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

Spring Framework 6.0.4

Spring Framework 6 presenta molte modifiche (vedi Novità in Spring Framework 6.x), le principali modifiche sono:

  1. Passaggio della base Java a Java 17 (ancora l’ultima versione LTS di Java al momento della stesura di questo articolo) — vale a dire, è la versione minima di Java che dobbiamo utilizzare.
  2. Passaggio della base Jakarta a Jakarta EE 9+.

Oltre a ciò, c’è solo una piccola modifica in Spring MVC utilizzato nel progetto SAT.

Spring MVC 6.0.4

Tutti i metodi nella classe ResponseEntityExceptionHandler hanno modificato le loro firme. L’argomento status è stato modificato dal tipo HttpStatus al tipo HttpStatusCode (vedi la modifica nella classe CityExceptionHandler).

HttpStatus in Spring MVC 5

Java

 

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

HttpStatusCode in 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 presenta molte modifiche (vedi Note sulla versione di Spring Boot 3.0); le più importanti sono queste:

  • Java 17 (definito dal Framework Spring).
  • Aggiornamento al Framework Spring 6 (vedi sopra).
  • Aumentiamo a Hibernate 6 (vedi sotto).
  • Migrazione dalle dipendenze JavaEE alle dipendenze Jakarta EE — non è importante nel nostro caso poiché non dipendiamo da Java enterprise, ma può influenzare molte dipendenze (ad esempio, API servlet, validazione, persistenza, JMS, ecc.).
  • Piccola modifica in Spring MVC 6 nella classe ResponseEntityExceptionHandler (vedi la parte successiva).

Iniziamo con le semplici modifiche di Jakarta EE. Così possiamo concentrarsi sulla persistenza dopo.

Dipendenze Jakarta EE

La modifica significativa è la migrazione dalle dipendenze Java EE alle dipendenze Jakarta EE. Spring Framework 6 ha impostato la linea di base come Jakarta EE 9 (vedi Cosa c’è di nuovo in Spring Framework 6.x), ma Spring Boot 3 utilizza già Jakarta EE 10 (vedi Note sulla versione di Spring Boot 3.0) per molte API (ad esempio, Servlet o JPA — per citare alcune tecnologie).

Di conseguenza, tutte le classi che utilizzano una classe dal pacchetto jakarta devono invece passare alla stessa classe dal pacchetto javax (vedi, ad esempio, annotazioni come PostConstruct o Entity).

Importazioni Javax

Java

 

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

Importazioni Jakarta

Java

 

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

Hibernate 6.1.6

Un’altra importante modifica in Spring Boot 3 è l’aggiornamento da Hibernate 5.6 a Hibernate 6.1. I dettagli su Hibernate 6.1 possono essere trovati qui o all’interno delle note di rilascio.

Onestamente, non ho prestato attenzione a questa modifica fino a quando non ho dovuto risolvere un test fallito a causa di una dimensione del risultato diversa (vedi il fix nella classe CountryRepositoryCustomTests). L’implementazione con il nuovo Hibernate 6 restituisce meno entità rispetto a prima.

Vale la pena menzionare due modifiche qui sulla base di questa indagine. Iniziamo prima con la registrazione.

Logging

I logger di Hibernate sono stati modificati da org.hibernate.type a org.hibernate.orm.jdbc (vedi questo riferimento e questo riferimento).

Nota: tutti gli elementi di configurazione disponibili possono essere trovati nella classe org.hibernate.cfg.AvailableSettings.

Hibernate 5

C’era un unico logger per i valori bidonati e estratti.

Properties files

 

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

Hibernate 6

Hibernate 6 ha cambiato il pacchetto principale e li ha divisi in due logger diversi per distinguere l’operazione -> quale valore dovrebbe essere registrato.

Properties files

 

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

Modello di Query Semantico

Con la correzione del logging, è stato confermato che Hibernate estrae davvero due record dal DB:

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]

Tuttavia, il test riceve solo un’entità da Hibernate, come puoi vedere in queste schermate di debug:

Il comportamento cambiato è causato dal nuovo Modello di Query Semantico con deduplicazione automatica (vedi parte Modello di Query Semantico) introdotto con Hibernate 6 (vedi linea 178 nella classe org.hibernate.sql.results.spi.ListResultsConsumer). Hibernate 6 ora restituisce il risultato deduplicato.

Generatore JPA di Hibernate

La dipendenza Maven Hibernate Jpamodelgen gestita da Spring Boot Dependencies è stata spostata dal pacchetto org.hibernate al pacchetto org.hibernate.orm. Vedere:

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

Per completare le dipendenze di persistenza di base, Liquibase è stato aggiornato dalla versione 4.9.1 alla 4.17.2. Non ci sono differenze significative tranne l’uso della dipendenza JAX-B. La dipendenza dovrebbe utilizzare JAX-B da Jakarta invece di Javax (vedi il seguente riferimento).

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 dipende da Spring Data Release Train 2022.0.1 (vedi Note sulla versione di Spring Data 2022.0 – Turing Release Notes), dove Spring Data JPA 3.0.1 viene utilizzato con questi principali cambiamenti (vedi le note sulla versione):

  1. Passaggio della baseline di Java a Java 17,
  2. Passaggio della baseline di Jakarta a Jakarta EE 10 e
  3. Aumentare a Hibernate 6

Nota: la versione precedentemente utilizzata (nel nostro caso) era la 2.7.5.

Querydsl 5.0.0

La versione di Querydsl non è stata modificata, ma è stata influenzata in modo simile a Liquibase. Le dipendenze devono essere utilizzate da Jakarta invece di Javax. Pertanto, la dipendenza di Querydsl deve utilizzare il classificatore jakarta invece dell’ormai obsoleto classificatore jpa (vedi questa riferimento).

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>

Conclusione

Questo articolo ha trattato l’aggiornamento alla più recente versione di Spring Boot 3.0.2 per JPA e Querydsl (al momento della stesura di questo articolo). L’articolo ha iniziato con le modifiche del Spring Framework e di Spring Boot. Successivamente, sono state coperte tutte le modifiche a Hibernate e alle tecnologie correlate. Alla fine, abbiamo menzionato le piccole modifiche relative a Spring Data 3.0.1 e Querydsl 5.0.0.

Il codice sorgente completo dimostrato sopra è disponibile nel mio GitHub repository.

Nota: esiste anche un articolo precedente, Guida all’aggiornamento a Spring Data Elasticsearch 5.0, dedicato a un aggiornamento simile di Elasticsearch con Spring Boot 3.

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