預先最佳化
本章介紹了 Spring Data 的提前(AOT)最佳化,這些最佳化基於 Spring 的提前最佳化。
執行時提示
將應用程式作為原生映象執行需要比常規 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和命名查詢方法 -
返回
void、int和long的@Modifying方法 -
分頁、
Slice、Stream和Optional返回型別 -
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 或使用者宣告的片段介面),則以interface和fragment元組形式描述。
|
規範化查詢形式
對查詢進行靜態分析只能有限地表示執行時查詢行為。查詢以其規範化(預解析和重寫)形式表示
|