Vorig jaar schreef ik het artikel, “Upgrade Guide To Spring Boot 3.0 for Spring Data JPA and Querydsl“, voor de upgrade naar Spring Boot 3.0.x. Nu hebben we Spring Boot 3.2. Laten we twee problemen bekijken die je misschien tegenkomt bij de upgrade naar Spring Boot 3.2.2.
De technologieën die worden gebruikt in het SAT-project zijn:
- Spring Boot 3.2.2 en Spring Framework 6.1.3
- Hibernate + JPA model generator 6.4.1. Final
- Spring Data JPA 3.2.2
- Querydsl 5.0.0.
Wijzigingen
Alle wijzigingen in Spring Boot 3.2 worden beschreven in Spring Boot 3.2 Release Notes en What’s New in Version 6.1 voor Spring Framework 6.1.
De nieuwste wijzigingen in Spring Boot 3.2.2 kunnen worden gevonden op GitHub.
Problemen gevonden
- A different treatment of Hibernate dependencies due to the changed
hibernate-jpamodelgen
behavior for annotation processors Unpaged
class is opnieuw ontworpen.
Laten we eerst beginnen met de Hibernate afhankelijkheden.
Integratie van Statische Metamodel Generatie
De grootste verandering komt van de hibernate-jpamodelgen
afhankelijkheid, die een statisch metamodel genereert. In Hibernate 6.3 is de behandeling van afhankelijkheden aangepast om transitieve afhankelijkheden te beperken. Spring Boot 3.2.0 heeft de hibernate-jpamodelgen
afhankelijkheid opgewaardeerd naar versie 6.3 (zie Dependency Upgrades). Helaas veroorzaakt de nieuwe versie compilatiefouten (zie hieronder).
Opmerking: Hier gebruikte Spring Boot 3.2.2 maakt al gebruik van Hibernate 6.4 met dezelfde gedraging.
Compilatiefout
Met deze wijziging mislukt de compilatie van ons project (Maven build) met Spring Boot 3.2.2 op een fout zoals deze:
[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
Dit wordt veroorzaakt door de aangepaste aanpak in de statische metamodelgeneratie, zoals aangekondigd in de Hibernate migratiegids (zie Integratie van Statische Metamodel Generatie en het oorspronkelijke probleem HHH-17362). Hun verklaring voor deze wijziging is als volgt:
“… in eerdere versies van Hibernate ORM lekte je onbewust afhankelijkheden van
hibernate-jpamodelgen
in je compile classpath. Met Hibernate ORM 6.3 kun je nu een compilatiefout ervaren tijdens annotatieverwerking met betrekking tot ontbrekende Antlr-klassen.”
Afhankelijkheidswijzigingen
Zoals je in de onderstaande screenshots kunt zien, zijn de Hibernate-afhankelijkheden echt gewijzigd.
- Spring Boot 3.1.6:
- Spring Boot 3.2.2:
Uitleg
Zoals vermeld in de migratiegids, moeten we onze pom.xml
wijzigen van een eenvoudige Maven-afhankelijkheid naar de annotatieverwerkingspaden van de Maven compiler plugin (zie documentatie).
Oplossing
We kunnen de Maven-afhankelijkheden hibernate-jpamodelgen
en querydsl-apt
(in ons geval) zoals aanbevolen in het laatste artikel verwijderen. In plaats daarvan moet pom.xml
de statische metamodelgeneratoren definiëren via maven-compiler-plugin
op deze manier:
<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>
Zie de gerelateerde wijzigingen in het SAT-project op GitHub.
Omdat we gedwongen zijn deze aanpak te gebruiken vanwege hibernate-jpamodelgen
, moeten we deze toepassen op alle afhankelijkheden die nauw verbonden zijn met annotatie-verwerking (querydsl-apt
of lombok
). Bijvoorbeeld, wanneer lombok
niet op deze manier wordt gebruikt, krijgen we een compilatiefout zoals deze:
[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
Hetzelfde geldt voor querydsl-apt
. In dit geval zien we een compilatiefout zoals deze:
[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]
De reden is duidelijk. We moeten alle annotatie-verwerkers tegelijk toepassen. Anders kunnen sommige codefragmenten ontbreken, en krijgen we een compilatiefout.
Herontworpen Unpaged
Het tweede kleine probleem heeft betrekking op een verandering in de Unpaged
klasse. De serialisatie van PageImpl
door de Jackson-bibliotheek werd beïnvloed door de verandering van Unpaged
van enum
naar class
(zie spring-projects/spring-data-commons#2987).
- Spring Boot 3.1.6:
public interface Pageable {
static Pageable unpaged() {
return Unpaged.INSTANCE;
}
...
}
enum Unpaged implements Pageable {
INSTANCE;
...
}
- Spring Boot 3.2.2:
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());
...
}
Wanneer new PageImpl<City>(cities)
wordt gebruikt (zoals we gewend waren), dan wordt deze fout gegooid:
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
De oplossing is om de constructor te gebruiken met alle attributen als:
new PageImpl<City>(cities, ofSize(PAGE_SIZE), cities.size())
In plaats van:
new PageImpl<City>(cities)
Opmerking: Dit zou moeten worden opgelost in Spring Boot 3.3 (zie deze issue commentaar).
Conclusie
Dit artikel heeft zowel de problemen behandeld die optraden bij de upgrade naar de nieuwste versie van Spring Boot 3.2.2 (op het moment van het schrijven van dit artikel). Het artikel begon met de afhandeling van annotatieprocessoren als gevolg van de veranderde Hibernate afhankelijkheidsbeheer. Vervolgens werd de wijziging in de Unpaged
klasse en een oplossing voor het gebruik van PageImpl
uitgelegd.
Alle wijzigingen (samen met enkele andere wijzigingen) zijn te zien in PR #64. De volledige broncode die hierboven wordt getoond, is beschikbaar in mijn GitHub repository.
Source:
https://dzone.com/articles/upgrade-guide-to-spring-boot-32-for-spring-data-jp