Spring Data 預編譯倉庫 - 第 2 部分

工程 | Christoph Strobl | 2025年11月25日 | ...

總結 Road to GA 部落格系列,讓我們探討 Spring Data AOT Repositories 的好處。

早在 2025 年 5 月,我們首次將 預編譯(AOT)倉庫 作為 JPA 和 MongoDB 的預覽功能引入,同時釋出了 下一代 Spring Data 的第三個里程碑。簡而言之,此功能利用 AOT 處理,透過依賴倉庫的特定儲存特性,使用實際原始碼實現你的倉庫查詢方法。

從那時起,我們吸取了反饋意見,解決了粗糙之處,並引入了另外兩個模組:Apache Cassandra 和 JDBC。這意味著,隨著 2025.1.0 版本的釋出,您將能夠使用AOT生成的倉庫與四個Spring Data模組一起使用

您將得到什麼

透過利用通用的 Spring AOT 基礎設施,我們對您的應用程式配置有了深入的瞭解,從而實現了比使用註解處理器更好的整合。AOT 處理的預初始化應用程式上下文配置使我們能夠訪問所有已註冊的 bean、它們的型別和屬性,捕獲您的意見和配置。正如您所能想象的,當您希望與 Data JDBC 等整合一起工作時,這種洞察力至關重要:雖然目標是標準 SQL,但 JDBC 模組需要了解目標資料庫的潛在特殊性,這些特殊性通常伴隨著它們自己的方言偏差。訪問已註冊的 bean(如 JdbcDialect)使得生成正確的語句成為可能。

因此,當我們檢視生成的程式碼時,它與各個模組共享共同的原則,同時儘可能地貼近底層技術。

正如每個 Spring Data 模組都特定於其目標技術一樣,生成的程式碼也是如此。一個簡單的查詢方法,如 JDBC 中的 findByLastnameStartingWith,與為 JPAApache Cassandra 生成的方法截然不同。

Spring Data JDBC

public List<User> findByLastnameStartingWith(String lastname) {
  Criteria criteria = Criteria.where("lastname").like(escape(lastname) + "%");
  StatementFactory.SelectionBuilder builder = getStatementFactory().select(User.class).filter(criteria);

  RowMapper rowMapper = getRowMapperFactory().create(User.class);
  List result = (List) builder.executeWith((sql, paramSource) -> getJdbcOperations().query(sql, paramSource, new RowMapperResultSetExtractor<>(rowMapper)));
  return (List<User>) convertMany(result, User.class);
}

Spring Data JPA

public List<User> findByLastnameStartingWith(String lastname) {
  String queryString = "SELECT u FROM example.springdata.aot.User u WHERE u.lastname LIKE :lastname ESCAPE '\\'";
  Query query = entityManager.createQuery(queryString);
  query.setParameter("lastname", "%s%%".formatted(lastname));

  return (List<User>) query.getResultList();
}

Spring Data for Apache Cassandra

public List<User> findByLastnameStartingWith(String lastname) {
  Query query = Query.query(Criteria.where("lastname").like(lastname + "%"));

  ExecutableSelectOperation.TerminatingSelect<User> select = operations.query(User.class).matching(query);
  return select.all();
}

在構建時啟用通用 Spring AOT 支援,AOT 倉庫會預設生成並掛載。如果您希望,可以透過 spring.aot.repositories.enabled=false 選擇不生成倉庫程式碼,或者選擇單個模組,如 spring.aot.jdbc.repositories.enabled=false

要實際使用它們,您的應用程式必須在 AOT 模式下啟動(可以透過使用 spring.aot.enabled 屬性或作為 GraalVM 本機映象)。

但是,我們也要看看開發和除錯的一些優勢。

可除錯性和元資料

AOT 生成的倉庫不僅在程式碼中解釋了查詢的執行方式,您還可以在 IDE 中設定斷點以在需要時除錯語句。

順著在 AOT 處理期間瞭解查詢外觀的思路,我們為您的 Spring Data 介面引入了 JSON 元資料表示。就像上面提到的倉庫一樣,我們暫時稱之為 UserRepository,Spring Data 會生成一個 UserRepository.json 資源,該資源與您的倉庫介面位於同一包中。該檔案包含有關查詢方法、它們所針對的實現以及它們將執行的查詢的資訊。它可用於文件目的,但也可以被您喜歡的 IDE 讀取,以便在您開發時顯示附加資訊。

Spring Data JDBC

{
 "name": "example.springdata.UserRepository",
 "module": "JDBC",
 "type": "IMPERATIVE",
 "methods": [
 {
   "name": "findByLastnameStartingWith",
   "signature": "public abstract List<User> UserRepository.findByLastnameStartingWith(String)",
   "query": {
     "query": "SELECT 'USER'.'ID' AS 'ID', 'USER'.'LAST_NAME' AS 'LAST_NAME', 'USER'.'FIRST_NAME' AS 'FIRST_NAME' FROM 'USER' WHERE 'USER'.'LASTNAME' LIKE :name"
   }
 }
 //...

Spring Data JPA

{
 // ...
 "query": {
   "query": "SELECT u FROM example.springdata.User u WHERE u.lastname LIKE ?1 ESCAPE '\\'"
 }
}

Spring Data for Apache Cassandra

{
 // ...
 "query": {
   "query": "SELECT * FROM users WHERE last_name LIKE ?"
 }
}

您可以透過設定 spring.aot.repositories.metadata.enabled=false 來選擇性地停用元資料生成。

AOT 快取和 Leyden 專案

提前生成倉庫程式碼不僅具有可除錯性的優勢,而且與最新 Java 版本中的最新發展非常契合。與執行時生成的使事物正常工作的程式碼片段不同,預生成的倉庫可以在 AOT 快取訓練執行期間被 JVM 看到和分析。這有助於您透過從 Java 共享物件檔案提供已完全載入和連結的倉庫實現來減少啟動時間。

[info][class,load] example.springdata.aot.UserRepositoryImpl__AotRepository source: shared objects file

Spring Data NoSQL 實現的一個眾所周知的特性是它們的**物件對映**功能。不為人知的是,在執行時,Spring Data 會嘗試透過生成使用 MethodHandle 的位元組碼來最佳化新例項的建立和域物件的屬性訪問,從而提高載入/儲存效能。

收集有關您的域模型的資訊為生成的位元組碼的包含鋪平了道路,這些位元組碼用於構建時屬性訪問器和實體例項化器。擁有已生成的位元組碼消除了使用執行時最佳化的需要。此外,捕獲的實體例項化器和屬性訪問器被打包到捆綁的 JAR 中,因此可以直接透過類載入器提供,從而更有效地提供服務。

[info][class,load] example.springdata.User__Instantiator_cu2hga source: shared objects file
[info][class,load] example.springdata.User__Accessor_cu2hga source: shared objects file

有什麼缺點嗎?

顯然,計算時間不會簡單地消失,它會轉移到其他地方。在這種情況下,會轉移到構建階段。所以,是的,分析應用程式和準備資料,以及進行 JVM 訓練執行需要時間,並且可能需要更改您的構建和部署管道。您還需要用框架的某些動態方面來換取更少的記憶體消耗和更快的啟動時間。當選擇 AOT 時,您的部分配置需要被凍結。還記得之前提到的 JdbcDialect 嗎?這正是其中一個凍結點,因為您不能為**一個**資料庫生成最佳化的 SQL,然後切換到**另一個**資料庫,就好像什麼都沒改變一樣。

此外,目前提前(Ahead of Time)倉庫僅支援命令式倉庫介面。響應式介面不會觸發任何程式碼生成。

如果您好奇,可以直接使用 spring.aot.enabled 構建並執行您的應用程式,或者前往我們的 Spring Data 示例,我們已經為您準備了演示應用程式供您嘗試。

無論如何,您始終可以回退到不帶 spring.aot.enabled 標誌啟動應用程式,這將停用所有即時增強。這使您可以在 AOT 模式下構建和測試應用程式,同時內建了回退策略。

我們很高興看到您如何利用即時功能。
並且,一如既往,您的反饋至關重要!因此,我們期待聽到您關於改進方法的意見和經驗。

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有