L’anno scorso, ho scritto l’articolo, “Guida all’aggiornamento per Spring Boot 3.0 per Spring Data JPA e Querydsl,” per l’aggiornamento a Spring Boot 3.0.x. Ora, abbiamo Spring Boot 3.2. Vediamo due problemi che potresti incontrare durante l’aggiornamento a Spring Boot 3.2.2.
Le tecnologie utilizzate nel progetto SAT sono:
- Spring Boot 3.2.2 e Spring Framework 6.1.3
- Hibernate + generatore di modelli JPA 6.4.1. Final
- Spring Data JPA 3.2.2
- Querydsl 5.0.0.
Modifiche
Tutte le modifiche in Spring Boot 3.2 sono descritte in Note sulla versione di Spring Boot 3.2 e Novità nella versione 6.1 per Spring Framework 6.1.
Le ultime modifiche in Spring Boot 3.2.2 possono essere trovate su GitHub.
Problemi Riscontrati
- A different treatment of Hibernate dependencies due to the changed
hibernate-jpamodelgen
behavior for annotation processors Unpaged
class redesigned.
Cominciamo prima con le dipendenze di Hibernate.
Integrazione della Generazione del Metamodello Statico
Il cambiamento più significativo deriva dalla dipendenza hibernate-jpamodelgen
, che genera un metamodello statico metamodello. In Hibernate 6.3, il trattamento delle dipendenze è stato modificato per mitigare le dipendenze transitive. Spring Boot 3.2.0 ha aggiornato la dipendenza hibernate-jpamodelgen
alla versione 6.3 (vedi Aggiornamenti delle Dipendenze). Sfortunatamente, la nuova versione causa errori di compilazione (vedi sotto).
Nota: Spring Boot 3.2.2 utilizzato qui già utilizza Hibernate 6.4 con lo stesso comportamento.
Errore di Compilazione
Con questo cambiamento, la compilazione del nostro progetto (build Maven) con Spring Boot 3.2.2 fallisce con un errore simile a questo:
[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
Questo è causato dall’approccio modificato nella generazione del metamodello statico annunciato nella guida di migrazione di Hibernate (vedi Integrazione della Generazione del Metamodello Statico e l’issue originale HHH-17362). La loro spiegazione per questo cambiamento è la seguente:
“… in versioni precedenti di Hibernate ORM si verificava uno smarrimento di dipendenze di
hibernate-jpamodelgen
nel percorso di compilazione senza rendersene conto. Con Hibernate ORM 6.3, potresti ora riscontrare un errore di compilazione durante il processamento delle annotazioni a causa della mancanza di classi Antlr.”
Modifiche alle Dipendenze
Come puoi vedere nelle schermate qui sotto, le dipendenze di Hibernate sono state davvero modificate.
- Spring Boot 3.1.6:
- Spring Boot 3.2.2:
Spiegazione
Come indicato nella guida di migrazione, è necessario modificare il nostro pom.xml
da una semplice dipendenza Maven alle path dei processori di annotazioni del plugin di compilatore Maven (vedi documentazione).
Soluzione
Possiamo rimuovere le dipendenze Maven hibernate-jpamodelgen
e querydsl-apt
(nel nostro caso) come raccomandato nell’ultimo articolo. Invece, pom.xml
deve definire i generatori di modelli metamodello statici tramite maven-compiler-plugin
in questo modo:
<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>
Vedi le modifiche correlate nel progetto SAT su GitHub.
Poiché siamo costretti a utilizzare questo approccio a causa di hibernate-jpamodelgen
, dobbiamo applicarlo a tutte le dipendenze strettamente legate alla elaborazione delle annotazioni (querydsl-apt
o lombok
). Ad esempio, quando lombok
non viene utilizzato in questo modo, otteniamo un errore di compilazione simile a questo:
[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
Lo stesso vale per querydsl-apt
. In questo caso, possiamo vedere un errore di compilazione simile a questo:
[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 ragione è ovvia. Dobbiamo applicare tutti i processori di annotazioni contemporaneamente. Altrimenti, alcuni pezzi di codice potrebbero mancare e otteniamo un errore di compilazione.
Ridisegnato Non Paginato
Il secondo problema minore è legato a un cambiamento nella classe Unpaged
. La serializzazione di PageImpl
da parte della libreria Jackson è stata influenzata dal cambiamento di Unpaged
da enum
a class
(vedi 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());
...
}
Quando viene utilizzato new PageImpl<City>(cities)
(come eravamo abituati a usarlo), viene lanciato questo errore:
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 soluzione alternativa è utilizzare il costruttore con tutti gli attributi come:
new PageImpl<City>(cities, ofSize(PAGE_SIZE), cities.size())
Invece di:
new PageImpl<City>(cities)
Nota: Dovrebbe essere risolto in Spring Boot 3.3 (vedi questo commento dell’issue).
Conclusione
Questo articolo ha trattato sia i problemi riscontrati durante l’aggiornamento alla versione più recente di Spring Boot 3.2.2 (al momento della stesura di questo articolo). L’articolo è iniziato con la gestione dei processori di annotazioni a causa del cambiamento nella gestione delle dipendenze di Hibernate. Successivamente, è stato spiegato il cambiamento nella classe Unpaged
e la soluzione alternativa per l’utilizzo della classe PageImpl
.
Tutte le modifiche (insieme ad altre modifiche) possono essere osservate in PR #64. Il codice sorgente completo dimostrato sopra è disponibile nel mio GitHub repository.
Source:
https://dzone.com/articles/upgrade-guide-to-spring-boot-32-for-spring-data-jp