此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data JPA 3.5.0! |
提前优化
本章介绍了 Spring Data 的 Ahead of Time (AOT) 优化,这些优化建立在 Spring 的 Ahead of Time Optimizations 之上。
运行时提示
与常规 JVM 运行时相比,将应用程序作为本机映像运行需要更多信息。 Spring Data 在 AOT 处理期间为本机图像使用提供运行时提示。 这些是以下方面的特别提示:
-
审计
-
ManagedTypes
捕获类路径扫描的结果 -
存储 库
-
实体、返回类型和 Spring Data 注释的反射提示
-
存储库片段
-
查询 dsl
Q
类 -
Kotlin 协程支持
-
-
Web 支持(Jackson 提示
PagedModel
)
预先存储库
AOT 存储库是通过预先生成符合条件的查询方法实现来扩展 AOT 处理的。 对于在查询方法调用中执行的基础查询,查询方法对开发人员来说是不透明的。 AOT 存储库基于构建时已知的派生、注释和命名查询提供查询方法实现。 此优化将查询方法处理从运行时移至构建时,这可以显著提高性能,因为不需要在每次应用程序启动时反射性地分析查询方法。
生成的 AOT 存储库片段遵循<Repository FQCN>Impl__Aot
,并与 repository 接口放置在同一包中。
您可以在生成的存储库查询方法的 String 形式中找到所有查询。
将 AOT 存储库类视为内部优化。 请勿直接在代码中使用它们,因为生成和实现详细信息在未来版本中可能会发生变化。 |
使用 AOT 存储库运行
AOT 是将 Spring 应用程序转换为本机可执行文件的强制性步骤,因此在此模式下运行时会自动启用它。
还可以通过设置spring.aot.enabled
和spring.aot.repositories.enabled
properties 设置为true
.
AOT 存储库将配置更改贡献到实际存储库 Bean 注册中,以注册生成的存储库片段。
当包含 AOT 优化时,在构建时做出的一些决策将在应用程序设置中进行硬编码。 例如,在构建时启用的配置文件也会在运行时自动启用。 此外,实现存储库的 Spring Data 模块是固定的。 更改实现需要 AOT 重新处理。 |
AOT 处理可避免数据库访问。
因此,它初始化了一个用于元数据收集的内存 Hibernate 实例。
Hibernate 配置的类型由我们的 AOT 元数据收集器确定。
我们更喜欢使用PersistentEntityTypes bean(如果可用)并回退到PersistenceUnitInfo 或者我们自己发现的类型。
如果我们的类型扫描不足以满足您的安排,您可以启用直接EntityManagerFactory usage 通过配置spring.aot.jpa.repositories.use-entitymanager=true 财产。 |
符合条件的方法
AOT 存储库筛选符合 AOT 处理条件的方法。 这些通常是不受 implementation fragment 支持的查询方法。
支持的功能
-
派生查询方法,
@Query
/@NativeQuery
和命名查询方法 -
@Modifying
方法返回void
或int
-
@QueryHints
支持 -
分页
Slice
,Stream
和Optional
返回类型 -
排序查询重写
-
DTO 投影
-
值表达式(这些表达式需要一些反射信息。 请注意,使用 Value Expressions 需要表达式解析和上下文信息来评估表达式)
局限性
-
需要 Hibernate 进行 AOT 处理。
-
的配置
escapeCharacter
和queryEnhancerSelector
尚未考虑 -
QueryRewriter
必须是 no-args 类。QueryRewriter
尚不支持 bean。 -
接受
ScrollPosition
(例如Keyset
pagination)尚不支持
排除的方法
-
CrudRepository
、Querydsl、Query by Example 和其他基接口方法,因为它们的实现由基类各自的片段提供 -
实现过于复杂的方法
-
接受
ScrollPosition
(例如Keyset
分页) -
带 注解的存储过程查询方法
@Procedure
-
动态投影
-
存储库元数据
AOT 处理会内省查询方法并收集有关存储库查询的元数据。 Spring Data JPA 将此元数据存储在 JSON 文件中,这些文件的名称类似于存储库接口,并存储在它旁边(即在同一个包中)。 存储库 JSON 元数据 包含有关查询和片段的详细信息。 以下存储库的示例如下所示:
interface UserRepository extends CrudRepository<User, Integer> {
List<User> findUserNoArgumentsBy(); (1)
Page<User> findPageOfUsersByLastnameStartingWith(String lastname, Pageable page); (2)
@Query("select u from User u where u.emailAddress = ?1")
User findAnnotatedQueryByEmailAddress(String username); (3)
User findByEmailAddress(String emailAddress); (4)
@Procedure(value = "sp_add")
Integer providedProcedure(@Param("arg") Integer arg); (5)
}
1 | 不带参数的派生查询。 |
2 | 使用分页的派生查询。 |
3 | 带注释的查询。 |
4 | 命名查询。 |
5 | 具有提供的过程名称的存储过程。 虽然存储过程方法包含在 JSON 元数据中,但其方法代码块不会在 AOT 存储库中生成。 |
{
"name": "com.acme.UserRepository",
"module": "",
"type": "IMPERATIVE",
"methods": [
{
"name": "findUserNoArgumentsBy",
"signature": "public abstract java.util.List<com.acme.User> com.acme.UserRepository.findUserNoArgumentsBy()",
"query": {
"query": "SELECT u FROM com.acme.User u"
}
},
{
"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 u FROM com.acme.User u WHERE u.lastname LIKE ?1 ESCAPE '\\'",
"count-query": "SELECT COUNT(u) FROM com.acme.User u WHERE u.lastname LIKE ?1 ESCAPE '\\'"
}
},
{
"name": "findAnnotatedQueryByEmailAddress",
"signature": "public abstract com.acme.User com.acme.UserRepository.findAnnotatedQueryByEmailAddress(java.lang.String)",
"query": {
"query": "select u from User u where u.emailAddress = ?1"
}
},
{
"name": "findByEmailAddress",
"signature": "public abstract com.acme.User com.acme.UserRepository.findByEmailAddress(java.lang.String)",
"query": {
"name": "User.findByEmailAddress",
"query": "SELECT u FROM User u WHERE u.emailAddress = ?1"
}
},
{
"name": "providedProcedure",
"signature": "public abstract java.lang.Integer com.acme.UserRepository.providedProcedure(java.lang.Integer)",
"query": {
"procedure": "sp_add"
}
},
{
"name": "count",
"signature": "public abstract long org.springframework.data.repository.CrudRepository.count()",
"fragment": {
"fragment": "org.springframework.data.jpa.repository.support.SimpleJpaRepository"
}
}
]
}
查询可能包含以下字段:
-
query
:如果方法是查询方法,则为查询描述符。-
name
:命名查询的名称(如果查询是命名查询)。 -
query
用于从EntityManager
-
count-name
:如果 count 查询是命名查询,则命名 count 查询的名称。 -
count-query
:用于获取使用分页的查询方法的计数的计数查询。 -
procedure-name
:命名存储过程的名称(如果存储过程是命名存储过程)。 -
procedure
:如果查询方法使用存储过程,则为存储过程名称。
-
-
fragment
:如果方法调用委托给商店(存储库基类、功能片段,如 Querydsl)或用户片段,则目标片段。 片段要么只用fragment
如果没有其他接口或 ASinterface
和fragment
tuple 以防存在接口(例如 Querydsl 或用户声明的片段接口)。
规范化查询表单
查询的静态分析只允许对运行时查询行为进行有限的表示。 查询以其规范化(预解析和重写)的形式表示:
|