לשנה שעברה, כתבתי את המאמר, "מדריך עדכון ל-Spring Boot 3.0 עבור Spring Data JPA ו-Querydsl," עבור העדכון ל-Spring Boot 3.0.x. כעת, יש לנו Spring Boot 3.2. בואו נראה שתי בעיות שייתכן שתיתקלו בהן בעת העדכון ל-Spring Boot 3.2.2.
הטכנולוגיות המשמשות בפרויקט SAT הן:
- Spring Boot 3.2.2 ו-Spring Framework 6.1.3
- Hibernate + ייצור מודל JPA 6.4.1. סופי
- Spring Data JPA 3.2.2
- Querydsl 5.0.0.
שינויים
כל השינויים ב-Spring Boot 3.2 מתוארים בהערה שידור Spring Boot 3.2 וב-מה חדש בגרסה 6.1 עבור Spring Framework 6.1.
השינויים האחרונים ב-Spring Boot 3.2.2 ניתן למצוא ב-GitHub.
בעיות שנמצאו
- A different treatment of Hibernate dependencies due to the changed
hibernate-jpamodelgen
behavior for annotation processors Unpaged
מחדש.
נתחיל עם התלויות של Hibernate ראשית.
שילוב יצירת מטמולוגים סטטיים
השינוי הגדול ביותר בא מהתלות hibernate-jpamodelgen
, שמייצרת מטמולוג סטטי מטמולוג. ב-Hibernate 6.3, התייחסות לתלויות השתנתה כדי לפחות תלויות מעבריות. Spring Boot 3.2.0 העלה את התלות hibernate-jpamodelgen
לגרסה 6.3 (ראה עדכון תלויות). לרוע המזל, הגרסה החדשה גורמת לשגיאות הרכבה (ראה להלן).
הערה: Spring Boot 3.2.2 שמשתמשים כאן כבר משתמש ב-Hibernate 6.4 עם אותו התנהגות.
שגיאת הרכבה
עם השינוי הזה, ההרכבה של הפרויקט שלנו (בניית Maven) עם Spring Boot 3.2.2 נכשלת בשגיאה כזו:
[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
זה נגרם על ידי הגישה השתנתה ביצירת המטמולוג הסטטי המו�عלז במדריך ההעברה של Hibernate (ראה שילוב יצירת מטמולוגים סטטיים והפרט המקורי HHH-17362). ההסבר שלהם לשינוי כזה הוא זה:
"… בגרסאות קודמות של Hibernate ORM היו תלותים של
hibernate-jpamodelgen
שנזרקו למסד הנתונים שלך ללא ידיעתך. עם Hibernate ORM 6.3, עשויים להתרחש באגים בשלב ההידור בעקבות חוסר סימניות של מחלקות Antlr."
שינויי תלות
כפי שניתן לראות בתצוגות המסך להלן, תלותי Hibernate השתנו באמת.
- Spring Boot 3.1.6:
- Spring Boot 3.2.2:
הסבר
כפי שצויין במדריך ההעברה, יש לשנות את pom.xml
שלנו מתלות Maven פשוטות לנתיבי עיבוד הסימניות של פלטפורמת המהדר Maven (ראה תיעוד).
פתרון
ניתן להסיר את התלותים של Maven hibernate-jpamodelgen
ו-querydsl-apt
(במקרה שלנו) כמומלץ במאמר האחרון. במקום זאת, pom.xml
צריך להגדיר את יוצרי המטמולוגים הסטטיים באמצעות maven-compiler-plugin
ככה:
<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>
ראה את השינויים הרלוונטיים בפרויקט SAT ב-GitHub.
כשאנו נאלצים להשתמש בגישה זו בגלל hibernate-jpamodelgen
, עלינו ליישם אותה לכל ההסמכות הקשורות לעיבוד הערכות (querydsl-apt
או lombok
). לדוגמה, כשlombok
אינו בשימוש כזה, אנו מקבלים שגיאת הקמפילציה כזו:
[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
אותו הדבר חל גם על querydsl-apt
. במקרה זה, אנו יכולים לראות את שגיאת הקמפילציה כזו:
[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]
הסיבה היא ברורה. אנו צריכים ליישם את כל מעבדי הערכות בו זמנית. אחרת, חלקים מסוימים של הקוד עשויים לחסר, ואנו מקבלים שגיאת קמפילציה.
אונפייד מעוצב מחדש
הנושא השני הקטן השני קשור לשינוי במחלקה Unpaged
. סריקה של PageImpl
על ידי ספריית ה-Jackson הושפעה משינוי Unpaged
מ-enum
ל-class
(ראה 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());
...
}
כשמשתמשים ב-new PageImpl<City>(cities)
(כפי שהיינו רגילים להשתמש בו), אז זו שגיאה נזרקת:
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
הפיתרון הבטוח הוא להשתמש בבנאי עם כל התכונות כך:
new PageImpl<City>(cities, ofSize(PAGE_SIZE), cities.size())
במקום:
new PageImpl<City>(cities)
הערה: זה אמור להיות מתוקן ב-Spring Boot 3.3 (ראה תגובת הבעיה זו).
מסקנה
מאמר זה כיסה גם בעיות שנמצאו בעת עלייתן לגרסה האחרונה של Spring Boot 3.2.2 (בעת כתיבת מאמר זה). המאמר התחיל בטיפול במפרשי העיצוב עקב הניהול התלוי של היסברניה. לאחר מכן, נתבא על השינוי במחלקה Unpaged
ופיתרון נגדי לשימוש בPageImpl
.
כל השינויים (יחד עם שינויים נוספים) ניתן לראות בPR #64. הקוד המקורי המדגים לעיל זמין במאגר הGitHub שלי.
Source:
https://dzone.com/articles/upgrade-guide-to-spring-boot-32-for-spring-data-jp