Letztes Jahr habe ich den Artikel „Upgrade-Anleitung zu Spring Boot 3.0 für Spring Data JPA und Querydsl“ für die Aktualisierung auf Spring Boot 3.0.x geschrieben. Jetzt haben wir Spring Boot 3.2. Schauen wir uns zwei Probleme an, mit denen Sie möglicherweise konfrontiert sind, wenn Sie auf Spring Boot 3.2.2 aktualisieren.
Die in dem SAT-Projekt verwendeten Technologien sind:
- Spring Boot 3.2.2 und Spring Framework 6.1.3
- Hibernate + JPA-Modellgenerator 6.4.1. Final
- Spring Data JPA 3.2.2
- Querydsl 5.0.0.
Änderungen
Alle Änderungen in Spring Boot 3.2 sind in den Spring Boot 3.2 Release Notes und in Was ist neu in Version 6.1 für Spring Framework 6.1 beschrieben.
Die neuesten Änderungen in Spring Boot 3.2.2 finden Sie auf GitHub.
Gefundene Probleme
- A different treatment of Hibernate dependencies due to the changed
hibernate-jpamodelgen
behavior for annotation processors Unpaged
-Klasse wurde überarbeitet.
Fangen wir zunächst mit den Hibernate-Abhängigkeiten an.
Integration der Generierung statischer Metamodelle
Die größte Änderung ergibt sich aus der Abhängigkeit hibernate-jpamodelgen
, die ein statisches Metamodell erzeugt. In Hibernate 6.3 wurde die Behandlung von Abhängigkeiten geändert, um transitiven Abhängigkeiten entgegenzuwirken. Spring Boot 3.2.0 hat die Abhängigkeit hibernate-jpamodelgen
auf die Version 6.3 erhöht (siehe Abhängigkeits-Upgrades). Leider führt die neue Version zu Kompilierungsfehlern (siehe unten).
Hinweis: Hier verwendetes Spring Boot 3.2.2 verwendet bereits Hibernate 6.4 mit gleichem Verhalten.
Kompilierungsfehler
Durch diese Änderung schlägt der Build unseres Projekts (Maven-Build) mit Spring Boot 3.2.2 aufgrund eines Fehlers wie diesem fehl:
[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
Dies wird durch den geänderten Ansatz bei der Generierung des statischen Metamodells verursacht, wie in der Hibernate-Migrationsanleitung angekündigt (siehe Integration der Generierung statischer Metamodelle und das ursprüngliche Problem HHH-17362). Ihre Begründung für eine solche Änderung lautet:
„… in früheren Versionen von Hibernate ORM flossen Abhängigkeiten von
hibernate-jpamodelgen
unbewusst in Ihren Kompilationspfad ein. Mit Hibernate ORM 6.3 können Sie nun einen Kompilierungsfehler während der Annotationverarbeitung aufgrund fehlender Antlr-Klassen erleben.“
Abhängigkeitsänderungen
Wie Sie auf den folgenden Screenshots sehen können, wurden die Hibernate-Abhängigkeiten tatsächlich geändert.
- Spring Boot 3.1.6:
- Spring Boot 3.2.2:
Erklärung
Wie im Migrationsleitfaden angegeben, müssen wir unsere pom.xml
von einer einfachen Maven-Abhängigkeit zu den Annotationverarbeitungspfaden des Maven-Compiler-Plugins ändern (siehe Dokumentation).
Lösung
Wir können die Maven-Abhängigkeiten hibernate-jpamodelgen
und querydsl-apt
(in unserem Fall) wie im letzten Artikel empfohlen entfernen. Stattdessen muss pom.xml
die statischen Metamodellgeneratoren über maven-compiler-plugin
wie folgt definieren:
<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>
Siehe die bezüglichen Änderungen im SAT-Projekt auf GitHub.
Da wir aufgrund von hibernate-jpamodelgen
gezwungen sind, diesen Ansatz zu verwenden, müssen wir ihn auf alle Abhängigkeiten anwenden, die eng mit Annotation-Verarbeitung verbunden sind (querydsl-apt
oder lombok
). Zum Beispiel erhalten wir bei nicht korrektem Gebrauch von lombok
einen Kompilierungsfehler wie diesen:
[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
Dasselbe gilt für querydsl-apt
. In diesem Fall sehen wir einen Kompilierungsfehler wie diesen:
[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]
Der Grund ist offensichtlich. Wir müssen alle Annotation-Prozessoren gleichzeitig anwenden. Andernfalls können einige Code-Teile fehlen, und wir erhalten einen Kompilierungsfehler.
Unpaged Redesigned
Das zweite kleine Problem ist auf eine Änderung in der Unpaged
-Klasse zurückzuführen. Die Serialisierung von PageImpl
durch die Jackson-Bibliothek wurde durch die Änderung von Unpaged
von einem enum
zu einer class
betroffen (siehe 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());
...
}
Wenn new PageImpl<City>(cities)
verwendet wird (wie wir es gewohnt waren), wird dieser Fehler ausgelöst:
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
Die Problemumgehung besteht darin, den Konstruktor mit allen Attributen wie folgt zu verwenden:
new PageImpl<City>(cities, ofSize(PAGE_SIZE), cities.size())
Anstatt:
new PageImpl<City>(cities)
Hinweis: Das sollte in Spring Boot 3.3 behoben sein (siehe diesen Issue-Kommentar).
Schlussfolgerung
Dieser Artikel behandelt sowohl gefundene Probleme beim Upgrade auf die neueste Version von Spring Boot 3.2.2 (zum Zeitpunkt der Erstellung dieses Artikels). Der Artikel beginnt mit der Behandlung von Annotation-Prozessoren aufgrund der geänderten Hibernate-Abhängigkeitsverwaltung. Anschließend wurde die Änderung der Unpaged
-Klasse und eine Abhilfe für die Verwendung von PageImpl
erläutert.
Alle diese Änderungen (zusammen mit einigen anderen Änderungen) sind in PR #64 zu sehen. Der vollständige Quellcode, der oben demonstriert wurde, ist in meinem GitHub-Repository verfügbar.
Source:
https://dzone.com/articles/upgrade-guide-to-spring-boot-32-for-spring-data-jp