此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data JPA 3.5.0spring-doc.cadn.net.cn

提前优化

本章介绍了 Spring Data 的 Ahead of Time (AOT) 优化,这些优化建立在 Spring 的 Ahead of Time Optimizations 之上。spring-doc.cadn.net.cn

最佳实践

注释您的域类型

在应用程序启动期间, Spring 会扫描 Classpath 中的域类,以便对实体进行早期处理。 通过使用特定于 Spring Data 的域类型进行注释@Table,@Document@Entity注释中,您可以帮助初始实体扫描并确保这些类型已注册ManagedTypes以获取运行时提示。 类路径扫描在本机映像排列中是不可能的,因此 Spring 必须使用ManagedTypes对于初始实体集。spring-doc.cadn.net.cn

运行时提示

与常规 JVM 运行时相比,将应用程序作为本机映像运行需要更多信息。 Spring Data 在 AOT 处理期间为本机图像使用提供运行时提示。 这些是以下方面的特别提示:spring-doc.cadn.net.cn

预先存储库

AOT 存储库是通过预先生成符合条件的查询方法实现来扩展 AOT 处理的。 对于在查询方法调用中执行的基础查询,查询方法对开发人员来说是不透明的。 AOT 存储库基于构建时已知的派生、注释和命名查询提供查询方法实现。 此优化将查询方法处理从运行时移至构建时,这可以显著提高性能,因为不需要在每次应用程序启动时反射性地分析查询方法。spring-doc.cadn.net.cn

生成的 AOT 存储库片段遵循<Repository FQCN>Impl__Aot,并与 repository 接口放置在同一包中。 您可以在生成的存储库查询方法的 String 形式中找到所有查询。spring-doc.cadn.net.cn

将 AOT 存储库类视为内部优化。 请勿直接在代码中使用它们,因为生成和实现详细信息在未来版本中可能会发生变化。

使用 AOT 存储库运行

AOT 是将 Spring 应用程序转换为本机可执行文件的强制性步骤,因此在此模式下运行时会自动启用它。 还可以通过设置spring.aot.enabledspring.aot.repositories.enabledproperties 设置为true.spring-doc.cadn.net.cn

AOT 存储库将配置更改贡献到实际存储库 Bean 注册中,以注册生成的存储库片段。spring-doc.cadn.net.cn

当包含 AOT 优化时,在构建时做出的一些决策将在应用程序设置中进行硬编码。 例如,在构建时启用的配置文件也会在运行时自动启用。 此外,实现存储库的 Spring Data 模块是固定的。 更改实现需要 AOT 重新处理。
AOT 处理可避免数据库访问。 因此,它初始化了一个用于元数据收集的内存 Hibernate 实例。 Hibernate 配置的类型由我们的 AOT 元数据收集器确定。 我们更喜欢使用PersistentEntityTypesbean(如果可用)并回退到PersistenceUnitInfo或者我们自己发现的类型。 如果我们的类型扫描不足以满足您的安排,您可以启用直接EntityManagerFactoryusage 通过配置spring.aot.jpa.repositories.use-entitymanager=true财产。

符合条件的方法

AOT 存储库筛选符合 AOT 处理条件的方法。 这些通常是不受 implementation fragment 支持的查询方法。spring-doc.cadn.net.cn

支持的功能spring-doc.cadn.net.cn

排除的方法spring-doc.cadn.net.cn

存储库元数据

AOT 处理会内省查询方法并收集有关存储库查询的元数据。 Spring Data JPA 将此元数据存储在 JSON 文件中,这些文件的名称类似于存储库接口,并存储在它旁边(即在同一个包中)。 存储库 JSON 元数据 包含有关查询和片段的详细信息。 以下存储库的示例如下所示:spring-doc.cadn.net.cn

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"
      }
    }
  ]
}

查询可能包含以下字段:spring-doc.cadn.net.cn

  • query:如果方法是查询方法,则为查询描述符。spring-doc.cadn.net.cn

  • fragment:如果方法调用委托给商店(存储库基类、功能片段,如 Querydsl)或用户片段,则目标片段。 片段要么只用fragment如果没有其他接口或 ASinterfacefragmenttuple 以防存在接口(例如 Querydsl 或用户声明的片段接口)。spring-doc.cadn.net.cn

规范化查询表单

查询的静态分析只允许对运行时查询行为进行有限的表示。 查询以其规范化(预解析和重写)的形式表示:spring-doc.cadn.net.cn

  • 值表达式将替换为绑定标记。spring-doc.cadn.net.cn

  • 查询遵循指定的查询语言(JPQL 或本机),不代表最终的 SQL 查询。 Spring Data 无法派生最终的 SQL 查询,因为这是特定于数据库的,并且取决于实际的运行时环境和参数(例如实体图、延迟加载)。spring-doc.cadn.net.cn

  • Query Metadata 不反映绑定值处理。StartingWith/EndingWith查询将通配符追加到实际的 bind 值。%spring-doc.cadn.net.cn

  • 运行时排序信息不能合并到查询字符串本身中,因为在构建时不知道该详细信息。spring-doc.cadn.net.cn