預先最佳化

本章介紹了 Spring Data 的提前(AOT)最佳化,這些最佳化基於 Spring 的提前最佳化

最佳實踐

註解您的領域型別

在應用程式啟動期間,Spring 會掃描類路徑中的域類以進行早期實體處理。透過使用 Spring Data 特定的 @Table@Document@Entity 註解來標註您的域型別,您可以幫助初始實體掃描,並確保這些型別在 ManagedTypes 中註冊以用於執行時提示。在原生映象環境中無法進行類路徑掃描,因此 Spring 必須使用 ManagedTypes 來處理初始實體集。

執行時提示

將應用程式作為原生映象執行需要比常規 JVM 執行時更多的資訊。Spring Data 在 AOT 處理期間為原生映象使用貢獻了 執行時提示。這些提示特別適用於

  • 審計

  • ManagedTypes 用於捕獲類路徑掃描的結果

  • Repositories

    • 實體、返回型別和 Spring Data 註解的反射提示

    • Repository 片段

    • Querydsl Q

    • Kotlin 協程支援

  • Web 支援(PagedModel 的 Jackson 提示)

提前(AOT)Repository

AOT Repository 是 AOT 處理的擴充套件,透過預生成符合條件的查詢方法實現。查詢方法對開發人員來說是不透明的,因為它隱藏了在查詢方法呼叫中執行的底層查詢。AOT Repository 基於在構建時已知的派生、註解和命名查詢來貢獻查詢方法實現。這種最佳化將查詢方法處理從執行時移至構建時,這可以顯著提高效能,因為查詢方法無需在每次應用程式啟動時進行反射分析。

生成的 AOT 倉庫片段遵循 <Repository FQCN>Impl__Aot 的命名方案,並放置在與倉庫介面相同的包中。您可以在生成的倉庫查詢方法中找到所有以字串形式存在的查詢。

請將 AOT Repository 類視為內部最佳化。不要在您的程式碼中直接使用它們,因為生成和實現細節可能會在未來的版本中發生變化。

使用 AOT 倉庫執行

AOT 是將 Spring 應用程式轉換為原生可執行檔案的一個強制步驟,因此在此模式下執行時會自動啟用。當啟用 AOT(無論是用於原生編譯還是透過設定 spring.aot.enabled=true)時,也會生成 AOT 倉庫。

您可以完全停用 AOT 倉庫生成,或者只停用 JDBC AOT 倉庫。

  • 設定 spring.aot.repositories.enabled=false 屬性可以停用所有 Spring Data 模組的生成倉庫。

  • 設定 spring.aot.jdbc.repositories.enabled=false 屬性可以僅停用 JDBC AOT 倉庫。

AOT 倉庫會向實際的倉庫 bean 註冊貢獻配置更改,以註冊生成的倉庫片段。

當包含 AOT 最佳化時,一些在構建時做出的決定會被硬編碼到應用程式設定中。例如,在構建時啟用的配置檔案在執行時也會自動啟用。此外,實現倉庫的 Spring Data 模組是固定的。更改實現需要重新進行 AOT 處理。
請提供 JdbcDialect 以避免方言檢測導致的早期資料庫訪問。

符合條件的方法

AOT 倉庫會過濾符合 AOT 處理條件的方法。這些方法通常是不由實現片段支援的所有查詢方法。

支援的功能

  • 派生查詢方法、@Query 和命名查詢方法

  • 返回 voidintlong@Modifying 方法

  • 分頁、SliceStreamOptional 返回型別

  • DTO 和介面投影

  • 值表示式

限制

  • 接受 ScrollPosition(例如 Keyset 分頁)的方法尚不支援

排除的方法

  • CrudRepository、Querydsl、Query by Example 以及其他基本介面方法,因為它們的實現由基類各自的片段提供

  • 實現過於複雜的方法

Repository 元資料

AOT 處理會內省查詢方法並收集有關倉庫查詢的元資料。Spring Data JDBC 將此元資料儲存在 JSON 檔案中,這些檔案以倉庫介面命名並與其儲存在同一位置(即同一包中)。倉庫 JSON 元資料包含有關查詢和片段的詳細資訊。以下是以下倉庫的示例:

interface UserRepository extends CrudRepository<User, Integer> {

  List<User> findUserNoArgumentsBy();                                                  (1)

  Page<User> findPageOfUsersByLastnameStartingWith(String lastname, Pageable page);    (2)

  @Query("select * from User u where username = ?1")
  User findAnnotatedQueryByEmailAddress(String username);                              (3)

  User findByEmailAddress(String emailAddress);                                        (4)
}
1 無引數的派生查詢。
2 使用分頁的派生查詢。
3 帶註解的查詢。
4 命名查詢。儘管儲存過程方法包含在 JSON 元資料中,但它們的方法程式碼塊不會在 AOT 倉庫中生成。
{
  "name": "com.acme.UserRepository",
  "module": "JDBC",
  "type": "IMPERATIVE",
  "methods": [
    {
      "name": "findUserNoArgumentsBy",
      "signature": "public abstract java.util.List<com.acme.User> com.acme.UserRepository.findUserNoArgumentsBy()",
      "query": {
        "query": "SELECT * FROM User"
      }
    },
    {
      "name": "findPageOfUsersByLastnameStartingWith",
      "signature": "public abstract org.springframework.data.domain.Page<com.acme.User> com.acme.UserRepository.findPageOfUsersByLastnameStartingWith(java.lang.String,org.springframework.data.domain.Pageable)",
      "query": {
        "query": "SELECT * FROM User u WHERE lastname LIKE :lastname",
        "count-query": "SELECT COUNT(*) FROM User WHERE lastname LIKE :lastname"
      }
    },
    {
      "name": "findAnnotatedQueryByEmailAddress",
      "signature": "public abstract com.acme.User com.acme.UserRepository.findAnnotatedQueryByEmailAddress(java.lang.String)",
      "query": {
        "query": "select * from User where emailAddress = ?1"
      }
    },
    {
      "name": "findByEmailAddress",
      "signature": "public abstract com.acme.User com.acme.UserRepository.findByEmailAddress(java.lang.String)",
      "query": {
        "name": "User.findByEmailAddress",
        "query": "SELECT * FROM User WHERE emailAddress = ?1"
      }
    },
    {
      "name": "count",
      "signature": "public abstract long org.springframework.data.repository.CrudRepository.count()",
      "fragment": {
        "fragment": "org.springframework.data.jdbc.repository.support.SimpleJdbcRepository"
      }
    }
  ]
}

查詢可能包含以下欄位:

  • query:如果方法是查詢方法,則為查詢描述符。

    • name:如果查詢是命名查詢,則為命名查詢的名稱。

    • query:用於從 EntityManager 獲取查詢方法結果的查詢

    • count-name:如果計數查詢是命名查詢,則為命名計數查詢的名稱。

    • count-query:用於獲取使用分頁的查詢方法的計數的計數查詢。

  • fragment:如果方法呼叫委託給儲存(倉庫基類、功能片段如 Querydsl)或使用者片段,則為目標片段。如果不存在進一步的介面,片段僅用 fragment 描述;如果存在介面(如 Querydsl 或使用者宣告的片段介面),則以 interfacefragment 元組形式描述。

規範化查詢形式

對查詢進行靜態分析只能有限地表示執行時查詢行為。查詢以其規範化(預解析和重寫)形式表示

  • 值表示式被替換為繫結標記。

  • 查詢元資料不反映繫結值處理。StartingWith/EndingWith 查詢會在實際繫結值之前/之後新增萬用字元 %

  • 執行時排序資訊無法合併到查詢字串本身,因為該細節在構建時是未知的。

© . This site is unofficial and not affiliated with VMware.