Guide d’upgrade pour passer à Spring Boot 3.2 pour les projets Spring Data JPA et Querydsl

L’année dernière, j’ai écrit l’article, « Guide de mise à niveau vers Spring Boot 3.0 pour Spring Data JPA et Querydsl, » pour la mise à niveau de Spring Boot 3.0.x. Maintenant, nous avons Spring Boot 3.2. Voyons deux problèmes que vous pourriez rencontrer lors de la mise à niveau vers Spring Boot 3.2.2.

Les technologies utilisées dans le projet SAT sont :

  1. Spring Boot 3.2.2 et Spring Framework 6.1.3
  2. Hibernate + générateur de modèle JPA 6.4.1. Final 
  3. Spring Data JPA 3.2.2
  4. Querydsl 5.0.0.

Modifications

Toutes les modifications dans Spring Boot 3.2 sont décrites dans Notes de version de Spring Boot 3.2 et Nouveautés de la version 6.1 pour Spring Framework 6.1.

Les dernières modifications de Spring Boot 3.2.2 peuvent être trouvées sur GitHub.

Problèmes détectés

  • A different treatment of Hibernate dependencies due to the changed hibernate-jpamodelgen behavior for annotation processors
  • Unpaged la classe a été repensée.

Commençons par les dépendances Hibernate en premier lieu.

Intégration de la Génération du Métamodèle Statique

Le plus grand changement provient de la dépendance hibernate-jpamodelgen, qui génère un métamodèle statique metamodel. Dans Hibernate 6.3, le traitement des dépendances a été modifié afin de réduire les dépendances transitives. Spring Boot 3.2.0 a mis à niveau la dépendance hibernate-jpamodelgen à la version 6.3 (voir Mises à niveau des dépendances). Malheureusement, la nouvelle version provoque des erreurs de compilation (voir ci-dessous).

Note: Spring Boot 3.2.2 utilisé ici utilise déjà Hibernate 6.4 avec le même comportement.

Erreur de Compilation

Avec ce changement, la compilation de notre projet (construction Maven) avec Spring Boot 3.2.2 échoue sur une erreur comme celle-ci:

Plain Text

 

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  3.049 s
[INFO] Finished at: 2024-01-05T08:43:10+01:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project sat-jpa: Compilation failure: Compilation failure: 
[ERROR]   on the class path. A future release of javac may disable annotation processing
[ERROR]   unless at least one processor is specified by name (-processor), or a search
[ERROR]   path is specified (--processor-path, --processor-module-path), or annotation
[ERROR]   processing is enabled explicitly (-proc:only, -proc:full).
[ERROR]   Use -Xlint:-options to suppress this message.
[ERROR]   Use -proc:none to disable annotation processing.
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\city\CityRepository.java:[3,41] error: cannot find symbol
[ERROR]   symbol:   class City_
[ERROR]   location: package com.github.aha.sat.jpa.city
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\city\CityRepository.java:[3] error: static import only from classes and interfaces
...
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\country\CountryCustomRepositoryImpl.java:[4] error: static import only from classes and interfaces
[ERROR] java.lang.NoClassDefFoundError: net/bytebuddy/matcher/ElementMatcher
[ERROR] 	at org.hibernate.jpamodelgen.validation.ProcessorSessionFactory.<clinit>(ProcessorSessionFactory.java:69)
[ERROR] 	at org.hibernate.jpamodelgen.annotation.AnnotationMeta.handleNamedQuery(AnnotationMeta.java:104)
[ERROR] 	at org.hibernate.jpamodelgen.annotation.AnnotationMeta.handleNamedQueryRepeatableAnnotation(AnnotationMeta.java:78)
[ERROR] 	at org.hibernate.jpamodelgen.annotation.AnnotationMeta.checkNamedQueries(AnnotationMeta.java:57)
[ERROR] 	at org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity.init(AnnotationMetaEntity.java:297)
[ERROR] 	at org.hibernate.jpamodelgen.annotation.AnnotationMetaEntity.create(AnnotationMetaEntity.java:135)
[ERROR] 	at org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.handleRootElementAnnotationMirrors(JPAMetaModelEntityProcessor.java:360)
[ERROR] 	at org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.processClasses(JPAMetaModelEntityProcessor.java:203)
[ERROR] 	at org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor.process(JPAMetaModelEntityProcessor.java:174)
[ERROR] 	at jdk.compiler/com.sun.tools.javac.processing.JavacProcessingEnvironment.callProcessor(JavacProcessingEnvironment.java:1021)
[ER...
[ERROR] 	at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:348)
[ERROR] Caused by: java.lang.ClassNotFoundException: net.bytebuddy.matcher.ElementMatcher
[ERROR] 	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
[ERROR] 	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:593)
[ERROR] 	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526)
[ERROR] 	... 51 more
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException

Cela est dû à l’approche modifiée dans la génération du métamodèle statique annoncée dans le guide de migration Hibernate (voir Intégration de la Génération du Métamodèle Statique et le problème original HHH-17362). Leur explication pour un tel changement est la suivante:

« … dans les versions précédentes d’Hibernate ORM, vous laissiez inconsciemment fuir des dépendances de hibernate-jpamodelgen dans votre classpath de compilation. Avec Hibernate ORM 6.3, vous pouvez désormais rencontrer une erreur de compilation lors du traitement des annotations liée à des classes Antlr manquantes. »

Changements de dépendances

Comme on peut le voir ci-dessous sur les captures d’écran, les dépendances Hibernate ont vraiment changé.

  • Spring Boot 3.1.6:

  • Spring Boot 3.2.2:

Explication

Comme indiqué dans le guide de migration, nous devons modifier notre pom.xml d’une simple dépendance Maven aux chemins des processeurs d’annotations du plugin Maven compiler (voir documentation).

Solution

Nous pouvons supprimer les dépendances Maven hibernate-jpamodelgen et querydsl-apt (dans notre cas) comme recommandé dans l’article précédent. À la place, pom.xml doit définir les générateurs de modèles métamodels statiques via maven-compiler-plugin comme ceci:

XML

 

<plugins>
	<plugin>
		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-compiler-plugin</artifactId>
		<configuration>
			<annotationProcessorPaths>
				<path>
					<groupId>org.hibernate.orm</groupId>
					<artifactId>hibernate-jpamodelgen</artifactId>
					<version>${hibernate.version}</version>
				</path>
				<path>
					<groupId>com.querydsl</groupId>
					<artifactId>querydsl-apt</artifactId>
					<version>${querydsl.version}</version>
					<classifier>jakarta</classifier>
				</path>
				<path>
					<groupId>org.projectlombok</groupId>
					<artifactId>lombok</artifactId>
					<version>${lombok.version}</version>
				</path>
			</annotationProcessorPaths>
		</configuration>
	</plugin>
</plugins>

Voir les changements liés dans le projet SAT sur GitHub.

Comme nous sommes obligés d’utiliser cette approche en raison de hibernate-jpamodelgen, nous devons l’appliquer à toutes les dépendances liées au traitement des annotations (querydsl-apt ou lombok). Par exemple, lorsque lombok n’est pas utilisé de cette manière, nous obtenons une erreur de compilation comme celle-ci:

Plain Text

 

[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR : 
[INFO] -------------------------------------------------------------
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\city\CityService.java:[15,30] error: variable repository not initialized in the default constructor
[INFO] 1 error
[INFO] -------------------------------------------------------------
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.535 s
[INFO] Finished at: 2024-01-08T08:40:29+01:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project sat-jpa: Compilation failure
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\city\CityService.java:[15,30] error: variable repository not initialized in the default constructor

La même chose s’applique à querydsl-apt. Dans ce cas, nous pouvons voir l’erreur de compilation comme celle-ci:

Plain Text

 

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.211 s
[INFO] Finished at: 2024-01-11T08:39:18+01:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project sat-jpa: Compilation failure: Compilation failure: 
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\country\CountryRepository.java:[3,44] error: cannot find symbol
[ERROR]   symbol:   class QCountry
[ERROR]   location: package com.github.aha.sat.jpa.country
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\country\CountryRepository.java:[3] error: static import only from classes and interfaces
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\country\CountryCustomRepositoryImpl.java:[3,41] error: cannot find symbol
[ERROR]   symbol:   class QCity
[ERROR]   location: package com.github.aha.sat.jpa.city
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\country\CountryCustomRepositoryImpl.java:[3] error: static import only from classes and interfaces
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\country\CountryCustomRepositoryImpl.java:[4,44] error: cannot find symbol
[ERROR]   symbol:   class QCountry
[ERROR]   location: package com.github.aha.sat.jpa.country
[ERROR] <SAT_PATH>\sat-jpa\src\main\java\com\github\aha\sat\jpa\country\CountryCustomRepositoryImpl.java:[4] error: static import only from classes and interfaces
[ERROR] -> [Help 1]

La raison est évidente. Nous devons appliquer tous les processeurs d’annotations en même temps. Sinon, certaines parties du code peuvent être manquantes, et nous obtenons une erreur de compilation.

Redésigné Non Paginé

Le deuxième problème mineur est lié à un changement dans la classe Unpaged. Une sérialisation de PageImpl par la bibliothèque Jackson a été impactée par le changement de Unpaged d’un enum à une class (voir spring-projects/spring-data-commons#2987).

  • Spring Boot 3.1.6:
Java

 

public interface Pageable {

	static Pageable unpaged() {
		return Unpaged.INSTANCE;
	}

	...
}

enum Unpaged implements Pageable {

	INSTANCE;

	...
}

  • Spring Boot 3.2.2:
Java

 

public interface Pageable {

	static Pageable unpaged() {
		return unpaged(Sort.unsorted());
	}

	static Pageable unpaged(Sort sort) {
		return Unpaged.sorted(sort);
	}

	...
}
	
final class Unpaged implements Pageable {

	private static final Pageable UNSORTED = new Unpaged(Sort.unsorted());

	...

}

Lorsque new PageImpl<City>(cities) est utilisé (comme nous avions l’habitude de l’utiliser), alors cette erreur est lancée:

Plain Text

 

2024-01-11T08:47:56.446+01:00  WARN 5168 --- [sat-elk] [           main] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: (was java.lang.UnsupportedOperationException)]

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /api/cities/country/Spain
       Parameters = {}
          Headers = []
             Body = null
    Session Attrs = {}

Handler:
             Type = com.github.aha.sat.elk.city.CityController
           Method = com.github.aha.sat.elk.city.CityController#searchByCountry(String, Pageable)

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = org.springframework.http.converter.HttpMessageNotWritableException

La solution de contournement consiste à utiliser le constructeur avec tous les attributs comme suit:

Java

 

new PageImpl<City>(cities, ofSize(PAGE_SIZE), cities.size())

Au lieu de:

Java

 

new PageImpl<City>(cities)

Note: Cela devrait être corrigé dans Spring Boot 3.3 (voir ce commentaire d’issue).

Conclusion

Cet article a abordé les problèmes rencontrés lors de la mise à niveau vers la dernière version de Spring Boot 3.2.2 (au moment de la rédaction de cet article). L’article a commencé par la gestion des processeurs d’annotations en raison du changement de gestion des dépendances Hibernate. Ensuite, le changement dans la classe Unpaged et la solution de contournement pour l’utilisation de PageImpl a été expliquée.

Toutes les modifications (avec d’autres changements) peuvent être vues dans PR #64. Le code source complet démontré ci-dessus est disponible dans mon dépôt GitHub.

Source:
https://dzone.com/articles/upgrade-guide-to-spring-boot-32-for-spring-data-jp