Spring Data JPA - 参考文档

Spring Data JPA - 参考文档

OliverGierke Thomas DarimontChristoph StroblMark PaluchJay Bryant版本2.1.0.RELEASE,2018-09-21

©2008-2018原作者。

 本文档的副本可供您自己使用并分发给他人,前提是您不对此类副本收取任何费用,并且每份副本均包含本版权声明,无论是以印刷版还是电子版分发。

 

目录

前言

1.项目元数据

2.新的和值得注意的

2.1。Spring Data JPA 1.11中的新功能

2.2。Spring Data JPA 1.10中的新功能

3.依赖性

3.1。Spring Boot的依赖管理

3.2。Spring框架

4.使用Spring Data Repositories

4.1。核心概念

4.2。查询方法

4.3。定义存储库接口

4.4。定义查询方法

4.5。创建存储库实例

4.6。Spring Data Repositories的自定义实现

4.7。从聚合根发布事件

4.8。Spring Data Extensions

参考文档

5. JPA存储库

5.1。介绍

5.2。坚持实体

5.3。查询方法

5.4。存储过程

5.5。产品规格

5.6。按示例查询

5.7。事务性

5.8。锁定

5.9。审计

5.10. Miscellaneous Considerations

附录

附录A:命名空间参考

该元素

附录B:Populators命名空间参考

元素

附录C:存储库查询关键字

支持的查询关键字

附录D:存储库查询返回类型

支持的查询返回类型

附录E:常见问题

共同

基础设施

审计

附录F:术语表


前言

Spring Data JPA为Java Persistence API(JPA)提供了存储库支持。它简化了需要访问JPA数据源的应用程序的开发。

1.项目元数据

2.新的和值得注意的

2.1。Spring Data JPA 1.11中的新功能

Spring Data JPA 1.11增加了以下功能:

  • 改进了与Hibernate 5.2的兼容性。

  • 支持按示例查询的任意匹配模式。

  • 分页查询执行优化。

  • 支持exists存储库查询派生中的投影。

2.2。Spring Data JPA 1.10中的新功能

Spring Data JPA 1.10增加了以下功能:

  • 支持存储库查询方法中的预测

  • 支持按示例查询

  • 以下注解已启用的基础上组成的注释:@EntityGraph@Lock@Modifying@Query@QueryHints,和@Procedure

  • 支持Contains集合表达式上的关键字。

  • AttributeConverter为实现ZoneIdJSR-310和ThreeTenBP的。

  • 升级到Querydsl 4,Hibernate 5,OpenJPA 2.4和EclipseLink 2.6.1。

3.依赖性

由于各个Spring Data模块的初始日期不同,因此大多数模块都带有不同的主要版本号和次要版本号。找到兼容版本的最简单方法是依赖我们随定义的兼容版本提供的Spring Data Release Train BOM。在Maven项目中,您将在<dependencyManagement />POM 的部分中声明此依赖项,如下所示:

示例1.使用Spring Data版本系列BOM

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><dependencyManagement></span>
  <span style="color:#000088"><dependencies></span>
    <span style="color:#000088"><dependency></span>
      <span style="color:#000088"><groupId></span><span style="color:#000000">org.springframework.data</span><span style="color:#000088"></groupId></span>
      <span style="color:#000088"><artifactId></span><span style="color:#000000">spring-data-releasetrain</span><span style="color:#000088"></artifactId></span>
      <span style="color:#000088"><version></span><span style="color:#000000">Lovelace-RELEASE</span><span style="color:#000088"></version></span>
      <span style="color:#000088"><scope></span><span style="color:#000000">import</span><span style="color:#000088"></scope></span>
      <span style="color:#000088"><type></span><span style="color:#000000">pom</span><span style="color:#000088"></type></span>
    <span style="color:#000088"></dependency></span>
  <span style="color:#000088"></dependencies></span>
<span style="color:#000088"></dependencyManagement></span></code></span></span>

目前的发布列车版本是Lovelace-RELEASE。列车名称按字母顺序上升,此处列出了当前可用的列车。版本名称遵循以下模式:${name}-${release}其中release可以是以下之一:

  • BUILD-SNAPSHOT:当前快照

  • M1M2等等:里程碑

  • RC1RC2等等:发布候选人

  • RELEASE:GA发布

  • SR1SR2等等:服务版本

可以在我们的Spring Data示例存储库中找到使用BOM的工作示例。有了这个,您可以在块中声明要使用的Spring Data模块,而不是版本<dependencies />,如下所示:

示例2.声明对Spring Data模块的依赖关系

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><dependencies></span>
  <span style="color:#000088"><dependency></span>
    <span style="color:#000088"><groupId></span><span style="color:#000000">org.springframework.data</span><span style="color:#000088"></groupId></span>
    <span style="color:#000088"><artifactId></span><span style="color:#000000">spring-data-jpa</span><span style="color:#000088"></artifactId></span>
  <span style="color:#000088"></dependency></span>
<span style="color:#000088"><dependencies></span></code></span></span>

3.1。Spring Boot的依赖管理

Spring Boot为您选择最新版本的Spring Data模块。如果您仍想升级到较新版本,请将该属性配置为您要使用spring-data-releasetrain.version列车名称和迭代

3.2。Spring框架

当前版本的Spring Data模块需要版本5.1.0.RELEASE或更高版本的Spring Framework。这些模块也可以使用该次要版本的旧版本。但是,强烈建议使用该代中的最新版本。

4.使用Spring Data Repositories

Spring Data存储库抽象的目标是显着减少为各种持久性存储实现数据访问层所需的样板代码量。

 

Spring Data存储库文档和您的模块

本章介绍Spring Data存储库的核心概念和接口。本章中的信息来自Spring Data Commons模块。它使用Java Persistence API(JPA)模块的配置和代码示例。您应该将XML名称空间声明和要扩展的类型调整为您使用的特定模块的等效项。“ 命名空间参考 ”涵盖XML配置,支持存储库API的所有Spring Data模块都支持XML配置。“ 存储库查询关键字 ”涵盖了存储库抽象支持的查询方法关键字。有关模块特定功能的详细信息,请参阅本文档该模块的章节。

4.1。核心概念

Spring Data存储库抽象中的中央接口是Repository。它将域类以及域类的ID类型作为类型参数进行管理。此接口主要用作标记接口,用于捕获要使用的类型,并帮助您发现扩展此接口的接口。该CrudRepository规定对于正在管理的实体类复杂的CRUD功能。

例3. CrudRepository界面

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID </span><span style="color:#000088">extends</span> <span style="color:#660066">Serializable</span><span style="color:#666600">></span>
  <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#666600"><</span><span style="color:#000000">S </span><span style="color:#000088">extends</span><span style="color:#000000"> T</span><span style="color:#666600">></span><span style="color:#000000"> S save</span><span style="color:#666600">(</span><span style="color:#000000">S entity</span><span style="color:#666600">);</span>      

  <span style="color:#660066">Optional</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> findById</span><span style="color:#666600">(</span><span style="color:#000000">ID primaryKey</span><span style="color:#666600">);</span> 

  <span style="color:#660066">Iterable</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> findAll</span><span style="color:#666600">();</span>               

  <span style="color:#000088">long</span><span style="color:#000000"> count</span><span style="color:#666600">();</span>                        

  <span style="color:#000088">void</span> <span style="color:#000088">delete</span><span style="color:#666600">(</span><span style="color:#000000">T entity</span><span style="color:#666600">);</span>               

  <span style="color:#000088">boolean</span><span style="color:#000000"> existsById</span><span style="color:#666600">(</span><span style="color:#000000">ID primaryKey</span><span style="color:#666600">);</span>   

  <span style="color:#880000">// … more functionality omitted.</span>
<span style="color:#666600">}</span></code></span></span>
 保存给定的实体。
 返回由给定ID标识的实体。
 返回所有实体。
 返回实体数量。
 删除给定的实体。
 指示是否存在具有给定ID的实体。
 我们还提供持久性技术特定的抽象,例如JpaRepositoryMongoRepositoryCrudRepository除了相当通用的持久性技术无关的接口之外,这些接口还扩展和公开了底层持久性技术的功能CrudRepository

最重要的是CrudRepository,有一个PagingAndSortingRepository抽象添加了额外的方法来简化对实体的分页访问:

例4. PagingAndSortingRepository界面

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">PagingAndSortingRepository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID </span><span style="color:#000088">extends</span> <span style="color:#660066">Serializable</span><span style="color:#666600">></span>
  <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#660066">Iterable</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> findAll</span><span style="color:#666600">(</span><span style="color:#660066">Sort</span><span style="color:#000000"> sort</span><span style="color:#666600">);</span>

  <span style="color:#660066">Page</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> findAll</span><span style="color:#666600">(</span><span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

要访问User页面大小为20 的第二页,您可以执行以下操作:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">PagingAndSortingRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span><span style="color:#000000"> repository </span><span style="color:#666600">=</span> <span style="color:#880000">// … get access to a bean</span>
<span style="color:#660066">Page</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> users </span><span style="color:#666600">=</span><span style="color:#000000"> repository</span><span style="color:#666600">.</span><span style="color:#000000">findAll</span><span style="color:#666600">(</span><span style="color:#000088">new</span> <span style="color:#660066">PageRequest</span><span style="color:#666600">(</span><span style="color:#006666">1</span><span style="color:#666600">,</span> <span style="color:#006666">20</span><span style="color:#666600">));</span></code></span></span>

除了查询方法之外,还可以使用计数和删除查询的查询派生。以下列表显示派生计数查询的接口定义:

示例5.派生计数查询

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#000088">long</span><span style="color:#000000"> countByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

以下列表显示了派生删除查询的接口定义:

示例6.派生的删除查询

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#000088">long</span><span style="color:#000000"> deleteByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>

  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> removeByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

4.2。查询方法

标准CRUD功能存储库通常对基础数据存储区进行查询。使用Spring Data,声明这些查询将分为四个步骤:

  1. 声明扩展Repository或其子接口之一的接口,并将其键入它应处理的域类和ID类型,如以下示例所示:

    <span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">PersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span> <span style="color:#666600">…</span> <span style="color:#666600">}</span></code></span></span>
  2. 在接口上声明查询方法。

    <span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">PersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
      <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>
    <span style="color:#666600">}</span></code></span></span>
  3. 设置Spring以使用JavaConfigXML配置为这些接口创建代理实例。

    1. 要使用Java配置,请创建类似于以下内容的类:

      <span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">import</span><span style="color:#000000"> org</span><span style="color:#666600">.</span><span style="color:#000000">springframework</span><span style="color:#666600">.</span><span style="color:#000000">data</span><span style="color:#666600">.</span><span style="color:#000000">jpa</span><span style="color:#666600">.</span><span style="color:#000000">repository</span><span style="color:#666600">.</span><span style="color:#000000">config</span><span style="color:#666600">.</span><span style="color:#660066">EnableJpaRepositories</span><span style="color:#666600">;</span>
      
      <span style="color:#006666">@EnableJpaRepositories</span>
      <span style="color:#000088">class</span> <span style="color:#660066">Config</span> <span style="color:#666600">{}</span></code></span></span>
    2. 要使用XML配置,请定义类似于以下内容的bean:

      <span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#666600"><?</span><span style="color:#000000">xml version</span><span style="color:#666600">=</span><span style="color:#008800">"1.0"</span><span style="color:#000000"> encoding</span><span style="color:#666600">=</span><span style="color:#008800">"UTF-8"</span><span style="color:#666600">?></span>
      <span style="color:#000088"><beans</span> <span style="color:#660066">xmlns</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/beans"</span>
         <span style="color:#660066">xmlns:xsi</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.w3.org/2001/XMLSchema-instance"</span>
         <span style="color:#660066">xmlns:jpa</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/data/jpa"</span>
         <span style="color:#660066">xsi:schemaLocation</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/data/jpa
           http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"</span><span style="color:#000088">></span>
      
         <span style="color:#000088"><jpa:repositories</span> <span style="color:#660066">base-package</span><span style="color:#666600">=</span><span style="color:#008800">"com.acme.repositories"</span><span style="color:#000088">/></span>
      
      <span style="color:#000088"></beans></span></code></span></span>

    在此示例中使用JPA名称空间。如果对任何其他商店使用存储库抽象,则需要将其更改为商店模块的相应名称空间声明。换句话说,你应该换一个jpa赞成,例如,mongodb

    +另请注意,JavaConfig变体未显式配置包,因为默认情况下使用带注释的类的包。要自定义要扫描的包,请使用basePackage…特定于数据存储库的@Enable${store}Repositories-annotation 的属性之一。

  4. 注入存储库实例并使用它,如以下示例所示:

    <span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">SomeClient</span> <span style="color:#666600">{</span>
    
      <span style="color:#000088">private</span> <span style="color:#000088">final</span> <span style="color:#660066">PersonRepository</span><span style="color:#000000"> repository</span><span style="color:#666600">;</span>
    
      <span style="color:#660066">SomeClient</span><span style="color:#666600">(</span><span style="color:#660066">PersonRepository</span><span style="color:#000000"> repository</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
        <span style="color:#000088">this</span><span style="color:#666600">.</span><span style="color:#000000">repository </span><span style="color:#666600">=</span><span style="color:#000000"> repository</span><span style="color:#666600">;</span>
      <span style="color:#666600">}</span>
    
      <span style="color:#000088">void</span><span style="color:#000000"> doSomething</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
        <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> persons </span><span style="color:#666600">=</span><span style="color:#000000"> repository</span><span style="color:#666600">.</span><span style="color:#000000">findByLastname</span><span style="color:#666600">(</span><span style="color:#008800">"Matthews"</span><span style="color:#666600">);</span>
      <span style="color:#666600">}</span>
    <span style="color:#666600">}</span></code></span></span>

以下各节详细说明了每个步骤:

4.3。定义存储库接口

首先,定义特定于域类的存储库接口。接口必须扩展Repository并键入域类和ID类型。如果要为该域类型公开CRUD方法,请使用extend CrudRepository而不是Repository

4.3.1。微调存储库定义

通常情况下,你的资料库接口扩展RepositoryCrudRepositoryPagingAndSortingRepository。或者,如果您不想扩展Spring Data接口,还可以使用注释来存储您的存储库接口@RepositoryDefinition。扩展CrudRepository公开了一组完整的方法来操纵您的实体。如果您希望对要公开的方法有选择性,请将要公开的方法复制CrudRepository到域存储库中。

 这样做可以让您在提供的Spring Data Repositories功能之上定义自己的抽象。

下面的示例示出了如何以选择性地露出CRUD方法(findByIdsave,在这种情况下):

示例7.有选择地暴露CRUD方法

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@NoRepositoryBean</span>
<span style="color:#000088">interface</span> <span style="color:#660066">MyBaseRepository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID </span><span style="color:#000088">extends</span> <span style="color:#660066">Serializable</span><span style="color:#666600">></span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#660066">Optional</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> findById</span><span style="color:#666600">(</span><span style="color:#000000">ID id</span><span style="color:#666600">);</span>

  <span style="color:#666600"><</span><span style="color:#000000">S </span><span style="color:#000088">extends</span><span style="color:#000000"> T</span><span style="color:#666600">></span><span style="color:#000000"> S save</span><span style="color:#666600">(</span><span style="color:#000000">S entity</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span>

<span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">MyBaseRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
  <span style="color:#660066">User</span><span style="color:#000000"> findByEmailAddress</span><span style="color:#666600">(</span><span style="color:#660066">EmailAddress</span><span style="color:#000000"> emailAddress</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

在前面的例子,你定义为所有站点库一个共同的基础界面和暴露findById(…),以及save(…)。这些方法被发送到基础信息库实现你所选择的由Spring提供的数据(例如,如果使用JPA商店,实现是SimpleJpaRepository),因为它们匹配方法签名CrudRepository。因此,UserRepository现在可以保存用户,按ID查找单个用户,并触发查询以Users通过电子邮件地址查找。

 中间存储库接口使用注释@NoRepositoryBean。确保将该注释添加到Spring Data不应在运行时创建实例的所有存储库接口。

4.3.2。存储库方法的空处理

从Spring Data 2.0开始,返回单个聚合实例的存储库CRUD方法使用Java 8 Optional来指示可能缺少值。除此之外,Spring Data支持在查询方法上返回以下包装类型:

  • com.google.common.base.Optional

  • scala.Option

  • io.vavr.control.Option

  • javaslang.control.Option (不推荐使用Javaslang)

或者,查询方法可以选择根本不使用包装类型。然后通过返回指示缺少查询结果null。保证返回集合,集合替代,包装器和流的存储库方法永远不会返回null,而是相应的空表示。有关详细信息,请参阅“ 存储库查询返回类型 ”。

可空性注释

您可以使用Spring Framework的可空性注释来表达存储库方法的可空性约束。它们null在运行时提供了一种工具友好的方法和选择性检查,如下所示:

  • @NonNullApi:在包级别上使用,以声明参数和返回值的默认行为是不接受或生成null值。

  • @NonNull:用于必须不是的参数或返回值null (参数上不需要,返回值@NonNullApi适用)。

  • @Nullable:用于可以的参数或返回值null

Spring注释是使用JSR 305注释进行元注释的(一种休眠但广泛传播的JSR)。JSR 305元注释允许IDEAEclipseKotlin等工具供应商以通用方式提供空安全支持,而无需对Spring注释进行硬编码支持。要为查询方法启用运行时检查可空性约束,需要使用Spring的@NonNullApiin 来激活包级别的非可空性package-info.java,如以下示例所示:

例8.声明不可为空性 package-info.java

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@org</span><span style="color:#666600">.</span><span style="color:#000000">springframework</span><span style="color:#666600">.</span><span style="color:#000000">lang</span><span style="color:#666600">.</span><span style="color:#660066">NonNullApi</span>
<span style="color:#000088">package</span><span style="color:#000000"> com</span><span style="color:#666600">.</span><span style="color:#000000">acme</span><span style="color:#666600">;</span></code></span></span>

一旦存在非空默认,就会在运行时验证存储库查询方法调用的可空性约束。如果查询执行结果违反了定义的约束,则抛出异常。当方法返回null但声明为非可空时(默认情况下,存储库所在的包中定义了注释)会发生这种情况。如果您想再次选择可以为空的结果,请有选择地使用@Nullable单个方法。使用本节开头提到的结果包装器类型继续按预期工作:将空结果转换为表示缺席的值。

以下示例显示了刚才描述的许多技术:

示例9.使用不同的可空性约束

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">package</span><span style="color:#000000"> com</span><span style="color:#666600">.</span><span style="color:#000000">acme</span><span style="color:#666600">;</span>                                                       

<span style="color:#000088">import</span><span style="color:#000000"> org</span><span style="color:#666600">.</span><span style="color:#000000">springframework</span><span style="color:#666600">.</span><span style="color:#000000">lang</span><span style="color:#666600">.</span><span style="color:#660066">Nullable</span><span style="color:#666600">;</span>

<span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#660066">User</span><span style="color:#000000"> getByEmailAddress</span><span style="color:#666600">(</span><span style="color:#660066">EmailAddress</span><span style="color:#000000"> emailAddress</span><span style="color:#666600">);</span>                    

  <span style="color:#006666">@Nullable</span>
  <span style="color:#660066">User</span><span style="color:#000000"> findByEmailAddress</span><span style="color:#666600">(</span><span style="color:#006666">@Nullable</span> <span style="color:#660066">EmailAddress</span><span style="color:#000000"> emailAdress</span><span style="color:#666600">);</span>          

  <span style="color:#660066">Optional</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findOptionalByEmailAddress</span><span style="color:#666600">(</span><span style="color:#660066">EmailAddress</span><span style="color:#000000"> emailAddress</span><span style="color:#666600">);</span> 
<span style="color:#666600">}</span></code></span></span>
 存储库驻留在我们已定义非空行为的包(或子包)中。
 抛出一个EmptyResultDataAccessException在执行时不产生结果的查询。抛出一个IllegalArgumentExceptionemailAddress交给该方法null
 null执行查询时不返回结果。也接受null作为的值emailAddress
 Optional.empty()执行查询时不返回结果。抛出一个IllegalArgumentExceptionemailAddress交给该方法null

基于Kotlin的存储库中的可空性

Kotlin 对语言中的可空性约束进行了定义。Kotlin代码编译为字节码,它不通过方法签名表达可空性约束,而是通过编译元数据表达。确保kotlin-reflect在项目中包含JAR,以便对Kotlin的可空性约束进行内省。Spring Data存储库使用语言机制来定义这些约束以应用相同的运行时检查,如下所示:

示例10.对Kotlin存储库使用可空性约束

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-kotlin"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#666600">:</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#666600">></span> <span style="color:#666600">{</span><span style="color:#000000">

  fun findByUsername</span><span style="color:#666600">(</span><span style="color:#000000">username</span><span style="color:#666600">:</span> <span style="color:#660066">String</span><span style="color:#666600">):</span> <span style="color:#660066">User</span>     <span style="color:#000000">

  fun findByFirstname</span><span style="color:#666600">(</span><span style="color:#000000">firstname</span><span style="color:#666600">:</span> <span style="color:#660066">String</span><span style="color:#666600">?):</span> <span style="color:#660066">User</span><span style="color:#666600">?</span> 
<span style="color:#666600">}</span></code></span></span>
 该方法将参数和结果都定义为非可空(Kotlin默认值)。Kotlin编译器拒绝传递null给方法的方法调用。如果查询执行产生空结果,EmptyResultDataAccessException则抛出a。
 该方法接受nullfirstname参数,并返回null,如果查询执行不产生结果。

4.3.3。使用具有多个Spring数据模块的存储库

在应用程序中使用唯一的Spring Data模块会使事情变得简单,因为定义范围内的所有存储库接口都绑定到Spring Data模块。有时,应用程序需要使用多个Spring Data模块。在这种情况下,存储库定义必须区分持久性技术。当它在类路径上检测到多个存储库工厂时,Spring Data进入严格的存储库配置模式。严格配置使用存储库或域类的详细信息来确定存储库定义的Spring Data模块绑定:

  1. 如果存储库定义扩展了特定于模块的存储库,那么它是特定Spring Data模块的有效候选者。

  2. 如果使用特定于模块的类型注释对域类进行注释,则它是特定Spring Data模块的有效候选者。Spring Data模块接受第三方注释(例如JPA @Entity)或提供自己的注释(例如@DocumentSpring Data MongoDB和Spring Data Elasticsearch)。

以下示例显示了使用特定于模块的接口的存储库(在本例中为JPA):

示例11.使用模块特定接口的存储库定义

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">MyRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span> <span style="color:#666600">}</span>

<span style="color:#006666">@NoRepositoryBean</span>
<span style="color:#000088">interface</span> <span style="color:#660066">MyBaseRepository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID </span><span style="color:#000088">extends</span> <span style="color:#660066">Serializable</span><span style="color:#666600">></span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
  <span style="color:#666600">…</span>
<span style="color:#666600">}</span>

<span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">MyBaseRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
  <span style="color:#666600">…</span>
<span style="color:#666600">}</span></code></span></span>

MyRepository并在其类型层次结构中UserRepository扩展JpaRepository。它们是Spring Data JPA模块的有效候选者。

以下示例显示了使用通用接口的存储库:

示例12.使用通用接口的存储库定义

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">AmbiguousRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
 <span style="color:#666600">…</span>
<span style="color:#666600">}</span>

<span style="color:#006666">@NoRepositoryBean</span>
<span style="color:#000088">interface</span> <span style="color:#660066">MyBaseRepository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID </span><span style="color:#000088">extends</span> <span style="color:#660066">Serializable</span><span style="color:#666600">></span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
  <span style="color:#666600">…</span>
<span style="color:#666600">}</span>

<span style="color:#000088">interface</span> <span style="color:#660066">AmbiguousUserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">MyBaseRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
  <span style="color:#666600">…</span>
<span style="color:#666600">}</span></code></span></span>

AmbiguousRepositoryAmbiguousUserRepository仅延伸Repository,并CrudRepository在他们的类型层次。虽然在使用独特的Spring Data模块时这是完全正常的,但是多个模块无法区分这些存储库应该绑定到哪个特定的Spring Data。

以下示例显示了使用带注释的域类的存储库:

示例13.使用带注释的域类的存储库定义

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">PersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
 <span style="color:#666600">…</span>
<span style="color:#666600">}</span>

<span style="color:#006666">@Entity</span>
<span style="color:#000088">class</span> <span style="color:#660066">Person</span> <span style="color:#666600">{</span>
  <span style="color:#666600">…</span>
<span style="color:#666600">}</span>

<span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
 <span style="color:#666600">…</span>
<span style="color:#666600">}</span>

<span style="color:#006666">@Document</span>
<span style="color:#000088">class</span> <span style="color:#660066">User</span> <span style="color:#666600">{</span>
  <span style="color:#666600">…</span>
<span style="color:#666600">}</span></code></span></span>

PersonRepository引用Person,使用JPA @Entity注释进行注释,因此该存储库显然属于Spring Data JPA。UserRepository引用User,使用Spring Data MongoDB的@Document注释进行注释。

以下错误示例显示了使用具有混合注释的域类的存储库:

示例14.使用具有混合注释的域类的存储库定义

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">JpaPersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
 <span style="color:#666600">…</span>
<span style="color:#666600">}</span>

<span style="color:#000088">interface</span> <span style="color:#660066">MongoDBPersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
 <span style="color:#666600">…</span>
<span style="color:#666600">}</span>

<span style="color:#006666">@Entity</span>
<span style="color:#006666">@Document</span>
<span style="color:#000088">class</span> <span style="color:#660066">Person</span> <span style="color:#666600">{</span>
  <span style="color:#666600">…</span>
<span style="color:#666600">}</span></code></span></span>

此示例显示了使用JPA和Spring Data MongoDB注释的域类。它定义了两个存储库,JpaPersonRepositoryMongoDBPersonRepository。一个用于JPA,另一个用于MongoDB用法。Spring Data不再能够将存储库分开,从而导致未定义的行为。

存储库类型详细信息区分域类注释用于严格存储库配置,以识别特定Spring数据模块的存储库候选。在同一域类型上使用多个持久性技术特定的注释是可能的,并允许跨多种持久性技术重用域类型。但是,Spring Data不再能够确定用于绑定存储库的唯一模块。

区分存储库的最后一种方法是使用存储库基础包。基础包定义了扫描存储库接口定义的起点,这意味着将存储库定义放在相应的包中。默认情况下,注释驱动的配置使用配置类的包。基于XML的配置中基本包是必需的。

以下示例显示了基础包的注释驱动配置:

示例15.基础包的注释驱动配置

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@EnableJpaRepositories</span><span style="color:#666600">(</span><span style="color:#000000">basePackages </span><span style="color:#666600">=</span> <span style="color:#008800">"com.acme.repositories.jpa"</span><span style="color:#666600">)</span>
<span style="color:#006666">@EnableMongoRepositories</span><span style="color:#666600">(</span><span style="color:#000000">basePackages </span><span style="color:#666600">=</span> <span style="color:#008800">"com.acme.repositories.mongo"</span><span style="color:#666600">)</span>
<span style="color:#000088">interface</span> <span style="color:#660066">Configuration</span> <span style="color:#666600">{</span> <span style="color:#666600">}</span></code></span></span>

4.4。定义查询方法

存储库代理有两种方法可以从方法名称派生特定于商店的查询:

  • 通过直接从方法名称派生查询。

  • 通过使用手动定义的查询。

可用选项取决于实际商店。但是,必须有一个策略来决定创建实际查询的内容。下一节将介绍可用选项。

4.4.1。查询查找策略

存储库基础结构可以使用以下策略来解析查询。使用XML配置,您可以通过query-lookup-strategy属性在命名空间配置策略。对于Java配置,您可以使用注释的queryLookupStrategy属性Enable${store}Repositories。特定数据存储可能不支持某些策略。

  • CREATE尝试从查询方法名称构造特定于商店的查询。一般方法是从方法名称中删除一组已知的前缀,并解析方法的其余部分。您可以在“ 查询创建 ”中阅读有关查询构造的更多信息。

  • USE_DECLARED_QUERY尝试查找声明的查询,如果找不到,则抛出异常。查询可以通过某处的注释来定义,也可以通过其他方式声明。查阅特定商店的文档以查找该商店​​的可用选项。如果存储库基础结构在引导时未找到该方法的声明查询,则它将失败。

  • CREATE_IF_NOT_FOUND(默认)组合CREATEUSE_DECLARED_QUERY。它首先查找声明的查询,如果没有找到声明的查询,它会创建一个基于自定义方法名称的查询。这是默认的查找策略,因此,如果您未明确配置任何内容,则使用此策略。它允许通过方法名称快速查询,还可以根据需要引入声明的查询来自定义这些查询。

4.4.2。查询创建

构建到Spring Data存储库基础结构中的查询构建器机制对于构建对存储库实体的约束查询很有用。该机制条前缀find…Byread…Byquery…Bycount…By,和get…By从所述方法和开始分析它的其余部分。introduction子句可以包含其他表达式,例如Distinct在要创建的查询上设置不同的标志。但是,第一个By用作分隔符以指示实际标准的开始。在最基本的层面上,您可以在实体属性上定义条件,并将它们与And和它们连接起来Or。以下示例显示了如何创建大量查询:

示例16.从方法名称创建查询

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">PersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findByEmailAddressAndLastname</span><span style="color:#666600">(</span><span style="color:#660066">EmailAddress</span><span style="color:#000000"> emailAddress</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>

  <span style="color:#880000">// Enables the distinct flag for the query</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findDistinctPeopleByLastnameOrFirstname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">);</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findPeopleDistinctByLastnameOrFirstname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">);</span>

  <span style="color:#880000">// Enabling ignoring case for an individual property</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findByLastnameIgnoreCase</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>
  <span style="color:#880000">// Enabling ignoring case for all suitable properties</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findByLastnameAndFirstnameAllIgnoreCase</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">);</span>

  <span style="color:#880000">// Enabling static ORDER BY for a query</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findByLastnameOrderByFirstnameAsc</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findByLastnameOrderByFirstnameDesc</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

解析方法的实际结果取决于您为其创建查询的持久性存储。但是,有一些一般要注意的事项:

  • 表达式通常是属性遍历与可以连接的运算符相结合。您可以将属性表达式与AND和组合使用OR。您还可以得到这样的运营商为支撑BetweenLessThanGreaterThan,和Like该属性的表达式。支持的运算符可能因数据存储而异,因此请参阅参考文档的相应部分。

  • 方法解析器支持IgnoreCase为各个属性(例如findByLastnameIgnoreCase(…))或支持忽略大小写的类型(String例如,通常为实例)的所有属性设置标志findByLastnameAndFirstnameAllIgnoreCase(…)。是否支持忽略大小写可能因商店而异,因此请参阅参考文档中有关特定于商店的查询方法的相关章节。

  • 您可以通过OrderBy在引用属性的查询方法中附加子句并提供排序方向(AscDesc)来应用静态排序。要创建支持动态排序的查询方法,请参阅“ 特殊参数处理 ”。

4.4.3。财产表达

属性表达式只能引用被管实体的直接属性,如前面的示例所示。在创建查询时,您已确保已解析的属性是托管域类的属性。但是,您也可以通过遍历嵌套属性来定义约束。请考虑以下方法签名:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findByAddressZipCode</span><span style="color:#666600">(</span><span style="color:#660066">ZipCode</span><span style="color:#000000"> zipCode</span><span style="color:#666600">);</span></code></span></span>

假设a PersonAddressZipCode。在这种情况下,该方法创建属性遍历x.address.zipCode。解析算法首先将整个part(AddressZipCode)解释为属性,并检查域类中是否具有该名称的属性(未大写)。如果算法成功,则使用该属性。如果没有,算法将来自右侧的驼峰案例部分的源分成头部和尾部,并尝试找到相应的属性 - 在我们的示例中,AddressZipCode。如果算法找到具有该头部的属性,则它采用尾部并继续从那里构建树,以刚才描述的方式将尾部分开。如果第一个分割不匹配,算法会将分割点移动到左侧(AddressZipCode)并继续。

虽然这应该适用于大多数情况,但算法可能会选择错误的属性。假设Person该类也具有addressZip属性。算法将在第一个拆分轮中匹配,选择错误的属性,并失败(因为类型addressZip可能没有code属性)。

要解决此歧义,您可以_在方法名称内部使用手动定义遍历点。所以我们的方法名称如下:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findByAddress_ZipCode</span><span style="color:#666600">(</span><span style="color:#660066">ZipCode</span><span style="color:#000000"> zipCode</span><span style="color:#666600">);</span></code></span></span>

因为我们将下划线字符视为保留字符,所以我们强烈建议遵循标准Java命名约定(即,不在属性名称中使用下划线,而是使用camel case)。

4.4.4。特殊参数处理

要处理查询中的参数,请定义方法参数,如前面示例中所示。除此之外,基础设施承认某些特定的类型,如PageableSort,动态地应用分页和排序,以查询。以下示例演示了这些功能:

示例17.使用Pageable,, SliceSort查询方法

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">Page</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">);</span>

<span style="color:#660066">Slice</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">);</span>

<span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Sort</span><span style="color:#000000"> sort</span><span style="color:#666600">);</span>

<span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">);</span></code></span></span>

第一种方法允许您将org.springframework.data.domain.Pageable实例传递给查询方法,以动态地将分页添加到静态定义的查询中。A Page知道可用元素和页面的总数。它通过基础设施触发计数查询来计算总数来实现。由于这可能很昂贵(取决于所使用的商店),您可以改为返回Slice。A Slice只知道下一个Slice是否可用,这在浏览更大的结果集时可能就足够了。

排序选项也通过Pageable实例处理。如果只需要排序,org.springframework.data.domain.Sort请在方法中添加参数。如您所见,返回a List也是可能的。在这种情况下,Page不会创建构建实际实例所需的其他元数据(这反过来意味着不会发出必要的附加计数查询)。相反,它限制查询仅查找给定范围的实体。

 要了解整个查询的页数,您必须触发额外的计数查询。默认情况下,此查询是从您实际触发的查询派生的。

4.4.5。限制查询结果

查询方法的结果可以通过使用firsttop关键字来限制,这些关键字可以互换使用。可以附加一个可选的数值,top或者first指定要返回的最大结果大小。如果省略该数字,则假定结果大小为1。以下示例显示如何限制查询大小:

示例18.使用Top和限制查询的结果大小First

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">User</span><span style="color:#000000"> findFirstByOrderByLastnameAsc</span><span style="color:#666600">();</span>

<span style="color:#660066">User</span><span style="color:#000000"> findTopByOrderByAgeDesc</span><span style="color:#666600">();</span>

<span style="color:#660066">Page</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> queryFirst10ByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">);</span>

<span style="color:#660066">Slice</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findTop3ByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">);</span>

<span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findFirst10ByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Sort</span><span style="color:#000000"> sort</span><span style="color:#666600">);</span>

<span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findTop10ByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">);</span></code></span></span>

限制表达式也支持Distinct关键字。此外,对于将结果集限制为一个实例的查询,支持将结果包装到Optional关键字中。

如果将分页或切片应用于限制查询分页(以及可用页数的计算),则将其应用于有限结果中。

 通过使用Sort参数将结果与动态排序结合使用,可以表达“K”最小元素和“K”元素的查询方法。

4.4.6。流式查询结果

可以使用Java 8 Stream<T>作为返回类型以递增方式处理查询方法的结果。而不是将查询结果包装在Stream数据存储中特定的方法用于执行流式传输,如以下示例所示:

示例19.使用Java 8流式传输查询结果 Stream<T>

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"select u from User u"</span><span style="color:#666600">)</span>
<span style="color:#660066">Stream</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findAllByCustomQueryAndStream</span><span style="color:#666600">();</span>

<span style="color:#660066">Stream</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> readAllByFirstnameNotNull</span><span style="color:#666600">();</span>

<span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"select u from User u"</span><span style="color:#666600">)</span>
<span style="color:#660066">Stream</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> streamAllPaged</span><span style="color:#666600">(</span><span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">);</span></code></span></span>
 Stream潜在包装底层数据存储专用资源,因此必须在使用之后被关闭。您可以Stream使用该close()方法或使用Java 7 try-with-resources块手动关闭,如以下示例所示:

示例20. Stream<T>在try-with-resources块中使用结果

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">try</span> <span style="color:#666600">(</span><span style="color:#660066">Stream</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> stream </span><span style="color:#666600">=</span><span style="color:#000000"> repository</span><span style="color:#666600">.</span><span style="color:#000000">findAllByCustomQueryAndStream</span><span style="color:#666600">())</span> <span style="color:#666600">{</span><span style="color:#000000">
  stream</span><span style="color:#666600">.</span><span style="color:#000000">forEach</span><span style="color:#666600">(…);</span>
<span style="color:#666600">}</span></code></span></span>
 并非所有Spring Data模块目前都支持Stream<T>返回类型。

4.4.7。异步查询结果

可以使用Spring的异步方法执行功能异步运行存储库查询。这意味着该方法在调用时立即返回,而实际的查询执行发生在已提交给Spring的任务中TaskExecutor。异步查询执行与响应式查询执行不同,不应混合使用。有关反应支持的更多详细信息,请参阅特定于商店的文档。以下示例显示了许多异步查询:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Async</span>
<span style="color:#660066">Future</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByFirstname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">);</span>               

<span style="color:#006666">@Async</span>
<span style="color:#660066">CompletableFuture</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findOneByFirstname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">);</span> 

<span style="color:#006666">@Async</span>
<span style="color:#660066">ListenableFuture</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findOneByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>    </code></span></span>
 使用java.util.concurrent.Future作为返回类型。
 使用Java 8 java.util.concurrent.CompletableFuture作为返回类型。
 使用a org.springframework.util.concurrent.ListenableFuture作为返回类型。

4.5。创建存储库实例

在本节中,您将为定义的存储库接口创建实例和bean定义。一种方法是使用随每个支持存储库机制的Spring Data模块一起提供的Spring命名空间,尽管我们通常建议使用Java配置。

4.5.1。XML配置

每个Spring Data模块都包含一个repositories元素,允许您定义Spring为您扫描的基础包,如以下示例所示:

示例21.通过XML启用Spring Data存储库

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#666600"><?</span><span style="color:#000000">xml version</span><span style="color:#666600">=</span><span style="color:#008800">"1.0"</span><span style="color:#000000"> encoding</span><span style="color:#666600">=</span><span style="color:#008800">"UTF-8"</span><span style="color:#666600">?></span>
<span style="color:#000088"><beans:beans</span> <span style="color:#660066">xmlns:beans</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/beans"</span>
  <span style="color:#660066">xmlns:xsi</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.w3.org/2001/XMLSchema-instance"</span>
  <span style="color:#660066">xmlns</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/data/jpa"</span>
  <span style="color:#660066">xsi:schemaLocation</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"</span><span style="color:#000088">></span>

  <span style="color:#000088"><repositories</span> <span style="color:#660066">base-package</span><span style="color:#666600">=</span><span style="color:#008800">"com.acme.repositories"</span> <span style="color:#000088">/></span>

<span style="color:#000088"></beans:beans></span></code></span></span>

在前面的示例中,指示Spring扫描com.acme.repositories其所有子包以扩展接口Repository或其子接口之一。对于找到的每个接口,基础结构都会注册特定FactoryBean于持久性技术,以创建处理查询方法调用的相应代理。每个bean都是在从接口名称派生的bean名称下注册的,因此UserRepository将在其下注册一个接口userRepository。该base-package属性允许使用通配符,以便您可以定义扫描包的模式。

使用过滤器

默认情况下,基础结构会选择扩展Repository位于已配置的基础包下的特定于持久性技术的子接口的每个接口,并为其创建一个bean实例。但是,您可能希望对哪些接口为其创建bean实例进行更细粒度的控制。要做到这一点,使用<include-filter /><exclude-filter />内部元素<repositories />的元素。语义完全等同于Spring的上下文命名空间中的元素。有关详细信息,请参阅这些元素的Spring参考文档

例如,要将某些接口从实例化中排除为存储库bean,可以使用以下配置:

示例22.使用exclude-filter元素

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><repositories</span> <span style="color:#660066">base-package</span><span style="color:#666600">=</span><span style="color:#008800">"com.acme.repositories"</span><span style="color:#000088">></span>
  <span style="color:#000088"><context:exclude-filter</span> <span style="color:#660066">type</span><span style="color:#666600">=</span><span style="color:#008800">"regex"</span> <span style="color:#660066">expression</span><span style="color:#666600">=</span><span style="color:#008800">".*SomeRepository"</span> <span style="color:#000088">/></span>
<span style="color:#000088"></repositories></span></code></span></span>

前面的示例排除了SomeRepository从实例化结束的所有接口。

4.5.2。JavaConfig

还可以通过@Enable${store}Repositories在JavaConfig类上使用特定于商店的注释来触发存储库基础结构。有关Spring容器的基于Java的配置的介绍,请参阅Spring参考文档中的JavaConfig

启用S​​pring Data存储库的示例配置类似于以下内容:

示例23.基于样本注释的存储库配置

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Configuration</span>
<span style="color:#006666">@EnableJpaRepositories</span><span style="color:#666600">(</span><span style="color:#008800">"com.acme.repositories"</span><span style="color:#666600">)</span>
<span style="color:#000088">class</span> <span style="color:#660066">ApplicationConfiguration</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Bean</span>
  <span style="color:#660066">EntityManagerFactory</span><span style="color:#000000"> entityManagerFactory</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
    <span style="color:#880000">// …</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>
 上面的示例使用JPA特定的注释,您可以根据实际使用的商店模块进行更改。这同样适用于EntityManagerFactorybean的定义。请参阅有关特定于商店的配置的部分。

4.5.3。独立使用

您还可以在Spring容器之外使用存储库基础结构 - 例如,在CDI环境中。您仍然需要在类路径中使用一些Spring库,但通常也可以通过编程方式设置存储库。提供存储库支持的Spring Data模块提供了特定RepositoryFactory于持久性技术的特性,您可以按如下方式使用:

示例24.存储库工厂的独立使用

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">RepositoryFactorySupport</span><span style="color:#000000"> factory </span><span style="color:#666600">=</span> <span style="color:#666600">…</span> <span style="color:#880000">// Instantiate factory here</span>
<span style="color:#660066">UserRepository</span><span style="color:#000000"> repository </span><span style="color:#666600">=</span><span style="color:#000000"> factory</span><span style="color:#666600">.</span><span style="color:#000000">getRepository</span><span style="color:#666600">(</span><span style="color:#660066">UserRepository</span><span style="color:#666600">.</span><span style="color:#000088">class</span><span style="color:#666600">);</span></code></span></span>

4.6。Spring Data Repositories的自定义实现

本节介绍存储库自定义以及片段如何构成复合存储库。

当查询方法需要不同的行为或无法通过查询派生实现时,则需要提供自定义实现。Spring Data存储库允许您提供自定义存储库代码,并将其与通用CRUD抽象和查询方法功能集成。

4.6.1。自定义单个存储库

要使用自定义功能丰富存储库,必须首先定义片段接口和自定义功能的实现,如以下示例所示:

示例25.自定义存储库功能的接口

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">CustomizedUserRepository</span> <span style="color:#666600">{</span>
  <span style="color:#000088">void</span><span style="color:#000000"> someCustomMethod</span><span style="color:#666600">(</span><span style="color:#660066">User</span><span style="color:#000000"> user</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

然后,您可以让存储库接口从片段接口进一步扩展,如以下示例所示:

示例26.自定义存储库功能的实现

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">CustomizedUserRepositoryImpl</span> <span style="color:#000088">implements</span> <span style="color:#660066">CustomizedUserRepository</span> <span style="color:#666600">{</span>

  <span style="color:#000088">public</span> <span style="color:#000088">void</span><span style="color:#000000"> someCustomMethod</span><span style="color:#666600">(</span><span style="color:#660066">User</span><span style="color:#000000"> user</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#880000">// Your custom implementation</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>
 对应于片段接口的类名最重要的部分是Impl后缀。

实现本身不依赖于Spring Data,可以是常规的Spring bean。因此,您可以使用标准依赖项注入行为将引用注入其他bean(例如a JdbcTemplate),参与方面等。

您可以让存储库接口扩展片段接口,如以下示例所示:

示例27.对存储库界面的更改

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">>,</span> <span style="color:#660066">CustomizedUserRepository</span> <span style="color:#666600">{</span>

  <span style="color:#880000">// Declare query methods here</span>
<span style="color:#666600">}</span></code></span></span>

使用存储库接口扩展片段接口可以组合CRUD和自定义功能,并使其可供客户端使用。

Spring Data存储库通过使用形成存储库组合的片段来实现。片段是基本存储库,功能方面(如QueryDsl),自定义接口及其实现。每次向存储库界面添加接口时,都可以通过添加片段来增强组合。每个Spring Data模块都提供了基本存储库和存储库方面的实现。

以下示例显示了自定义接口及其实现:

示例28.具有其实现的片段

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">HumanRepository</span> <span style="color:#666600">{</span>
  <span style="color:#000088">void</span><span style="color:#000000"> someHumanMethod</span><span style="color:#666600">(</span><span style="color:#660066">User</span><span style="color:#000000"> user</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span>

<span style="color:#000088">class</span> <span style="color:#660066">HumanRepositoryImpl</span> <span style="color:#000088">implements</span> <span style="color:#660066">HumanRepository</span> <span style="color:#666600">{</span>

  <span style="color:#000088">public</span> <span style="color:#000088">void</span><span style="color:#000000"> someHumanMethod</span><span style="color:#666600">(</span><span style="color:#660066">User</span><span style="color:#000000"> user</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#880000">// Your custom implementation</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span>

<span style="color:#000088">interface</span> <span style="color:#660066">ContactRepository</span> <span style="color:#666600">{</span>

  <span style="color:#000088">void</span><span style="color:#000000"> someContactMethod</span><span style="color:#666600">(</span><span style="color:#660066">User</span><span style="color:#000000"> user</span><span style="color:#666600">);</span>

  <span style="color:#660066">User</span><span style="color:#000000"> anotherContactMethod</span><span style="color:#666600">(</span><span style="color:#660066">User</span><span style="color:#000000"> user</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span>

<span style="color:#000088">class</span> <span style="color:#660066">ContactRepositoryImpl</span> <span style="color:#000088">implements</span> <span style="color:#660066">ContactRepository</span> <span style="color:#666600">{</span>

  <span style="color:#000088">public</span> <span style="color:#000088">void</span><span style="color:#000000"> someContactMethod</span><span style="color:#666600">(</span><span style="color:#660066">User</span><span style="color:#000000"> user</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#880000">// Your custom implementation</span>
  <span style="color:#666600">}</span>

  <span style="color:#000088">public</span> <span style="color:#660066">User</span><span style="color:#000000"> anotherContactMethod</span><span style="color:#666600">(</span><span style="color:#660066">User</span><span style="color:#000000"> user</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#880000">// Your custom implementation</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

以下示例显示了扩展的自定义存储库的接口CrudRepository

示例29.对存储库界面的更改

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">>,</span> <span style="color:#660066">HumanRepository</span><span style="color:#666600">,</span> <span style="color:#660066">ContactRepository</span> <span style="color:#666600">{</span>

  <span style="color:#880000">// Declare query methods here</span>
<span style="color:#666600">}</span></code></span></span>

存储库可以由多个自定义实现组成,这些实现按其声明的顺序导入。自定义实现的优先级高于基本实现和存储库方面。如果两个片段提供相同的方法签名,则此排序允许您覆盖基本存储库和方面方法并解决歧义。存储库片段不限于在单个存储库接口中使用。多个存储库可以使用片段接口,允许您在不同的存储库中重用自定义。

以下示例显示了存储库片段及其实现:

示例30.碎片覆盖 save(…)

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">CustomizedSave</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
  <span style="color:#666600"><</span><span style="color:#000000">S </span><span style="color:#000088">extends</span><span style="color:#000000"> T</span><span style="color:#666600">></span><span style="color:#000000"> S save</span><span style="color:#666600">(</span><span style="color:#000000">S entity</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span>

<span style="color:#000088">class</span> <span style="color:#660066">CustomizedSaveImpl</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span> <span style="color:#000088">implements</span> <span style="color:#660066">CustomizedSave</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#000088">public</span> <span style="color:#666600"><</span><span style="color:#000000">S </span><span style="color:#000088">extends</span><span style="color:#000000"> T</span><span style="color:#666600">></span><span style="color:#000000"> S save</span><span style="color:#666600">(</span><span style="color:#000000">S entity</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#880000">// Your custom implementation</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

以下示例显示了使用前面的存储库片段的存储库:

示例31.定制的存储库接口

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">>,</span> <span style="color:#660066">CustomizedSave</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
<span style="color:#666600">}</span>

<span style="color:#000088">interface</span> <span style="color:#660066">PersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">>,</span> <span style="color:#660066">CustomizedSave</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
<span style="color:#666600">}</span></code></span></span>

组态

如果使用命名空间配置,则存储库基础结构会尝试通过扫描其找到存储库的包下面的类来自动检测自定义实现片段。这些类需要遵循将命名空间元素的repository-impl-postfix属性附加到片段接口名称的命名约定。此后缀默认为Impl。以下示例显示了使用默认后缀的存储库以及为后缀设置自定义值的存储库:

示例32.配置示例

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><repositories</span> <span style="color:#660066">base-package</span><span style="color:#666600">=</span><span style="color:#008800">"com.acme.repository"</span> <span style="color:#000088">/></span>

<span style="color:#000088"><repositories</span> <span style="color:#660066">base-package</span><span style="color:#666600">=</span><span style="color:#008800">"com.acme.repository"</span> <span style="color:#660066">repository-impl-postfix</span><span style="color:#666600">=</span><span style="color:#008800">"MyPostfix"</span> <span style="color:#000088">/></span></code></span></span>

上一个示例中的第一个配置尝试查找一个被调用com.acme.repository.CustomizedUserRepositoryImpl以充当自定义存储库实现的类。第二个示例尝试查找com.acme.repository.CustomizedUserRepositoryMyPostfix

解决歧义

如果在不同的包中找到具有匹配类名的多个实现,则Spring Data使用bean名来标识要使用的bean。

给定CustomizedUserRepository前面所示的以下两个自定义实现,使用第一个实现。它的bean名称是customizedUserRepositoryImpl,它与片段interface(CustomizedUserRepository)加上后缀相匹配Impl

示例33.无关紧要的实现的解决方案

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">package</span><span style="color:#000000"> com</span><span style="color:#666600">.</span><span style="color:#000000">acme</span><span style="color:#666600">.</span><span style="color:#000000">impl</span><span style="color:#666600">.</span><span style="color:#000000">one</span><span style="color:#666600">;</span>

<span style="color:#000088">class</span> <span style="color:#660066">CustomizedUserRepositoryImpl</span> <span style="color:#000088">implements</span> <span style="color:#660066">CustomizedUserRepository</span> <span style="color:#666600">{</span>

  <span style="color:#880000">// Your custom implementation</span>
<span style="color:#666600">}</span></code></span></span>
<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">package</span><span style="color:#000000"> com</span><span style="color:#666600">.</span><span style="color:#000000">acme</span><span style="color:#666600">.</span><span style="color:#000000">impl</span><span style="color:#666600">.</span><span style="color:#000000">two</span><span style="color:#666600">;</span>

<span style="color:#006666">@Component</span><span style="color:#666600">(</span><span style="color:#008800">"specialCustomImpl"</span><span style="color:#666600">)</span>
<span style="color:#000088">class</span> <span style="color:#660066">CustomizedUserRepositoryImpl</span> <span style="color:#000088">implements</span> <span style="color:#660066">CustomizedUserRepository</span> <span style="color:#666600">{</span>

  <span style="color:#880000">// Your custom implementation</span>
<span style="color:#666600">}</span></code></span></span>

如果使用bean 注释UserRepository接口@Component("specialCustom"),则bean名称加上Impl匹配为存储库实现定义的名称com.acme.impl.two,并使用它而不是第一个。

手动接线

如果您的自定义实现仅使用基于注释的配置和自动装配,则前面显示的方法效果很好,因为它被视为任何其他Spring bean。如果您的实现片段bean需要特殊连接,您可以声明bean并根据前一节中描述的约定对其进行命名。然后,基础结构按名称引用手动定义的bean定义,而不是自己创建一个。以下示例显示如何手动连接自定义实现:

示例34.定制实现的手动布线

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><repositories</span> <span style="color:#660066">base-package</span><span style="color:#666600">=</span><span style="color:#008800">"com.acme.repository"</span> <span style="color:#000088">/></span>

<span style="color:#000088"><beans:bean</span> <span style="color:#660066">id</span><span style="color:#666600">=</span><span style="color:#008800">"userRepositoryImpl"</span> <span style="color:#660066">class</span><span style="color:#666600">=</span><span style="color:#008800">"…"</span><span style="color:#000088">></span>
  <span style="color:#880000"><!-- further configuration --></span>
<span style="color:#000088"></beans:bean></span></code></span></span>

4.6.2。自定义Base Repository

当您要自定义基本存储库行为以便所有存储库都受到影响时,上一节中描述的方法需要自定义每个存储库接口。要改为更改所有存储库的行为,可以创建一个扩展特定于持久性技术的存储库基类的实现。然后,此类充当存储库代理的自定义基类,如以下示例所示:

示例35.自定义存储库基类

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">MyRepositoryImpl</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID </span><span style="color:#000088">extends</span> <span style="color:#660066">Serializable</span><span style="color:#666600">></span>
  <span style="color:#000088">extends</span> <span style="color:#660066">SimpleJpaRepository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span><span style="color:#000000"> ID</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#000088">private</span> <span style="color:#000088">final</span> <span style="color:#660066">EntityManager</span><span style="color:#000000"> entityManager</span><span style="color:#666600">;</span>

  <span style="color:#660066">MyRepositoryImpl</span><span style="color:#666600">(</span><span style="color:#660066">JpaEntityInformation</span><span style="color:#000000"> entityInformation</span><span style="color:#666600">,</span>
                          <span style="color:#660066">EntityManager</span><span style="color:#000000"> entityManager</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#000088">super</span><span style="color:#666600">(</span><span style="color:#000000">entityInformation</span><span style="color:#666600">,</span><span style="color:#000000"> entityManager</span><span style="color:#666600">);</span>

    <span style="color:#880000">// Keep the EntityManager around to used from the newly introduced methods.</span>
    <span style="color:#000088">this</span><span style="color:#666600">.</span><span style="color:#000000">entityManager </span><span style="color:#666600">=</span><span style="color:#000000"> entityManager</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>

  <span style="color:#006666">@Transactional</span>
  <span style="color:#000088">public</span> <span style="color:#666600"><</span><span style="color:#000000">S </span><span style="color:#000088">extends</span><span style="color:#000000"> T</span><span style="color:#666600">></span><span style="color:#000000"> S save</span><span style="color:#666600">(</span><span style="color:#000000">S entity</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#880000">// implementation goes here</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>
 该类需要具有特定于商店的存储库工厂实现所使用的超类的构造函数。如果存储库基类具有多个构造函数,则覆盖使用EntityInformation加号存储特定基础结构对象(例如,EntityManager模板类)的构造函数。

最后一步是使Spring Data Infrastructure了解自定义存储库基类。在Java配置中,您可以使用注释的repositoryBaseClass属性来执行此操作@Enable${store}Repositories,如以下示例所示:

示例36.使用JavaConfig配置自定义存储库基类

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Configuration</span>
<span style="color:#006666">@EnableJpaRepositories</span><span style="color:#666600">(</span><span style="color:#000000">repositoryBaseClass </span><span style="color:#666600">=</span> <span style="color:#660066">MyRepositoryImpl</span><span style="color:#666600">.</span><span style="color:#000088">class</span><span style="color:#666600">)</span>
<span style="color:#000088">class</span> <span style="color:#660066">ApplicationConfiguration</span> <span style="color:#666600">{</span> <span style="color:#666600">…</span> <span style="color:#666600">}</span></code></span></span>

XML命名空间中提供了相应的属性,如以下示例所示:

示例37.使用XML配置自定义存储库基类

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><repositories</span> <span style="color:#660066">base-package</span><span style="color:#666600">=</span><span style="color:#008800">"com.acme.repository"</span>
     <span style="color:#660066">base-class</span><span style="color:#666600">=</span><span style="color:#008800">"….MyRepositoryImpl"</span> <span style="color:#000088">/></span></code></span></span>

4.7。从聚合根发布事件

由存储库管理的实体是聚合根。在域驱动设计应用程序中,这些聚合根通常会发布域事件。Spring Data提供了一个注释@DomainEvents,可以在聚合根的方法上使用,以使该发布尽可能简单,如以下示例所示:

示例38.从聚合根公开事件域事件

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">AnAggregateRoot</span> <span style="color:#666600">{</span>

    <span style="color:#006666">@DomainEvents</span> 
    <span style="color:#660066">Collection</span><span style="color:#666600"><</span><span style="color:#660066">Object</span><span style="color:#666600">></span><span style="color:#000000"> domainEvents</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
        <span style="color:#880000">// … return events you want to get published here</span>
    <span style="color:#666600">}</span>

    <span style="color:#006666">@AfterDomainEventPublication</span> 
    <span style="color:#000088">void</span><span style="color:#000000"> callbackMethod</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
       <span style="color:#880000">// … potentially clean up domain events list</span>
    <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>
 使用的方法@DomainEvents可以返回单个事件实例或事件集合。它不能采取任何论点。
 在所有事件发布后,我们有一个注释的方法@AfterDomainEventPublication。它可用于潜在地清除要发布的事件列表(以及其他用途)。

每次调用Spring Data存储库的save(…)方法时,都会调用这些方法。

4.8。Spring Data Extensions

本节介绍了一组Spring Data扩展,它们可以在各种上下文中使用Spring Data。目前,大多数集成都针对Spring MVC。

4.8.1。Querydsl扩展

Querydsl是一个框架,可以通过其流畅的API构建静态类型的SQL类查询。

几个Spring Data模块提供与Querydsl的集成QuerydslPredicateExecutor,如以下示例所示:

例39. QuerydslPredicateExecutor接口

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">QuerydslPredicateExecutor</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#660066">Optional</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> findById</span><span style="color:#666600">(</span><span style="color:#660066">Predicate</span><span style="color:#000000"> predicate</span><span style="color:#666600">);</span>  

  <span style="color:#660066">Iterable</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> findAll</span><span style="color:#666600">(</span><span style="color:#660066">Predicate</span><span style="color:#000000"> predicate</span><span style="color:#666600">);</span>   

  <span style="color:#000088">long</span><span style="color:#000000"> count</span><span style="color:#666600">(</span><span style="color:#660066">Predicate</span><span style="color:#000000"> predicate</span><span style="color:#666600">);</span>            

  <span style="color:#000088">boolean</span><span style="color:#000000"> exists</span><span style="color:#666600">(</span><span style="color:#660066">Predicate</span><span style="color:#000000"> predicate</span><span style="color:#666600">);</span>        

  <span style="color:#880000">// … more functionality omitted.</span>
<span style="color:#666600">}</span></code></span></span>
 查找并返回与之匹配的单个实体Predicate
 查找并返回与之匹配的所有实体Predicate
 返回匹配的实体数Predicate
 返回与Predicateexists 匹配的实体。

要使用Querydsl支持,请QuerydslPredicateExecutor在存储库界面上进行扩展,如以下示例所示

示例40.对存储库的Querydsl集成

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">>,</span> <span style="color:#660066">QuerydslPredicateExecutor</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
<span style="color:#666600">}</span></code></span></span>

前面的示例允许您使用Querydsl Predicate实例编写类型安全查询,如以下示例所示:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">Predicate</span><span style="color:#000000"> predicate </span><span style="color:#666600">=</span><span style="color:#000000"> user</span><span style="color:#666600">.</span><span style="color:#000000">firstname</span><span style="color:#666600">.</span><span style="color:#000000">equalsIgnoreCase</span><span style="color:#666600">(</span><span style="color:#008800">"dave"</span><span style="color:#666600">)</span>
	<span style="color:#666600">.</span><span style="color:#000000">and</span><span style="color:#666600">(</span><span style="color:#000000">user</span><span style="color:#666600">.</span><span style="color:#000000">lastname</span><span style="color:#666600">.</span><span style="color:#000000">startsWithIgnoreCase</span><span style="color:#666600">(</span><span style="color:#008800">"mathews"</span><span style="color:#666600">));</span><span style="color:#000000">

userRepository</span><span style="color:#666600">.</span><span style="color:#000000">findAll</span><span style="color:#666600">(</span><span style="color:#000000">predicate</span><span style="color:#666600">);</span></code></span></span>

4.8.2。网络支持

 本节包含Spring Data Web支持的文档,因为它在Spring Data Commons的当前(及更高版本)版本中实现。由于新引入的支持更改了许多内容,因此我们在[web.legacy]中保留了以前行为的文档。

支持存储库编程模型的Spring Data模块具有各种Web支持。与Web相关的组件需要Spring MVC JAR位于类路径上。其中一些甚至提供与Spring HATEOAS的集成。通常,通过@EnableSpringDataWebSupport在JavaConfig配置类中使用注释来启用集成支持,如以下示例所示:

示例41.启用Spring Data Web支持

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Configuration</span>
<span style="color:#006666">@EnableWebMvc</span>
<span style="color:#006666">@EnableSpringDataWebSupport</span>
<span style="color:#000088">class</span> <span style="color:#660066">WebConfiguration</span> <span style="color:#666600">{}</span></code></span></span>

@EnableSpringDataWebSupport批注注册几个组件,我们将在一个位讨论。它还将检测类路径上的Spring HATEOAS,并为它注册集成组件(如果存在)。

或者,如果您使用XML配置,请注册SpringDataWebConfigurationHateoasAwareSpringDataWebConfiguration作为Spring bean 注册,如以下示例所示(for SpringDataWebConfiguration):

示例42.在XML中启用Spring Data Web支持

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><bean</span> <span style="color:#660066">class</span><span style="color:#666600">=</span><span style="color:#008800">"org.springframework.data.web.config.SpringDataWebConfiguration"</span> <span style="color:#000088">/></span>

<span style="color:#880000"><!-- If you use Spring HATEOAS, register this one *instead* of the former --></span>
<span style="color:#000088"><bean</span> <span style="color:#660066">class</span><span style="color:#666600">=</span><span style="color:#008800">"org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration"</span> <span style="color:#000088">/></span></code></span></span>

基本Web支持

上一节中显示的配置注册了一些基本组件:

DomainClassConverter

DomainClassConverter让你在Spring MVC中的控制器方法签名使用域类型直接,让你不用通过储存手动查找的情况下,如在下面的例子:

示例43.在方法签名中使用域类型的Spring MVC控制器

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Controller</span>
<span style="color:#006666">@RequestMapping</span><span style="color:#666600">(</span><span style="color:#008800">"/users"</span><span style="color:#666600">)</span>
<span style="color:#000088">class</span> <span style="color:#660066">UserController</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@RequestMapping</span><span style="color:#666600">(</span><span style="color:#008800">"/{id}"</span><span style="color:#666600">)</span>
  <span style="color:#660066">String</span><span style="color:#000000"> showUserForm</span><span style="color:#666600">(</span><span style="color:#006666">@PathVariable</span><span style="color:#666600">(</span><span style="color:#008800">"id"</span><span style="color:#666600">)</span> <span style="color:#660066">User</span><span style="color:#000000"> user</span><span style="color:#666600">,</span> <span style="color:#660066">Model</span><span style="color:#000000"> model</span><span style="color:#666600">)</span> <span style="color:#666600">{</span><span style="color:#000000">

    model</span><span style="color:#666600">.</span><span style="color:#000000">addAttribute</span><span style="color:#666600">(</span><span style="color:#008800">"user"</span><span style="color:#666600">,</span><span style="color:#000000"> user</span><span style="color:#666600">);</span>
    <span style="color:#000088">return</span> <span style="color:#008800">"userForm"</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

如您所见,该方法User直接接收实例,无需进一步查找。可以通过让Spring MVC首先将路径变量转换为id域类的类型来解析实例,并最终通过调用findById(…)为域类型注册的存储库实例来访问实例。

 目前,存储库必须实现CrudRepository才有资格被发现进行转换。

HandlerMethodArgumentResolvers for Pageable和Sort

上一节中显示的配置片段还注册了PageableHandlerMethodArgumentResolver一个实例以及一个实例SortHandlerMethodArgumentResolver。注册启用PageableSort作为有效的控制器方法参数,如以下示例所示:

例44.使用Pageable作为控制器方法参数

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Controller</span>
<span style="color:#006666">@RequestMapping</span><span style="color:#666600">(</span><span style="color:#008800">"/users"</span><span style="color:#666600">)</span>
<span style="color:#000088">class</span> <span style="color:#660066">UserController</span> <span style="color:#666600">{</span>

  <span style="color:#000088">private</span> <span style="color:#000088">final</span> <span style="color:#660066">UserRepository</span><span style="color:#000000"> repository</span><span style="color:#666600">;</span>

  <span style="color:#660066">UserController</span><span style="color:#666600">(</span><span style="color:#660066">UserRepository</span><span style="color:#000000"> repository</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#000088">this</span><span style="color:#666600">.</span><span style="color:#000000">repository </span><span style="color:#666600">=</span><span style="color:#000000"> repository</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>

  <span style="color:#006666">@RequestMapping</span>
  <span style="color:#660066">String</span><span style="color:#000000"> showUsers</span><span style="color:#666600">(</span><span style="color:#660066">Model</span><span style="color:#000000"> model</span><span style="color:#666600">,</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">)</span> <span style="color:#666600">{</span><span style="color:#000000">

    model</span><span style="color:#666600">.</span><span style="color:#000000">addAttribute</span><span style="color:#666600">(</span><span style="color:#008800">"users"</span><span style="color:#666600">,</span><span style="color:#000000"> repository</span><span style="color:#666600">.</span><span style="color:#000000">findAll</span><span style="color:#666600">(</span><span style="color:#000000">pageable</span><span style="color:#666600">));</span>
    <span style="color:#000088">return</span> <span style="color:#008800">"users"</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

前面的方法签名导致Spring MVC尝试Pageable使用以下默认配置从请求参数派生实例:

表1.为Pageable实例评估的请求参数

page

您要检索的页面。0索引并默认为0。

size

要检索的页面大小。默认为20。

sort

应按格式排序的属性property,property(,ASC|DESC)。默认排序方向是升序。sort如果要切换方向,请使用多个参数 - 例如,?sort=firstname&sort=lastname,asc

要自定义此行为,请分别注册实现PageableHandlerMethodArgumentResolverCustomizer接口或SortHandlerMethodArgumentResolverCustomizer接口的bean 。customize()调用其方法,让您更改设置,如以下示例所示:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Bean</span> <span style="color:#660066">SortHandlerMethodArgumentResolverCustomizer</span><span style="color:#000000"> sortCustomizer</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
    <span style="color:#000088">return</span><span style="color:#000000"> s </span><span style="color:#666600">-></span><span style="color:#000000"> s</span><span style="color:#666600">.</span><span style="color:#000000">setPropertyDelimiter</span><span style="color:#666600">(</span><span style="color:#008800">"<-->"</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

如果设置现有属性MethodArgumentResolver不足以满足您的需要,请扩展其中一个SpringDataWebConfiguration或启用HATEOAS的等效项,覆盖pageableResolver()sortResolver()方法,然后导入自定义配置文件而不是使用@Enable注释。

如果您需要从请求中解析多个PageableSort实例(例如,对于多个表),则可以使用Spring的@Qualifier注释来区分彼此。然后请求参数必须以前缀为前缀${qualifier}_。以下示例显示了生成的方法签名:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">String</span><span style="color:#000000"> showUsers</span><span style="color:#666600">(</span><span style="color:#660066">Model</span><span style="color:#000000"> model</span><span style="color:#666600">,</span>
      <span style="color:#006666">@Qualifier</span><span style="color:#666600">(</span><span style="color:#008800">"thing1"</span><span style="color:#666600">)</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> first</span><span style="color:#666600">,</span>
      <span style="color:#006666">@Qualifier</span><span style="color:#666600">(</span><span style="color:#008800">"thing2"</span><span style="color:#666600">)</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> second</span><span style="color:#666600">)</span> <span style="color:#666600">{</span> <span style="color:#666600">…</span> <span style="color:#666600">}</span></code></span></span>

你有填充thing1_pagething2_page等。

Pageable传递给方法的默认值相当于a,new PageRequest(0, 20)但可以使用参数@PageableDefault上的注释进行自定义Pageable

对Pageables的超媒体支持

Spring HATEOAS附带了一个表示模型类(PagedResources),它允许Page使用必要的Page元数据丰富实例的内容,以及让客户端轻松浏览页面的链接。将Page转换为a PagedResources是通过Spring HATEOAS ResourceAssembler接口的实现来完成的,称为PagedResourcesAssembler。以下示例显示如何使用a PagedResourcesAssembler作为控制器方法参数:

示例45.使用PagedResourcesAssembler作为控制器方法参数

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Controller</span>
<span style="color:#000088">class</span> <span style="color:#660066">PersonController</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Autowired</span> <span style="color:#660066">PersonRepository</span><span style="color:#000000"> repository</span><span style="color:#666600">;</span>

  <span style="color:#006666">@RequestMapping</span><span style="color:#666600">(</span><span style="color:#000000">value </span><span style="color:#666600">=</span> <span style="color:#008800">"/persons"</span><span style="color:#666600">,</span><span style="color:#000000"> method </span><span style="color:#666600">=</span> <span style="color:#660066">RequestMethod</span><span style="color:#666600">.</span><span style="color:#000000">GET</span><span style="color:#666600">)</span>
  <span style="color:#660066">HttpEntity</span><span style="color:#666600"><</span><span style="color:#660066">PagedResources</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">>></span><span style="color:#000000"> persons</span><span style="color:#666600">(</span><span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">,</span>
    <span style="color:#660066">PagedResourcesAssembler</span><span style="color:#000000"> assembler</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>

    <span style="color:#660066">Page</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> persons </span><span style="color:#666600">=</span><span style="color:#000000"> repository</span><span style="color:#666600">.</span><span style="color:#000000">findAll</span><span style="color:#666600">(</span><span style="color:#000000">pageable</span><span style="color:#666600">);</span>
    <span style="color:#000088">return</span> <span style="color:#000088">new</span> <span style="color:#660066">ResponseEntity</span><span style="color:#666600"><>(</span><span style="color:#000000">assembler</span><span style="color:#666600">.</span><span style="color:#000000">toResources</span><span style="color:#666600">(</span><span style="color:#000000">persons</span><span style="color:#666600">),</span> <span style="color:#660066">HttpStatus</span><span style="color:#666600">.</span><span style="color:#000000">OK</span><span style="color:#666600">);</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

如上例所示启用配置可以将PagedResourcesAssembler其用作控制器方法参数。调用toResources(…)它具有以下效果:

  • 内容Page成为PagedResources实例的内容。

  • PagedResources对象PageMetadata附加一个实例,并使用来自Page和底层的信息填充PageRequest

  • PagedResources可能会prevnext连接链路,根据页面的状态。链接指向方法映射到的URI。添加到方法的分页参数与设置相匹配,PageableHandlerMethodArgumentResolver以确保稍后可以解析链接。

假设我们在数据库中有30个Person实例。您现在可以触发request()并查看类似于以下内容的输出:GEThttp://localhost:8080/persons

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-javascript"><span style="color:#666600">{</span> <span style="color:#008800">"links"</span> <span style="color:#666600">:</span> <span style="color:#666600">[</span> <span style="color:#666600">{</span> <span style="color:#008800">"rel"</span> <span style="color:#666600">:</span> <span style="color:#008800">"next"</span><span style="color:#666600">,</span>
                <span style="color:#008800">"href"</span> <span style="color:#666600">:</span> <span style="color:#008800">"</span><span style="color:#000000">http</span><span style="color:#666600">:</span><span style="color:#880000">//localhost:8080/persons?page=1&size=20 }</span>
  <span style="color:#666600">],</span>
  <span style="color:#008800">"content"</span> <span style="color:#666600">:</span> <span style="color:#666600">[</span>
     <span style="color:#666600">…</span> <span style="color:#880000">// 20 Person instances rendered here</span>
  <span style="color:#666600">],</span>
  <span style="color:#008800">"pageMetadata"</span> <span style="color:#666600">:</span> <span style="color:#666600">{</span>
    <span style="color:#008800">"size"</span> <span style="color:#666600">:</span> <span style="color:#006666">20</span><span style="color:#666600">,</span>
    <span style="color:#008800">"totalElements"</span> <span style="color:#666600">:</span> <span style="color:#006666">30</span><span style="color:#666600">,</span>
    <span style="color:#008800">"totalPages"</span> <span style="color:#666600">:</span> <span style="color:#006666">2</span><span style="color:#666600">,</span>
    <span style="color:#008800">"number"</span> <span style="color:#666600">:</span> <span style="color:#006666">0</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

您会看到汇编程序生成了正确的URI,并且还选择了默认配置来将参数解析Pageable为即将发出的请求。这意味着,如果更改该配置,链接将自动遵循更改。默认情况下,汇编程序指向它所调用的控制器方法,但是可以通过将自定义Link作为基础来定制以构建分页链接,从而使PagedResourcesAssembler.toResource(…)方法重载。

Web数据绑定支持

Spring数据投影(在Projections中描述)可用于通过使用JSONPath表达式来绑定传入的请求有效负载(需要Jayway JsonPathXPath表达式(需要XmlBeam),如以下示例所示:

示例46.使用JSONPath或XPath表达式的HTTP有效负载绑定

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@ProjectedPayload</span>
<span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserPayload</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@XBRead</span><span style="color:#666600">(</span><span style="color:#008800">"//firstname"</span><span style="color:#666600">)</span>
  <span style="color:#006666">@JsonPath</span><span style="color:#666600">(</span><span style="color:#008800">"$..firstname"</span><span style="color:#666600">)</span>
  <span style="color:#660066">String</span><span style="color:#000000"> getFirstname</span><span style="color:#666600">();</span>

  <span style="color:#006666">@XBRead</span><span style="color:#666600">(</span><span style="color:#008800">"/lastname"</span><span style="color:#666600">)</span>
  <span style="color:#006666">@JsonPath</span><span style="color:#666600">({</span> <span style="color:#008800">"$.lastname"</span><span style="color:#666600">,</span> <span style="color:#008800">"$.user.lastname"</span> <span style="color:#666600">})</span>
  <span style="color:#660066">String</span><span style="color:#000000"> getLastname</span><span style="color:#666600">();</span>
<span style="color:#666600">}</span></code></span></span>

前面示例中显示的类型可以用作Spring MVC处理程序方法参数,也可以使用ParameterizedTypeReference其中一个RestTemplate方法。前面的方法声明将尝试查找firstname给定文档中的任何位置。该lastnameXML查询是对输入文档的顶层进行。JSON变体lastname首先尝试顶级,但如果前者没有返回值,则尝试lastname嵌套在user子文档中。这样,可以轻松地减轻源文档结构的变化,而无需客户端调用公开的方法(通常是基于类的有效负载绑定的缺点)。

如预测中所述,支持嵌套投影。如果该方法返回复杂的非接口类型,ObjectMapper则使用Jackson 来映射最终值。

对于Spring MVC,必要的转换器会@EnableSpringDataWebSupport在活动时自动注册,并且类路径上可以使用所需的依赖项。使用时RestTemplate,注册ProjectingJackson2HttpMessageConverter(JSON)或XmlBeamHttpMessageConverter手动。

有关更多信息,请参阅规范Spring Data Examples存储库中Web投影示例

Querydsl Web支持

对于那些具有QueryDSL集成的商店,可以从Request查询字符串中包含的属性派生查询。

请考虑以下查询字符串:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-text"><span style="color:#666600">?</span><span style="color:#000000">firstname</span><span style="color:#666600">=</span><span style="color:#660066">Dave</span><span style="color:#666600">&</span><span style="color:#000000">lastname</span><span style="color:#666600">=</span><span style="color:#660066">Matthews</span></code></span></span>

给定User前面示例中的对象,可以使用以下方法将查询字符串解析为以下值QuerydslPredicateArgumentResolver

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-text"><span style="color:#660066">QUser</span><span style="color:#666600">.</span><span style="color:#000000">user</span><span style="color:#666600">.</span><span style="color:#000000">firstname</span><span style="color:#666600">.</span><span style="color:#000000">eq</span><span style="color:#666600">(</span><span style="color:#008800">"Dave"</span><span style="color:#666600">).</span><span style="color:#000088">and</span><span style="color:#666600">(</span><span style="color:#660066">QUser</span><span style="color:#666600">.</span><span style="color:#000000">user</span><span style="color:#666600">.</span><span style="color:#000000">lastname</span><span style="color:#666600">.</span><span style="color:#000000">eq</span><span style="color:#666600">(</span><span style="color:#008800">"Matthews"</span><span style="color:#666600">))</span></code></span></span>
 @EnableSpringDataWebSupport当在类路径中找到Querydsl时 ,该功能会自动启用。

添加@QuerydslPredicate到方法签名提供了一个即用型Predicate,可以使用该方法运行QuerydslPredicateExecutor

 通常从方法的返回类型中解析类型信息。由于该信息不一定与域类型匹配,因此使用该root属性可能是个好主意QuerydslPredicate

以下示例显示了如何@QuerydslPredicate在方法签名中使用:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Controller</span>
<span style="color:#000088">class</span> <span style="color:#660066">UserController</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Autowired</span> <span style="color:#660066">UserRepository</span><span style="color:#000000"> repository</span><span style="color:#666600">;</span>

  <span style="color:#006666">@RequestMapping</span><span style="color:#666600">(</span><span style="color:#000000">value </span><span style="color:#666600">=</span> <span style="color:#008800">"/"</span><span style="color:#666600">,</span><span style="color:#000000"> method </span><span style="color:#666600">=</span> <span style="color:#660066">RequestMethod</span><span style="color:#666600">.</span><span style="color:#000000">GET</span><span style="color:#666600">)</span>
  <span style="color:#660066">String</span><span style="color:#000000"> index</span><span style="color:#666600">(</span><span style="color:#660066">Model</span><span style="color:#000000"> model</span><span style="color:#666600">,</span> <span style="color:#006666">@QuerydslPredicate</span><span style="color:#666600">(</span><span style="color:#000000">root </span><span style="color:#666600">=</span> <span style="color:#660066">User</span><span style="color:#666600">.</span><span style="color:#000088">class</span><span style="color:#666600">)</span> <span style="color:#660066">Predicate</span><span style="color:#000000"> predicate</span><span style="color:#666600">,</span>    
          <span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">,</span> <span style="color:#006666">@RequestParam</span> <span style="color:#660066">MultiValueMap</span><span style="color:#666600"><</span><span style="color:#660066">String</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#666600">></span><span style="color:#000000"> parameters</span><span style="color:#666600">)</span> <span style="color:#666600">{</span><span style="color:#000000">

    model</span><span style="color:#666600">.</span><span style="color:#000000">addAttribute</span><span style="color:#666600">(</span><span style="color:#008800">"users"</span><span style="color:#666600">,</span><span style="color:#000000"> repository</span><span style="color:#666600">.</span><span style="color:#000000">findAll</span><span style="color:#666600">(</span><span style="color:#000000">predicate</span><span style="color:#666600">,</span><span style="color:#000000"> pageable</span><span style="color:#666600">));</span>

    <span style="color:#000088">return</span> <span style="color:#008800">"index"</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>
 将查询字符串参数解析为匹配Predicatefor User

默认绑定如下:

  • Object在简单的属性上eq

  • Object像集合一样的属性contains

  • Collection在简单的属性上in

这些绑定可以通过Java 8 的bindings属性@QuerydslPredicate或通过使用Java 8 default methods并将该QuerydslBinderCustomizer方法添加到存储库接口来定制。

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#666600">>,</span>
                                 <span style="color:#660066">QuerydslPredicateExecutor</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">>,</span>                
                                 <span style="color:#660066">QuerydslBinderCustomizer</span><span style="color:#666600"><</span><span style="color:#660066">QUser</span><span style="color:#666600">></span> <span style="color:#666600">{</span>               

  <span style="color:#006666">@Override</span>
  <span style="color:#000088">default</span> <span style="color:#000088">void</span><span style="color:#000000"> customize</span><span style="color:#666600">(</span><span style="color:#660066">QuerydslBindings</span><span style="color:#000000"> bindings</span><span style="color:#666600">,</span> <span style="color:#660066">QUser</span><span style="color:#000000"> user</span><span style="color:#666600">)</span> <span style="color:#666600">{</span><span style="color:#000000">

    bindings</span><span style="color:#666600">.</span><span style="color:#000000">bind</span><span style="color:#666600">(</span><span style="color:#000000">user</span><span style="color:#666600">.</span><span style="color:#000000">username</span><span style="color:#666600">).</span><span style="color:#000000">first</span><span style="color:#666600">((</span><span style="color:#000000">path</span><span style="color:#666600">,</span><span style="color:#000000"> value</span><span style="color:#666600">)</span> <span style="color:#666600">-></span><span style="color:#000000"> path</span><span style="color:#666600">.</span><span style="color:#000000">contains</span><span style="color:#666600">(</span><span style="color:#000000">value</span><span style="color:#666600">))</span>    <span style="color:#000000">
    bindings</span><span style="color:#666600">.</span><span style="color:#000000">bind</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#666600">.</span><span style="color:#000088">class</span><span style="color:#666600">)</span>
      <span style="color:#666600">.</span><span style="color:#000000">first</span><span style="color:#666600">((</span><span style="color:#660066">StringPath</span><span style="color:#000000"> path</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#000000"> value</span><span style="color:#666600">)</span> <span style="color:#666600">-></span><span style="color:#000000"> path</span><span style="color:#666600">.</span><span style="color:#000000">containsIgnoreCase</span><span style="color:#666600">(</span><span style="color:#000000">value</span><span style="color:#666600">));</span> <span style="color:#000000">
    bindings</span><span style="color:#666600">.</span><span style="color:#000000">excluding</span><span style="color:#666600">(</span><span style="color:#000000">user</span><span style="color:#666600">.</span><span style="color:#000000">password</span><span style="color:#666600">);</span>                                           
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>
 QuerydslPredicateExecutor提供对特定查找程序方法的访问权限Predicate
 QuerydslBinderCustomizer在存储库界面上定义的是自动拾取和快捷方式@QuerydslPredicate(bindings=…​)
 username属性的绑定定义为简单contains绑定。
 String属性的默认绑定定义为不区分大小写的contains匹配。
 passwordPredicate解决方案中排除该属性。

4.8.3。存储库Populators

如果您使用Spring JDBC模块,您可能熟悉DataSource使用SQL脚本填充支持。虽然它不使用SQL作为数据定义语言,但它在存储库级别上提供了类似的抽象,因为它必须与存储无关。因此,填充程序支持XML(通过Spring的OXM抽象)和JSON(通过Jackson)来定义用于填充存储库的数据。

假设您有一个data.json包含以下内容的文件:

例47.在JSON中定义的数据

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-javascript"><span style="color:#666600">[</span> <span style="color:#666600">{</span> <span style="color:#008800">"_class"</span> <span style="color:#666600">:</span> <span style="color:#008800">"com.acme.Person"</span><span style="color:#666600">,</span>
 <span style="color:#008800">"firstname"</span> <span style="color:#666600">:</span> <span style="color:#008800">"Dave"</span><span style="color:#666600">,</span>
  <span style="color:#008800">"lastname"</span> <span style="color:#666600">:</span> <span style="color:#008800">"Matthews"</span> <span style="color:#666600">},</span>
  <span style="color:#666600">{</span> <span style="color:#008800">"_class"</span> <span style="color:#666600">:</span> <span style="color:#008800">"com.acme.Person"</span><span style="color:#666600">,</span>
 <span style="color:#008800">"firstname"</span> <span style="color:#666600">:</span> <span style="color:#008800">"Carter"</span><span style="color:#666600">,</span>
  <span style="color:#008800">"lastname"</span> <span style="color:#666600">:</span> <span style="color:#008800">"Beauford"</span> <span style="color:#666600">}</span> <span style="color:#666600">]</span></code></span></span>

您可以使用Spring Data Commons中提供的存储库命名空间的populator元素来填充存储库。要将前面的数据填充到PersonRepository,请声明类似于以下内容的populator:

示例48.声明Jackson存储库填充程序

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#666600"><?</span><span style="color:#000000">xml version</span><span style="color:#666600">=</span><span style="color:#008800">"1.0"</span><span style="color:#000000"> encoding</span><span style="color:#666600">=</span><span style="color:#008800">"UTF-8"</span><span style="color:#666600">?></span>
<span style="color:#000088"><beans</span> <span style="color:#660066">xmlns</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/beans"</span>
  <span style="color:#660066">xmlns:xsi</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.w3.org/2001/XMLSchema-instance"</span>
  <span style="color:#660066">xmlns:repository</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/data/repository"</span>
  <span style="color:#660066">xsi:schemaLocation</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    http://www.springframework.org/schema/data/repository/spring-repository.xsd"</span><span style="color:#000088">></span>

  <span style="color:#000088"><repository:jackson2-populator</span> <span style="color:#660066">locations</span><span style="color:#666600">=</span><span style="color:#008800">"classpath:data.json"</span> <span style="color:#000088">/></span>

<span style="color:#000088"></beans></span></code></span></span>

前面的声明导致data.json文件被Jackson读取和反序列化ObjectMapper

通过检查_classJSON文档的属性来确定JSON对象被解组的类型。基础结构最终选择适当的存储库来处理反序列化的对象。

要使用XML来定义应该填充存储库的数据,您可以使用该unmarshaller-populator元素。您将其配置为使用Spring OXM中提供的XML marshaller选项之一。有关详细信息,请参阅Spring参考文档。以下示例说明如何使用JAXB解组存储库填充程序:

示例49.声明一个解组存储库populator(使用JAXB)

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#666600"><?</span><span style="color:#000000">xml version</span><span style="color:#666600">=</span><span style="color:#008800">"1.0"</span><span style="color:#000000"> encoding</span><span style="color:#666600">=</span><span style="color:#008800">"UTF-8"</span><span style="color:#666600">?></span>
<span style="color:#000088"><beans</span> <span style="color:#660066">xmlns</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/beans"</span>
  <span style="color:#660066">xmlns:xsi</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.w3.org/2001/XMLSchema-instance"</span>
  <span style="color:#660066">xmlns:repository</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/data/repository"</span>
  <span style="color:#660066">xmlns:oxm</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/oxm"</span>
  <span style="color:#660066">xsi:schemaLocation</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/repository
    http://www.springframework.org/schema/data/repository/spring-repository.xsd
    http://www.springframework.org/schema/oxm
    http://www.springframework.org/schema/oxm/spring-oxm.xsd"</span><span style="color:#000088">></span>

  <span style="color:#000088"><repository:unmarshaller-populator</span> <span style="color:#660066">locations</span><span style="color:#666600">=</span><span style="color:#008800">"classpath:data.json"</span>
    <span style="color:#660066">unmarshaller-ref</span><span style="color:#666600">=</span><span style="color:#008800">"unmarshaller"</span> <span style="color:#000088">/></span>

  <span style="color:#000088"><oxm:jaxb2-marshaller</span> <span style="color:#660066">contextPath</span><span style="color:#666600">=</span><span style="color:#008800">"com.acme"</span> <span style="color:#000088">/></span>

<span style="color:#000088"></beans></span></code></span></span>

参考文档

5. JPA存储库

本章指出了JPA存储库支持的专业。这建立在“ 使用Spring Data Repositories ”中解释的核心存储库支持的基础上。确保您对那里解释的基本概念有充分的理解。

5.1。介绍

本节介绍通过以下任一方式配置Spring Data JPA的基础知识:

5.1.1。Spring命名空间

Spring Data的JPA模块包含一个允许定义存储库bean的自定义命名空间。它还包含JPA特有的某些功能和元素属性。通常,可以使用repositories元素设置JPA存储库,如以下示例所示:

示例50.使用命名空间设置JPA存储库

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#666600"><?</span><span style="color:#000000">xml version</span><span style="color:#666600">=</span><span style="color:#008800">"1.0"</span><span style="color:#000000"> encoding</span><span style="color:#666600">=</span><span style="color:#008800">"UTF-8"</span><span style="color:#666600">?></span>
<span style="color:#000088"><beans</span> <span style="color:#660066">xmlns</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/beans"</span>
  <span style="color:#660066">xmlns:xsi</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.w3.org/2001/XMLSchema-instance"</span>
  <span style="color:#660066">xmlns:jpa</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/data/jpa"</span>
  <span style="color:#660066">xsi:schemaLocation</span><span style="color:#666600">=</span><span style="color:#008800">"http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/data/jpa
    http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"</span><span style="color:#000088">></span>

  <span style="color:#000088"><jpa:repositories</span> <span style="color:#660066">base-package</span><span style="color:#666600">=</span><span style="color:#008800">"com.acme.repositories"</span> <span style="color:#000088">/></span>

<span style="color:#000088"></beans></span></code></span></span>

使用该repositories元素查找Spring Data存储库,如“ 创建存储库实例 ”中所述。除此之外,它@Repository还会激活所有使用注释的bean的持久性异常转换,以便将JPA持久性提供程序抛出的异常转换为Spring的DataAccessException层次结构。

自定义命名空间属性

除了repositories元素的默认属性之外,JPA名称空间还提供了其他属性,使您可以更好地控制存储库的设置:

表2. repositories元素的自定义JPA特定属性

entity-manager-factory-ref

显式EntityManagerFactory连接要与repositories元素检测到的存储库一起使用。通常EntityManagerFactory在应用程序中使用多个bean时使用。如果未配置,Spring Data会自动查找EntityManagerFactory名称entityManagerFactory中的bean ApplicationContext

transaction-manager-ref

显式PlatformTransactionManager连接要与repositories元素检测到的存储库一起使用。通常仅EntityManagerFactory在配置了多个事务管理器或bean 时才需要。默认为PlatformTransactionManager当前定义的单个ApplicationContext

 如果没有定义显式, Spring Data JPA要求存在一个PlatformTransactionManager名为bean的bean 。transactionManagertransaction-manager-ref

5.1.2。基于注释的配置

Spring Data JPA存储库支持不仅可以通过XML命名空间激活,还可以通过JavaConfig使用注释来激活,如以下示例所示:

例51.使用JavaConfig的Spring Data JPA存储库

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Configuration</span>
<span style="color:#006666">@EnableJpaRepositories</span>
<span style="color:#006666">@EnableTransactionManagement</span>
<span style="color:#000088">class</span> <span style="color:#660066">ApplicationConfig</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Bean</span>
  <span style="color:#000088">public</span> <span style="color:#660066">DataSource</span><span style="color:#000000"> dataSource</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>

    <span style="color:#660066">EmbeddedDatabaseBuilder</span><span style="color:#000000"> builder </span><span style="color:#666600">=</span> <span style="color:#000088">new</span> <span style="color:#660066">EmbeddedDatabaseBuilder</span><span style="color:#666600">();</span>
    <span style="color:#000088">return</span><span style="color:#000000"> builder</span><span style="color:#666600">.</span><span style="color:#000000">setType</span><span style="color:#666600">(</span><span style="color:#660066">EmbeddedDatabaseType</span><span style="color:#666600">.</span><span style="color:#000000">HSQL</span><span style="color:#666600">).</span><span style="color:#000000">build</span><span style="color:#666600">();</span>
  <span style="color:#666600">}</span>

  <span style="color:#006666">@Bean</span>
  <span style="color:#000088">public</span> <span style="color:#660066">LocalContainerEntityManagerFactoryBean</span><span style="color:#000000"> entityManagerFactory</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>

    <span style="color:#660066">HibernateJpaVendorAdapter</span><span style="color:#000000"> vendorAdapter </span><span style="color:#666600">=</span> <span style="color:#000088">new</span> <span style="color:#660066">HibernateJpaVendorAdapter</span><span style="color:#666600">();</span><span style="color:#000000">
    vendorAdapter</span><span style="color:#666600">.</span><span style="color:#000000">setGenerateDdl</span><span style="color:#666600">(</span><span style="color:#000088">true</span><span style="color:#666600">);</span>

    <span style="color:#660066">LocalContainerEntityManagerFactoryBean</span><span style="color:#000000"> factory </span><span style="color:#666600">=</span> <span style="color:#000088">new</span> <span style="color:#660066">LocalContainerEntityManagerFactoryBean</span><span style="color:#666600">();</span><span style="color:#000000">
    factory</span><span style="color:#666600">.</span><span style="color:#000000">setJpaVendorAdapter</span><span style="color:#666600">(</span><span style="color:#000000">vendorAdapter</span><span style="color:#666600">);</span><span style="color:#000000">
    factory</span><span style="color:#666600">.</span><span style="color:#000000">setPackagesToScan</span><span style="color:#666600">(</span><span style="color:#008800">"com.acme.domain"</span><span style="color:#666600">);</span><span style="color:#000000">
    factory</span><span style="color:#666600">.</span><span style="color:#000000">setDataSource</span><span style="color:#666600">(</span><span style="color:#000000">dataSource</span><span style="color:#666600">());</span>
    <span style="color:#000088">return</span><span style="color:#000000"> factory</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>

  <span style="color:#006666">@Bean</span>
  <span style="color:#000088">public</span> <span style="color:#660066">PlatformTransactionManager</span><span style="color:#000000"> transactionManager</span><span style="color:#666600">(</span><span style="color:#660066">EntityManagerFactory</span><span style="color:#000000"> entityManagerFactory</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>

    <span style="color:#660066">JpaTransactionManager</span><span style="color:#000000"> txManager </span><span style="color:#666600">=</span> <span style="color:#000088">new</span> <span style="color:#660066">JpaTransactionManager</span><span style="color:#666600">();</span><span style="color:#000000">
    txManager</span><span style="color:#666600">.</span><span style="color:#000000">setEntityManagerFactory</span><span style="color:#666600">(</span><span style="color:#000000">entityManagerFactory</span><span style="color:#666600">);</span>
    <span style="color:#000088">return</span><span style="color:#000000"> txManager</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>
 您必须创建LocalContainerEntityManagerFactoryBean而不是EntityManagerFactory直接创建,因为除了创建之外,前者还参与异常转换机制EntityManagerFactory

上述配置类使用EmbeddedDatabaseBuilderAPI来设置嵌入式HSQL数据库spring-jdbc。然后Spring Data设置EntityManagerFactory并使用Hibernate作为示例持久性提供程序。这里声明的最后一个基础架构组件是JpaTransactionManager。最后,该示例使用@EnableJpaRepositories注释激活Spring Data JPA存储库,该注释基本上具有与XML命名空间相同的属性。如果未配置基本软件包,则使用配置类所在的基础软件包。

5.1.3。引导模式

默认情况下,Spring Data JPA存储库是默认的Spring bean。它们是单例范围并且急切地初始化。在启动期间,他们已经与JPA EntityManager进行交互以进行验证和元数据分析。Spring Framework支持EntityManagerFactory在后台线程中初始化JPA ,因为该进程通常占用Spring应用程序中的大量启动时间。为了有效地利用后台初始化,我们需要确保尽可能晚地初始化JPA存储库。

从Spring Data JPA 2.1开始,您现在可以配置BootstrapMode(通过@EnableJpaRepositories注释或XML命名空间),它具有以下值:

  • DEFAULT(默认) - 除非明确注释,否则将急切地实例化存储库@Lazy。如果没有客户端bean需要存储库的实例,那么lazification只会生效,因为这需要初始化存储库bean。

  • LAZY - 隐式声明所有存储库bean都是惰性的,并且还会导致创建延迟初始化代理以将其注入到客户端bean中。这意味着,如果客户端bean只是将实例存储在字段中而不是在初始化期间使用存储库,则不会实例化存储库。初始化存储库实例并在首次与存储库交互时进行验证。

  • DEFERRED - 基本上与操作模式相同LAZY,但触发存储库初始化以响应,ContextRefreshedEvent以便在应用程序完全启动之前验证存储库。

建议

如果您没有使用默认引导程序模式的异步JPA引导程序棒。

如果您异步引导JPA,DEFERRED则是一个合理的默认值,因为它将确保Spring Data JPA引导程序仅等待EntityManagerFactory设置,如果它本身比初始化所有其他应用程序组件花费更长时间。尽管如此,它确保在应用程序发出信号之前正确初始化和验证存储库。

LAZY是测试场景和本地开发的不错选择。一旦您非常确定存储库将正确引导,或者在您测试应用程序的其他部分的情况下,对所有存储库执行验证可能只会不必要地增加启动时间。这同样适用于本地开发,在该开发中,您只访问可能只需要初始化单个存储库的应用程序部分。

5.2。保留实体

本节介绍如何使用Spring Data JPA保留(保存)实体。

5.2.1。保存实体

可以使用该CrudRepository.save(…)方法执行保存实体。它通过使用基础JPA持久化或合并给定实体EntityManager。如果实体尚未持久化,则Spring Data JPA会通过调用该entityManager.persist(…)方法来保存实体。否则,它会调用该entityManager.merge(…)方法。

实体状态检测策略

Spring Data JPA提供以下策略来检测实体是否是新实体:

  • Id-Property检查(默认):默认情况下,Spring Data JPA检查给定实体的identifier属性。如果标识符属性为null,则假定该实体是新的。否则,它被认为不是新的。

  • 实现Persistable:如果实体实现Persistable,Spring Data JPA会将新检测委托给isNew(…)实体的方法。有关详细信息,请参阅JavaDoc

  • 实现EntityInformation:您可以通过创建相应的方法并重写方法来自定义实现中EntityInformation使用的抽象。然后,您必须将自定义实现注册为Spring bean。请注意,这很少是必要的。有关详细信息,请参阅JavaDocSimpleJpaRepositoryJpaRepositoryFactorygetEntityInformation(…)JpaRepositoryFactory

5.3。查询方法

本节介绍使用Spring Data JPA创建查询的各种方法。

5.3.1。查询查找策略

JPA模块支持将查询手动定义为String或从方法名称派生。

声明的查询

尽管获取从方法名称派生的查询非常方便,但是可能面临这样的情况:方法名称解析器不支持要使用的关键字,或者方法名称会变得不必要地丑陋。所以,你可以通过命名约定使用JPA命名查询(请参阅使用JPA命名查询了解更多信息)或相当具有注解你的查询方法@Query(请参阅使用@Query的详细信息)。

5.3.2。查询创建

通常,JPA的查询创建机制的工作方式如“ 查询方法 ”中所述。以下示例显示了JPA查询方法转换为的内容:

示例52.从方法名称创建查询

public interface UserRepository扩展了Repository <User,Long> {

  List <User> findByEmailAddressAndLastname(String emailAddress,String lastname);
}

我们使用JPA标准API创建一个查询,但实质上,这转换为以下查询:select u from User u where u.emailAddress = ?1 and u.lastname = ?2。Spring Data JPA执行属性检查并遍历嵌套属性,如“ 属性表达式 ”中所述。

下表描述了JPA支持的关键字以及包含该关键字的方法转换为:

表3.方法名称中支持的关键字
关键词样品JPQL代码段

And

findByLastnameAndFirstname

… where x.lastname = ?1 and x.firstname = ?2

Or

findByLastnameOrFirstname

… where x.lastname = ?1 or x.firstname = ?2

Is,Equals

findByFirstnamefindByFirstnameIsfindByFirstnameEquals

… where x.firstname = ?1

Between

findByStartDateBetween

… where x.startDate between ?1 and ?2

LessThan

findByAgeLessThan

… where x.age < ?1

LessThanEqual

findByAgeLessThanEqual

… where x.age <= ?1

GreaterThan

findByAgeGreaterThan

… where x.age > ?1

GreaterThanEqual

findByAgeGreaterThanEqual

… where x.age >= ?1

After

findByStartDateAfter

… where x.startDate > ?1

Before

findByStartDateBefore

… where x.startDate < ?1

IsNull

findByAgeIsNull

… where x.age is null

IsNotNull,NotNull

findByAge(Is)NotNull

… where x.age not null

Like

findByFirstnameLike

… where x.firstname like ?1

NotLike

findByFirstnameNotLike

… where x.firstname not like ?1

StartingWith

findByFirstnameStartingWith

… where x.firstname like ?1(附加参数绑定%

EndingWith

findByFirstnameEndingWith

… where x.firstname like ?1(与前置绑定的参数%

Containing

findByFirstnameContaining

… where x.firstname like ?1(参数绑定包装%

OrderBy

findByAgeOrderByLastnameDesc

… where x.age = ?1 order by x.lastname desc

Not

findByLastnameNot

… where x.lastname <> ?1

In

findByAgeIn(Collection<Age> ages)

… where x.age in ?1

NotIn

findByAgeNotIn(Collection<Age> ages)

… where x.age not in ?1

True

findByActiveTrue()

… where x.active = true

False

findByActiveFalse()

… where x.active = false

IgnoreCase

findByFirstnameIgnoreCase

… where UPPER(x.firstame) = UPPER(?1)

 In并且NotIn还将任何子类Collection作为参数以及数组或变量。对于同一逻辑运算符的其他语法版本,请选中“ 存储库查询关键字 ”。

5.3.3。使用JPA命名查询

 示例使用<named-query />元素和@NamedQuery注释。必须在JPA查询语言中定义这些配置元素的查询。当然,你也可以使用<named-native-query />或者@NamedNativeQuery。这些元素允许您通过失去数据库平台独立性来在本机SQL中定义查询。

XML命名查询定义

要使用XML配置,请将必要的<named-query />元素添加到orm.xml位于META-INF类路径文件夹中的JPA配置文件中。通过使用某些已定义的命名约定,可以自动调用命名查询。有关详细信息,请参阅下文。

例53. XML命名查询配置

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><named-query</span> <span style="color:#660066">name</span><span style="color:#666600">=</span><span style="color:#008800">"User.findByLastname"</span><span style="color:#000088">></span>
  <span style="color:#000088"><query></span><span style="color:#000000">select u from User u where u.lastname = ?1</span><span style="color:#000088"></query></span>
<span style="color:#000088"></named-query></span></code></span></span>

该查询具有一个特殊名称,用于在运行时解析它。

基于注释的配置

基于注释的配置具有不需要编辑另一个配置文件的优点,从而降低了维护工作量。您需要为每个新的查询声明重新编译域类,从而为此获益。

示例54.基于注释的命名查询配置

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Entity</span>
<span style="color:#006666">@NamedQuery</span><span style="color:#666600">(</span><span style="color:#000000">name </span><span style="color:#666600">=</span> <span style="color:#008800">"User.findByEmailAddress"</span><span style="color:#666600">,</span><span style="color:#000000">
  query </span><span style="color:#666600">=</span> <span style="color:#008800">"select u from User u where u.emailAddress = ?1"</span><span style="color:#666600">)</span>
<span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#660066">User</span> <span style="color:#666600">{</span>

<span style="color:#666600">}</span></code></span></span>

声明接口

要允许执行这些命名查询,请指定UserRepository如下:

例55. UserRepository中的查询方法声明

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>

  <span style="color:#660066">User</span><span style="color:#000000"> findByEmailAddress</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> emailAddress</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

Spring Data尝试将对这些方法的调用解析为命名查询,从配置的域类的简单名称开始,后跟由点分隔的方法名称。因此,前面的示例将使用在examlpe中定义的命名查询,而不是尝试从方法名称创建查询。

5.3.4。运用@Query

使用命名查询来声明实体查询是一种有效的方法,适用于少量查询。由于查询本身与执行它们的Java方法相关联,因此您可以使用Spring Data JPA @Query注释直接绑定它们,而不是将它们注释到域类。这将域类从特定于持久性的信息中释放出来,并将查询与存储库接口共同定位。

对查询方法进行批注的查询优先于使用@NamedQuery或在其中声明的查询定义的查询orm.xml

以下示例显示使用@Query注释创建的查询:

例56.使用查询方法声明查询 @Query

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"select u from User u where u.emailAddress = ?1"</span><span style="color:#666600">)</span>
  <span style="color:#660066">User</span><span style="color:#000000"> findByEmailAddress</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> emailAddress</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

使用高级LIKE表达式

创建的手动定义查询的查询执行机制@Query允许LIKE在查询定义中定义高级表达式,如以下示例所示:

例57. like@Query中的高级表达式

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"select u from User u where u.firstname like %?1"</span><span style="color:#666600">)</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByFirstnameEndsWith</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

在前面的示例中,识别LIKE分隔符(%),并将查询转换为有效的JPQL查询(删除%)。在执行查询时,传递给方法调用的参数将使用先前识别的LIKE模式进行扩充。

本机查询

@Query注释允许通过设定运行的原生查询nativeQuery标志设置为true,如图以下示例:

例58.使用@Query在查询方法中声明本机查询

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#000000">value </span><span style="color:#666600">=</span> <span style="color:#008800">"SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1"</span><span style="color:#666600">,</span><span style="color:#000000"> nativeQuery </span><span style="color:#666600">=</span> <span style="color:#000088">true</span><span style="color:#666600">)</span>
  <span style="color:#660066">User</span><span style="color:#000000"> findByEmailAddress</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> emailAddress</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>
 Spring Data JPA目前不支持对本机查询进行动态排序,因为它必须操纵声明的实际查询,这对于本机SQL无法可靠地执行。但是,您可以通过自己指定计数查询来使用本机查询进行分页,如以下示例所示:

例59.使用查询方法为分页声明本机计数查询 @Query

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#000000">value </span><span style="color:#666600">=</span> <span style="color:#008800">"SELECT * FROM USERS WHERE LASTNAME = ?1"</span><span style="color:#666600">,</span><span style="color:#000000">
    countQuery </span><span style="color:#666600">=</span> <span style="color:#008800">"SELECT count(*) FROM USERS WHERE LASTNAME = ?1"</span><span style="color:#666600">,</span><span style="color:#000000">
    nativeQuery </span><span style="color:#666600">=</span> <span style="color:#000088">true</span><span style="color:#666600">)</span>
  <span style="color:#660066">Page</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

类似的方法也适用于命名的本机查询,方法是将.count后缀添加到查询副本中。但是,您可能需要为计数查询注册结果集映射。

5.3.5。使用排序

可以通过提供PageRequestSort直接使用来进行排序。在需要匹配域模型的Order实例中实际使用的属性Sort,这意味着它们需要解析为查询中使用的属性或别名。JPQL将其定义为状态字段路径表达式。

 使用任何不可引用的路径表达式导致Exception

但是,Sort@Query您一起使用可以让您潜入Order包含该ORDER BY子句中的函数的非路径检查实例。这是可能的,因为Order它附加到给定的查询字符串。默认情况下,Spring Data JPA拒绝任何Order包含函数调用的实例,但您可以使用它JpaSort.unsafe来添加可能不安全的排序。

以下示例使用SortJpaSort,包括不安全选项JpaSort

例60.使用SortJpaSort

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"select u from User u where u.lastname like ?1%"</span><span style="color:#666600">)</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByAndSort</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Sort</span><span style="color:#000000"> sort</span><span style="color:#666600">);</span>

  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"select u.id, LENGTH(u.firstname) as fn_len from User u where u.lastname like ?1%"</span><span style="color:#666600">)</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Object</span><span style="color:#666600">[]></span><span style="color:#000000"> findByAsArrayAndSort</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Sort</span><span style="color:#000000"> sort</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span><span style="color:#000000">

repo</span><span style="color:#666600">.</span><span style="color:#000000">findByAndSort</span><span style="color:#666600">(</span><span style="color:#008800">"lannister"</span><span style="color:#666600">,</span> <span style="color:#000088">new</span> <span style="color:#660066">Sort</span><span style="color:#666600">(</span><span style="color:#008800">"firstname"</span><span style="color:#666600">));</span>               <span style="color:#000000">
repo</span><span style="color:#666600">.</span><span style="color:#000000">findByAndSort</span><span style="color:#666600">(</span><span style="color:#008800">"stark"</span><span style="color:#666600">,</span> <span style="color:#000088">new</span> <span style="color:#660066">Sort</span><span style="color:#666600">(</span><span style="color:#008800">"LENGTH(firstname)"</span><span style="color:#666600">));</span>           <span style="color:#000000">
repo</span><span style="color:#666600">.</span><span style="color:#000000">findByAndSort</span><span style="color:#666600">(</span><span style="color:#008800">"targaryen"</span><span style="color:#666600">,</span> <span style="color:#660066">JpaSort</span><span style="color:#666600">.</span><span style="color:#000000">unsafe</span><span style="color:#666600">(</span><span style="color:#008800">"LENGTH(firstname)"</span><span style="color:#666600">));</span> <span style="color:#000000">
repo</span><span style="color:#666600">.</span><span style="color:#000000">findByAsArrayAndSort</span><span style="color:#666600">(</span><span style="color:#008800">"bolton"</span><span style="color:#666600">,</span> <span style="color:#000088">new</span> <span style="color:#660066">Sort</span><span style="color:#666600">(</span><span style="color:#008800">"fn_len"</span><span style="color:#666600">));</span>              </code></span></span>
 Sort指向域模型中的属性的有效表达式。
 Sort包含函数调用无效。有例外。
 有效Sort包含明确不安全 Order
 Sort指向别名函数的有效表达式。

5.3.6。使用命名参数

默认情况下,Spring Data JPA使用基于位置的参数绑定,如前面所有示例中所述。这使得查询方法在重构参数位置时容易出错。要解决此问题,可以使用@Param批注为方法参数指定具体名称并在查询中绑定名称,如以下示例所示:

例61.使用命名参数

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"select u from User u where u.firstname = :firstname or u.lastname = :lastname"</span><span style="color:#666600">)</span>
  <span style="color:#660066">User</span><span style="color:#000000"> findByLastnameOrFirstname</span><span style="color:#666600">(</span><span style="color:#006666">@Param</span><span style="color:#666600">(</span><span style="color:#008800">"lastname"</span><span style="color:#666600">)</span> <span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span>
                                 <span style="color:#006666">@Param</span><span style="color:#666600">(</span><span style="color:#008800">"firstname"</span><span style="color:#666600">)</span> <span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>
 方法参数根据它们在定义的查询中的顺序进行切换。
 从版本4开始,Spring完全支持基于-parameters编译器标志的Java 8参数名称发现。通过在构建中使用此标志作为调试信息的替代方法,可以省略@Param命名参数的注释。

5.3.7。使用SpEL表达式

从Spring Data JPA 1.4版开始,我们支持在手动定义的查询中使用受限制的SpEL模板表达式@Query。在执行查询时,将根据预定义的变量集评估这些表达式。Spring Data JPA支持一个名为的变量entityName。它的用法是select x from #{#entityName} x。它插入entityName与给定存储库关联的域类型。该entityName解决如下:如果域类型已设置的name属性@Entity的注释,它被使用。否则,使用域类型的简单类名。

以下示例演示#{#entityName}了查询字符串中表达式的一个用例,您希望使用查询方法和手动定义的查询来定义存储库接口:

示例62.在存储库查询方法中使用SpEL表达式 - entityName

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Entity</span>
<span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#660066">User</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Id</span>
  <span style="color:#006666">@GeneratedValue</span>
  <span style="color:#660066">Long</span><span style="color:#000000"> id</span><span style="color:#666600">;</span>

  <span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">;</span>
<span style="color:#666600">}</span>

<span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span><span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"select u from #{#entityName} u where u.lastname = ?1"</span><span style="color:#666600">)</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

要避免在@Query注释的查询字符串中声明实际实体名称,可以使用该#{#entityName}变量。

 entityName可以通过使用定制@Entity的注释。orm.xmlSpEL表达式不支持自定义。

当然,您可以User直接在查询声明中使用,但这也需要您更改查询。引用#entityNameUser类的潜在未来重映射到不同的实体名称(例如,通过使用@Entity(name = "MyUser")

#{#entityName}查询字符串中表达式的另一个用例是,如果要为具体域类型定义具有专用存储库接口的通用存储库接口。要在具体接口上不重复自定义查询方法的定义,可以@Query在通用存储库接口的注释的查询字符串中使用实体名称表达式,如以下示例所示:

示例63.在存储库查询方法中使用SpEL表达式 - 具有继承的entityName

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@MappedSuperclass</span>
<span style="color:#000088">public</span> <span style="color:#000088">abstract</span> <span style="color:#000088">class</span> <span style="color:#660066">AbstractMappedType</span> <span style="color:#666600">{</span>
  <span style="color:#666600">…</span>
  <span style="color:#660066">String</span><span style="color:#000000"> attribute
</span><span style="color:#666600">}</span>

<span style="color:#006666">@Entity</span>
<span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#660066">ConcreteType</span> <span style="color:#000088">extends</span> <span style="color:#660066">AbstractMappedType</span> <span style="color:#666600">{</span> <span style="color:#666600">…</span> <span style="color:#666600">}</span>

<span style="color:#006666">@NoRepositoryBean</span>
<span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">MappedTypeRepository</span><span style="color:#666600"><</span><span style="color:#000000">T </span><span style="color:#000088">extends</span> <span style="color:#660066">AbstractMappedType</span><span style="color:#666600">></span>
  <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"select t from #{#entityName} t where t.attribute = ?1"</span><span style="color:#666600">)</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> findAllByAttribute</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> attribute</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span>

<span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">ConcreteRepository</span>
  <span style="color:#000088">extends</span> <span style="color:#660066">MappedTypeRepository</span><span style="color:#666600"><</span><span style="color:#660066">ConcreteType</span><span style="color:#666600">></span> <span style="color:#666600">{</span> <span style="color:#666600">…</span> <span style="color:#666600">}</span></code></span></span>

在前面的示例中,MappedTypeRepository接口是一些扩展的域类型的公共父接口AbstractMappedType。它还定义了泛型findAllByAttribute(…)方法,该方法可用于专用存储库接口的实例。如果您现在调用findByAllAttribute(…)ConcreteRepository,查询变得select t from ConcreteType t where t.attribute = ?1

5.3.8。修改查询

前面的所有部分都描述了如何声明查询以访问给定实体或实体集合。您可以使用“ Spring Data Repositories的自定义实现 ”中描述的工具添加自定义修改行为。由于此方法对于全面的自定义功能是可行的,因此您可以通过使用注释查询方法来修改仅需要参数绑定的查询@Modifying,如以下示例所示:

例64.声明操纵查询

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Modifying</span>
<span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"update User u set u.firstname = ?1 where u.lastname = ?2"</span><span style="color:#666600">)</span>
<span style="color:#000088">int</span><span style="color:#000000"> setFixedFirstnameFor</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span></code></span></span>

这样做会触发将方法注释为更新查询而不是选择查询的查询。作为EntityManager可能的修改查询执行后包含过时的实体,我们不会自动清除它(见的JavaDocEntityManager.clear()详细内容),因为这有效地删除所有非刷新的变化仍悬而未决的EntityManager。如果希望EntityManager自动清除,可以将@Modifying注释的clearAutomatically属性设置为true

派生删除查询

Spring Data JPA还支持派生删除查询,使您可以避免显式声明JPQL查询,如以下示例所示:

示例65.使用派生的删除查询

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#000088">void</span><span style="color:#000000"> deleteByRoleId</span><span style="color:#666600">(</span><span style="color:#000088">long</span><span style="color:#000000"> roleId</span><span style="color:#666600">);</span>

  <span style="color:#006666">@Modifying</span>
  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"delete from User u where user.role.id = ?1"</span><span style="color:#666600">)</span>
  <span style="color:#000088">void</span><span style="color:#000000"> deleteInBulkByRoleId</span><span style="color:#666600">(</span><span style="color:#000088">long</span><span style="color:#000000"> roleId</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

尽管该deleteByRoleId(…)方法看起来基本上产生了与之相同的结果deleteInBulkByRoleId(…),但两种方法声明在执行方式方面存在重要差异。顾名思义,后一种方法针对数据库发出单个JPQL查询(在注释中定义的查询)。这意味着即使当前加载的实例User也没有看到调用生命周期回调。

为了确保实际调用生命周期查询,调用deleteByRoleId(…)执行查询然后逐个删除返回的实例,以便持久性提供程序可以实际调用@PreRemove这些实体的回调。

事实上,派生删除查询是执行查询然后调用CrudRepository.delete(Iterable<User> users)结果并保持行为与其他delete(…)方法的实现同步的快捷方式CrudRepository

5.3.9。应用查询提示

要将JPA查询提示应用于存储库界面中声明的查询,可以使用@QueryHints注释。它需要一组JPA @QueryHint注释和一个布尔标志来潜在地禁用应用于应用分页时触发的附加计数查询的提示,如以下示例所示:

示例66.将QueryHints与存储库方法一起使用

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@QueryHints</span><span style="color:#666600">(</span><span style="color:#000000">value </span><span style="color:#666600">=</span> <span style="color:#666600">{</span> <span style="color:#006666">@QueryHint</span><span style="color:#666600">(</span><span style="color:#000000">name </span><span style="color:#666600">=</span> <span style="color:#008800">"name"</span><span style="color:#666600">,</span><span style="color:#000000"> value </span><span style="color:#666600">=</span> <span style="color:#008800">"value"</span><span style="color:#666600">)},</span><span style="color:#000000">
              forCounting </span><span style="color:#666600">=</span> <span style="color:#000088">false</span><span style="color:#666600">)</span>
  <span style="color:#660066">Page</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Pageable</span><span style="color:#000000"> pageable</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

前面的声明将应用@QueryHint为实际查询配置,但省略将其应用于触发的计数查询以计算总页数。

5.3.10。配置Fetch-和LoadGraphs

JPA 2.1规范引入了对指定Fetch-和LoadGraphs的支持,我们也支持使用@EntityGraph注释,它允许您引用@NamedEntityGraph定义。您可以在实体上使用该批注来配置生成的查询的获取计划。可以使用注释上的属性来配置提取的类型(FetchLoad)。有关进一步的参考,请参阅JPA 2.1 Spec 3.7.4。type@EntityGraph

以下示例显示如何在实体上定义命名实体图:

例67.在实体上定义命名实体图。

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Entity</span>
<span style="color:#006666">@NamedEntityGraph</span><span style="color:#666600">(</span><span style="color:#000000">name </span><span style="color:#666600">=</span> <span style="color:#008800">"GroupInfo.detail"</span><span style="color:#666600">,</span><span style="color:#000000">
  attributeNodes </span><span style="color:#666600">=</span> <span style="color:#006666">@NamedAttributeNode</span><span style="color:#666600">(</span><span style="color:#008800">"members"</span><span style="color:#666600">))</span>
<span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#660066">GroupInfo</span> <span style="color:#666600">{</span>

  <span style="color:#880000">// default fetch mode is lazy.</span>
  <span style="color:#006666">@ManyToMany</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">GroupMember</span><span style="color:#666600">></span><span style="color:#000000"> members </span><span style="color:#666600">=</span> <span style="color:#000088">new</span> <span style="color:#660066">ArrayList</span><span style="color:#666600"><</span><span style="color:#660066">GroupMember</span><span style="color:#666600">>();</span>

  <span style="color:#666600">…</span>
<span style="color:#666600">}</span></code></span></span>

以下示例显示如何在存储库查询方法上引用命名实体图:

示例68.在存储库查询方法上引用命名实体图定义。

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Repository</span>
<span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">GroupRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">GroupInfo</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@EntityGraph</span><span style="color:#666600">(</span><span style="color:#000000">value </span><span style="color:#666600">=</span> <span style="color:#008800">"GroupInfo.detail"</span><span style="color:#666600">,</span><span style="color:#000000"> type </span><span style="color:#666600">=</span> <span style="color:#660066">EntityGraphType</span><span style="color:#666600">.</span><span style="color:#000000">LOAD</span><span style="color:#666600">)</span>
  <span style="color:#660066">GroupInfo</span><span style="color:#000000"> getByGroupName</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> name</span><span style="color:#666600">);</span>

<span style="color:#666600">}</span></code></span></span>

也可以使用来定义ad hoc实体图@EntityGraph。提供的内容attributePaths将转换为相应的,EntityGraph无需明确添加@NamedEntityGraph到您的域类型,如以下示例所示:

示例69.在存储库查询方法上使用AD-HOC实体图定义。

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Repository</span>
<span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">GroupRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">GroupInfo</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@EntityGraph</span><span style="color:#666600">(</span><span style="color:#000000">attributePaths </span><span style="color:#666600">=</span> <span style="color:#666600">{</span> <span style="color:#008800">"members"</span> <span style="color:#666600">})</span>
  <span style="color:#660066">GroupInfo</span><span style="color:#000000"> getByGroupName</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> name</span><span style="color:#666600">);</span>

<span style="color:#666600">}</span></code></span></span>

5.3.11。预测

Spring Data查询方法通常返回由存储库管理的聚合根的一个或多个实例。但是,有时可能需要根据这些类型的某些属性创建投影。Spring Data允许建模专用返回类型,以更有选择地检索托管聚合的部分视图。

想象一下存储库和聚合根类型,例如以下示例:

例子70.样本聚合和存储库

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">Person</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Id</span><span style="color:#000000"> UUID id</span><span style="color:#666600">;</span>
  <span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">,</span><span style="color:#000000"> lastname</span><span style="color:#666600">;</span>
  <span style="color:#660066">Address</span><span style="color:#000000"> address</span><span style="color:#666600">;</span>

  <span style="color:#000088">static</span> <span style="color:#000088">class</span> <span style="color:#660066">Address</span> <span style="color:#666600">{</span>
    <span style="color:#660066">String</span><span style="color:#000000"> zipCode</span><span style="color:#666600">,</span><span style="color:#000000"> city</span><span style="color:#666600">,</span><span style="color:#000000"> street</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span>

<span style="color:#000088">interface</span> <span style="color:#660066">PersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">,</span><span style="color:#000000"> UUID</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#660066">Collection</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

现在假设我们只想检索人的姓名属性。Spring Data提供了什么方法来实现这一目标?本章的其余部分回答了这个问题。

基于接口的预测

将查询结果限制为仅名称属性的最简单方法是声明一个接口,该接口公开要读取的属性的访问器方法,如以下示例所示:

示例71.用于检索属性子集的投影接口

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">NamesOnly</span> <span style="color:#666600">{</span>

  <span style="color:#660066">String</span><span style="color:#000000"> getFirstname</span><span style="color:#666600">();</span>
  <span style="color:#660066">String</span><span style="color:#000000"> getLastname</span><span style="color:#666600">();</span>
<span style="color:#666600">}</span></code></span></span>

这里重要的一点是,此处定义的属性与聚合根中的属性完全匹配。这样做可以添加查询方法,如下所示:

示例72.使用基于接口的投影和查询方法的存储库

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">PersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">,</span><span style="color:#000000"> UUID</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#660066">Collection</span><span style="color:#666600"><</span><span style="color:#660066">NamesOnly</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

查询执行引擎在运行时为返回的每个元素创建该接口的代理实例,并将对暴露方法的调用转发给目标对象。

可以递归使用预测。如果您还想包含一些Address信息,请为其创建一个投影接口,并从声明中返回该接口getAddress(),如以下示例所示:

示例73.用于检索属性子集的投影接口

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">PersonSummary</span> <span style="color:#666600">{</span>

  <span style="color:#660066">String</span><span style="color:#000000"> getFirstname</span><span style="color:#666600">();</span>
  <span style="color:#660066">String</span><span style="color:#000000"> getLastname</span><span style="color:#666600">();</span>
  <span style="color:#660066">AddressSummary</span><span style="color:#000000"> getAddress</span><span style="color:#666600">();</span>

  <span style="color:#000088">interface</span> <span style="color:#660066">AddressSummary</span> <span style="color:#666600">{</span>
    <span style="color:#660066">String</span><span style="color:#000000"> getCity</span><span style="color:#666600">();</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

在方法调用上,address获取目标实例的属性并依次将其包装到投影代理中。

封闭式预测

其访问器方法都与目标聚合的属性匹配的投影接口被认为是封闭投影。以下示例(我们在本章前面也使用过)是一个封闭的投影:

例74.一个封闭的投影

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">NamesOnly</span> <span style="color:#666600">{</span>

  <span style="color:#660066">String</span><span style="color:#000000"> getFirstname</span><span style="color:#666600">();</span>
  <span style="color:#660066">String</span><span style="color:#000000"> getLastname</span><span style="color:#666600">();</span>
<span style="color:#666600">}</span></code></span></span>

如果使用闭合投影,Spring Data可以优化查询执行,因为我们知道支持投影代理所需的所有属性。有关详细信息,请参阅参考文档中特定于模块的部分。

公开预测

投影接口中的访问器方法也可用于通过使用@Value注释计算新值,如以下示例所示:

例75.一个公开投影

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">NamesOnly</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Value</span><span style="color:#666600">(</span><span style="color:#008800">"#{target.firstname + ' ' + target.lastname}"</span><span style="color:#666600">)</span>
  <span style="color:#660066">String</span><span style="color:#000000"> getFullName</span><span style="color:#666600">();</span>
  <span style="color:#666600">…</span>
<span style="color:#666600">}</span></code></span></span>

支持投影的聚合根在target变量中可用。使用的投影界面@Value是开放投影。在这种情况下,Spring Data无法应用查询执行优化,因为SpEL表达式可以使用聚合根的任何属性。

使用的表达式@Value不应该太复杂 - 您希望避免在String变量中编程。对于非常简单的表达式,一个选项可能是采用默认方法(在Java 8中引入),如以下示例所示:

例76.使用默认方法进行自定义逻辑的投影界面

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">NamesOnly</span> <span style="color:#666600">{</span>

  <span style="color:#660066">String</span><span style="color:#000000"> getFirstname</span><span style="color:#666600">();</span>
  <span style="color:#660066">String</span><span style="color:#000000"> getLastname</span><span style="color:#666600">();</span>

  <span style="color:#000088">default</span> <span style="color:#660066">String</span><span style="color:#000000"> getFullName</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
    <span style="color:#000088">return</span><span style="color:#000000"> getFirstname</span><span style="color:#666600">.</span><span style="color:#000000">concat</span><span style="color:#666600">(</span><span style="color:#008800">" "</span><span style="color:#666600">).</span><span style="color:#000000">concat</span><span style="color:#666600">(</span><span style="color:#000000">getLastname</span><span style="color:#666600">());</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

这种方法要求您能够纯粹基于投影接口上公开的其他访问器方法实现逻辑。第二个更灵活的选项是在Spring bean中实现自定义逻辑,然后从SpEL表达式调用它,如以下示例所示:

例77.示例Person对象

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Component</span>
<span style="color:#000088">class</span> <span style="color:#660066">MyBean</span> <span style="color:#666600">{</span>

  <span style="color:#660066">String</span><span style="color:#000000"> getFullName</span><span style="color:#666600">(</span><span style="color:#660066">Person</span><span style="color:#000000"> person</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#666600">…</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span>

<span style="color:#000088">interface</span> <span style="color:#660066">NamesOnly</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Value</span><span style="color:#666600">(</span><span style="color:#008800">"#{@myBean.getFullName(target)}"</span><span style="color:#666600">)</span>
  <span style="color:#660066">String</span><span style="color:#000000"> getFullName</span><span style="color:#666600">();</span>
  <span style="color:#666600">…</span>
<span style="color:#666600">}</span></code></span></span>

注意SpEL表达式如何引用myBean并调用该getFullName(…)方法并将投影目标转发为方法参数。由SpEL表达式评估支持的方法也可以使用方法参数,然后可以从表达式引用它们。方法参数可通过Object名为的数组获得args。以下示例显示如何从args数组中获取方法参数:

例78.示例Person对象

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">NamesOnly</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Value</span><span style="color:#666600">(</span><span style="color:#008800">"#{args[0] + ' ' + target.firstname + '!'}"</span><span style="color:#666600">)</span>
  <span style="color:#660066">String</span><span style="color:#000000"> getSalutation</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> prefix</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

同样,对于更复杂的表达式,您应该使用Spring bean并让表达式调用方法,如前所述

基于类别的预测(DTO)

定义投影的另一种方法是使用值类型DTO(数据传输对象),它包含应该检索的字段的属性。这些DTO类型可以与投影界面完全相同的方式使用,除了不发生代理并且不能应用嵌套投影。

如果存储通过限制要加载的字段来优化查询执行,则要加载的字段将根据公开的构造函数的参数名称确定。

以下示例显示了投影DTO:

例79.投影DTO

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">NamesOnly</span> <span style="color:#666600">{</span>

  <span style="color:#000088">private</span> <span style="color:#000088">final</span> <span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">,</span><span style="color:#000000"> lastname</span><span style="color:#666600">;</span>

  <span style="color:#660066">NamesOnly</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>

    <span style="color:#000088">this</span><span style="color:#666600">.</span><span style="color:#000000">firstname </span><span style="color:#666600">=</span><span style="color:#000000"> firstname</span><span style="color:#666600">;</span>
    <span style="color:#000088">this</span><span style="color:#666600">.</span><span style="color:#000000">lastname </span><span style="color:#666600">=</span><span style="color:#000000"> lastname</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>

  <span style="color:#660066">String</span><span style="color:#000000"> getFirstname</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
    <span style="color:#000088">return</span> <span style="color:#000088">this</span><span style="color:#666600">.</span><span style="color:#000000">firstname</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>

  <span style="color:#660066">String</span><span style="color:#000000"> getLastname</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
    <span style="color:#000088">return</span> <span style="color:#000088">this</span><span style="color:#666600">.</span><span style="color:#000000">lastname</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>

  <span style="color:#880000">// equals(…) and hashCode() implementations</span>
<span style="color:#666600">}</span></code></span></span>
 

避免投影DTO的样板代码

您可以使用Project Lombok大大简化DTO的代码,它提供了一个@Value注释(不要与@Value之前的接口示例中显示的Spring的注释混淆)。如果您使用Project Lombok的@Value注释,前面显示的示例DTO将变为以下内容:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.6)"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Value</span>
<span style="color:#000088">class</span> <span style="color:#660066">NamesOnly</span> <span style="color:#666600">{</span>
	<span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">,</span><span style="color:#000000"> lastname</span><span style="color:#666600">;</span>
<span style="color:#666600">}</span></code></span></span></span>

private final默认情况下,字段是公开的构造函数,它接受所有字段并自动获取equals(…)hashCode()实现方法。

动态预测

到目前为止,我们已经使用投影类型作为集合的返回类型或元素类型。但是,您可能希望选择要在调用时使用的类型(这使其成为动态类型)。要应用动态投影,请使用查询方法,如以下示例中所示:

示例80.使用动态投影参数的存储库

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">PersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">,</span><span style="color:#000000"> UUID</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span> <span style="color:#660066">Collection</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">,</span> <span style="color:#660066">Class</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> type</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

这样,该方法可用于按原样或应用投影获取聚合,如以下示例所示:

例子81.使用具有动态投影的存储库

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">void</span><span style="color:#000000"> someMethod</span><span style="color:#666600">(</span><span style="color:#660066">PersonRepository</span><span style="color:#000000"> people</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>

  <span style="color:#660066">Collection</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> aggregates </span><span style="color:#666600">=</span><span style="color:#000000">
    people</span><span style="color:#666600">.</span><span style="color:#000000">findByLastname</span><span style="color:#666600">(</span><span style="color:#008800">"Matthews"</span><span style="color:#666600">,</span> <span style="color:#660066">Person</span><span style="color:#666600">.</span><span style="color:#000088">class</span><span style="color:#666600">);</span>

  <span style="color:#660066">Collection</span><span style="color:#666600"><</span><span style="color:#660066">NamesOnly</span><span style="color:#666600">></span><span style="color:#000000"> aggregates </span><span style="color:#666600">=</span><span style="color:#000000">
    people</span><span style="color:#666600">.</span><span style="color:#000000">findByLastname</span><span style="color:#666600">(</span><span style="color:#008800">"Matthews"</span><span style="color:#666600">,</span> <span style="color:#660066">NamesOnly</span><span style="color:#666600">.</span><span style="color:#000088">class</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

5.4。存储过程

JPA 2.1规范引入了对使用JPA条件查询API调用存储过程的支持。我们引入了@Procedure用于在存储库方法上声明存储过程元数据的注释。

要遵循的示例使用以下过程:

例82. plus1inoutHSQL DB中程序的定义。

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-sql"><span style="color:#666600">/;</span><span style="color:#000000">
DROP procedure IF EXISTS plus1inout
</span><span style="color:#666600">/;</span><span style="color:#000000">
CREATE procedure plus1inout </span><span style="color:#666600">(</span><span style="color:#000000">IN arg </span><span style="color:#000088">int</span><span style="color:#666600">,</span><span style="color:#000000"> OUT res </span><span style="color:#000088">int</span><span style="color:#666600">)</span>
<span style="color:#000088">BEGIN</span><span style="color:#000000"> ATOMIC
 </span><span style="color:#000088">set</span><span style="color:#000000"> res </span><span style="color:#666600">=</span><span style="color:#000000"> arg </span><span style="color:#666600">+</span> <span style="color:#006666">1</span><span style="color:#666600">;</span>
<span style="color:#000088">END</span>
<span style="color:#666600">/;</span></code></span></span>

可以使用NamedStoredProcedureQuery实体类型上的注释来配置存储过程的元数据。

例子83.实体上的StoredProcedure元数据定义。

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Entity</span>
<span style="color:#006666">@NamedStoredProcedureQuery</span><span style="color:#666600">(</span><span style="color:#000000">name </span><span style="color:#666600">=</span> <span style="color:#008800">"User.plus1"</span><span style="color:#666600">,</span><span style="color:#000000"> procedureName </span><span style="color:#666600">=</span> <span style="color:#008800">"plus1inout"</span><span style="color:#666600">,</span><span style="color:#000000"> parameters </span><span style="color:#666600">=</span> <span style="color:#666600">{</span>
  <span style="color:#006666">@StoredProcedureParameter</span><span style="color:#666600">(</span><span style="color:#000000">mode </span><span style="color:#666600">=</span> <span style="color:#660066">ParameterMode</span><span style="color:#666600">.</span><span style="color:#000000">IN</span><span style="color:#666600">,</span><span style="color:#000000"> name </span><span style="color:#666600">=</span> <span style="color:#008800">"arg"</span><span style="color:#666600">,</span><span style="color:#000000"> type </span><span style="color:#666600">=</span> <span style="color:#660066">Integer</span><span style="color:#666600">.</span><span style="color:#000088">class</span><span style="color:#666600">),</span>
  <span style="color:#006666">@StoredProcedureParameter</span><span style="color:#666600">(</span><span style="color:#000000">mode </span><span style="color:#666600">=</span> <span style="color:#660066">ParameterMode</span><span style="color:#666600">.</span><span style="color:#000000">OUT</span><span style="color:#666600">,</span><span style="color:#000000"> name </span><span style="color:#666600">=</span> <span style="color:#008800">"res"</span><span style="color:#666600">,</span><span style="color:#000000"> type </span><span style="color:#666600">=</span> <span style="color:#660066">Integer</span><span style="color:#666600">.</span><span style="color:#000088">class</span><span style="color:#666600">)</span> <span style="color:#666600">})</span>
<span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#660066">User</span> <span style="color:#666600">{}</span></code></span></span>

您可以通过多种方式从存储库方法引用存储过程。要调用的存储过程可以使用注释的valueor procedureName属性直接定义,也可以使用属性@Procedure间接定义name。如果未配置名称,则将使用存储库方法的名称作为回退。

以下示例显示如何引用显式映射过程:

例子84.在数据库中引用名为“plus1inout”的显式映射过程。

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Procedure</span><span style="color:#666600">(</span><span style="color:#008800">"plus1inout"</span><span style="color:#666600">)</span>
<span style="color:#660066">Integer</span><span style="color:#000000"> explicitlyNamedPlus1inout</span><span style="color:#666600">(</span><span style="color:#660066">Integer</span><span style="color:#000000"> arg</span><span style="color:#666600">);</span></code></span></span>

以下示例显示如何使用procedureName别名引用隐式映射过程:

例子85.通过procedureName别名在数据库中引用名为“plus1inout”的隐式映射过程。

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Procedure</span><span style="color:#666600">(</span><span style="color:#000000">procedureName </span><span style="color:#666600">=</span> <span style="color:#008800">"plus1inout"</span><span style="color:#666600">)</span>
<span style="color:#660066">Integer</span><span style="color:#000000"> plus1inout</span><span style="color:#666600">(</span><span style="color:#660066">Integer</span><span style="color:#000000"> arg</span><span style="color:#666600">);</span></code></span></span>

以下示例显示如何在以下位置引用显式映射的命名过程EntityManager

例86.引用显式映射的命名存储过程“User.plus1IO” EntityManager

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Procedure</span><span style="color:#666600">(</span><span style="color:#000000">name </span><span style="color:#666600">=</span> <span style="color:#008800">"User.plus1IO"</span><span style="color:#666600">)</span>
<span style="color:#660066">Integer</span><span style="color:#000000"> entityAnnotatedCustomNamedProcedurePlus1IO</span><span style="color:#666600">(</span><span style="color:#006666">@Param</span><span style="color:#666600">(</span><span style="color:#008800">"arg"</span><span style="color:#666600">)</span> <span style="color:#660066">Integer</span><span style="color:#000000"> arg</span><span style="color:#666600">);</span></code></span></span>

以下示例说明如何EntityManager使用方法名称引用隐式命名的存储过程:

示例87. EntityManager使用方法名称引用隐式映射的命名存储过程“User.plus1” 。

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Procedure</span>
<span style="color:#660066">Integer</span><span style="color:#000000"> plus1</span><span style="color:#666600">(</span><span style="color:#006666">@Param</span><span style="color:#666600">(</span><span style="color:#008800">"arg"</span><span style="color:#666600">)</span> <span style="color:#660066">Integer</span><span style="color:#000000"> arg</span><span style="color:#666600">);</span></code></span></span>

5.5。产品规格

JPA 2引入了一个标准API,您可以使用它以编程方式构建查询。通过编写a criteria,可以为域类定义查询的where子句。再退一步,可以将这些标准视为JPA标准API约束描述的实体的谓词。

Spring Data JPA采用Eric Evans的书“Domain Driven Design”中的规范概念,遵循相同的语义,并提供API以使用JPA标准API定义此类规范。要支持规范,可以使用JpaSpecificationExecutor界面扩展存储库界面,如下所示:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">CustomerRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">Customer</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">>,</span> <span style="color:#660066">JpaSpecificationExecutor</span> <span style="color:#666600">{</span>
 <span style="color:#666600">…</span>
<span style="color:#666600">}</span></code></span></span>

附加接口具有允许您以各种方式执行规范的方法。例如,该findAll方法返回与规范匹配的所有实体,如以下示例所示:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> findAll</span><span style="color:#666600">(</span><span style="color:#660066">Specification</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> spec</span><span style="color:#666600">);</span></code></span></span>

Specification接口被定义为如下:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">Specification</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span> <span style="color:#666600">{</span>
  <span style="color:#660066">Predicate</span><span style="color:#000000"> toPredicate</span><span style="color:#666600">(</span><span style="color:#660066">Root</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> root</span><span style="color:#666600">,</span> <span style="color:#660066">CriteriaQuery</span><span style="color:#666600"><?></span><span style="color:#000000"> query</span><span style="color:#666600">,</span>
            <span style="color:#660066">CriteriaBuilder</span><span style="color:#000000"> builder</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

可以轻松地使用规范在实体之上构建可扩展的谓词集,然后可以将其组合和使用,JpaRepository而无需为每个所需组合声明查询(方法),如以下示例所示:

例子88.顾客的规格

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#660066">CustomerSpecs</span> <span style="color:#666600">{</span>

  <span style="color:#000088">public</span> <span style="color:#000088">static</span> <span style="color:#660066">Specification</span><span style="color:#666600"><</span><span style="color:#660066">Customer</span><span style="color:#666600">></span><span style="color:#000000"> isLongTermCustomer</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
    <span style="color:#000088">return</span> <span style="color:#000088">new</span> <span style="color:#660066">Specification</span><span style="color:#666600"><</span><span style="color:#660066">Customer</span><span style="color:#666600">>()</span> <span style="color:#666600">{</span>
      <span style="color:#000088">public</span> <span style="color:#660066">Predicate</span><span style="color:#000000"> toPredicate</span><span style="color:#666600">(</span><span style="color:#660066">Root</span><span style="color:#666600"><</span><span style="color:#660066">Customer</span><span style="color:#666600">></span><span style="color:#000000"> root</span><span style="color:#666600">,</span> <span style="color:#660066">CriteriaQuery</span><span style="color:#666600"><?></span><span style="color:#000000"> query</span><span style="color:#666600">,</span>
            <span style="color:#660066">CriteriaBuilder</span><span style="color:#000000"> builder</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>

         <span style="color:#660066">LocalDate</span><span style="color:#000000"> date </span><span style="color:#666600">=</span> <span style="color:#000088">new</span> <span style="color:#660066">LocalDate</span><span style="color:#666600">().</span><span style="color:#000000">minusYears</span><span style="color:#666600">(</span><span style="color:#006666">2</span><span style="color:#666600">);</span>
         <span style="color:#000088">return</span><span style="color:#000000"> builder</span><span style="color:#666600">.</span><span style="color:#000000">lessThan</span><span style="color:#666600">(</span><span style="color:#000000">root</span><span style="color:#666600">.</span><span style="color:#000000">get</span><span style="color:#666600">(</span><span style="color:#660066">_Customer</span><span style="color:#666600">.</span><span style="color:#000000">createdAt</span><span style="color:#666600">),</span><span style="color:#000000"> date</span><span style="color:#666600">);</span>
      <span style="color:#666600">}</span>
    <span style="color:#666600">};</span>
  <span style="color:#666600">}</span>

  <span style="color:#000088">public</span> <span style="color:#000088">static</span> <span style="color:#660066">Specification</span><span style="color:#666600"><</span><span style="color:#660066">Customer</span><span style="color:#666600">></span><span style="color:#000000"> hasSalesOfMoreThan</span><span style="color:#666600">(</span><span style="color:#660066">MontaryAmount</span><span style="color:#000000"> value</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#000088">return</span> <span style="color:#000088">new</span> <span style="color:#660066">Specification</span><span style="color:#666600"><</span><span style="color:#660066">Customer</span><span style="color:#666600">>()</span> <span style="color:#666600">{</span>
      <span style="color:#000088">public</span> <span style="color:#660066">Predicate</span><span style="color:#000000"> toPredicate</span><span style="color:#666600">(</span><span style="color:#660066">Root</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span><span style="color:#000000"> root</span><span style="color:#666600">,</span> <span style="color:#660066">CriteriaQuery</span><span style="color:#666600"><?></span><span style="color:#000000"> query</span><span style="color:#666600">,</span>
            <span style="color:#660066">CriteriaBuilder</span><span style="color:#000000"> builder</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>

         <span style="color:#880000">// build query here</span>
      <span style="color:#666600">}</span>
    <span style="color:#666600">};</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

不可否认,样板的数量留下了改进的空间(最终可能会被Java 8关闭所减少),但客户端变得更好,正如您将在本节后面看到的那样。该_Customer类型是使用JPA Metamodel生成器生成的元模型类型(有关示例,请参阅Hibernate实现的文档)。所以表达式_Customer.createdAt假设Customer有一个createdAt类型的属性Date。除此之外,我们已经在业务需求抽象级别上表达了一些标准并创建了可执行文件Specifications。所以客户端可能会使用Specification如下:

例89.使用简单的规范

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Customer</span><span style="color:#666600">></span><span style="color:#000000"> customers </span><span style="color:#666600">=</span><span style="color:#000000"> customerRepository</span><span style="color:#666600">.</span><span style="color:#000000">findAll</span><span style="color:#666600">(</span><span style="color:#000000">isLongTermCustomer</span><span style="color:#666600">());</span></code></span></span>

为什么不为这种数据访问创建查询?使用单个文件Specification并不比普通的查询声明获得很多好处。当您将它们组合起来创建新Specification对象时,规范的力量确实很闪耀。您可以通过Specifications我们提供的帮助程序类来实现此目的,以构建类似于以下内容的表达式:

实施例90.组合规格

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">MonetaryAmount</span><span style="color:#000000"> amount </span><span style="color:#666600">=</span> <span style="color:#000088">new</span> <span style="color:#660066">MonetaryAmount</span><span style="color:#666600">(</span><span style="color:#006666">200.0</span><span style="color:#666600">,</span> <span style="color:#660066">Currencies</span><span style="color:#666600">.</span><span style="color:#000000">DOLLAR</span><span style="color:#666600">);</span>
<span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Customer</span><span style="color:#666600">></span><span style="color:#000000"> customers </span><span style="color:#666600">=</span><span style="color:#000000"> customerRepository</span><span style="color:#666600">.</span><span style="color:#000000">findAll</span><span style="color:#666600">(</span><span style="color:#000000">
  where</span><span style="color:#666600">(</span><span style="color:#000000">isLongTermCustomer</span><span style="color:#666600">()).</span><span style="color:#000000">or</span><span style="color:#666600">(</span><span style="color:#000000">hasSalesOfMoreThan</span><span style="color:#666600">(</span><span style="color:#000000">amount</span><span style="color:#666600">)));</span></code></span></span>

Specifications提供了一些链接和组合Specification实例的“胶水代码”方法。这些方法允许您通过创建新Specification实现并将它们与现有实现相结合来扩展数据访问层。

5.6。按示例查询

5.6.1。介绍

本章介绍Query by Example并解释如何使用它。

按示例查询(QBE)是一种用户友好的查询技术,具有简单的界面。它允许动态创建查询,并且不需要您编写包含字段名称的查询。实际上,Query by Example不要求您使用特定于商店的查询语言来编写查询。

5.6.2。用法

Query by Example API由三部分组成:

  • 探测器:具有填充字段的域对象的实际示例。

  • ExampleMatcherExampleMatcher有关如何匹配特定字段的详细信息。它可以在多个示例中重用。

  • Example:一个Example由探针和ExampleMatcher。它用于创建查询。

按示例查询非常适合几种用例:

  • 使用一组静态或动态约束查询数据存储。

  • 频繁重构域对象,而不必担心破坏现有查询。

  • 独立于底层数据存储API工作。

按示例查询也有几个限制:

  • 不支持嵌套或分组的属性约束,例如firstname = ?0 or (firstname = ?1 and lastname = ?2)

  • 仅支持字符串的开始/包含/结束/正则表达式匹配以及其他属性类型的精确匹配。

在开始使用Query by Example之前,您需要拥有一个域对象。首先,为存储库创建一个接口,如以下示例所示:

例91.示例Person对象

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#660066">Person</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Id</span>
  <span style="color:#000088">private</span> <span style="color:#660066">String</span><span style="color:#000000"> id</span><span style="color:#666600">;</span>
  <span style="color:#000088">private</span> <span style="color:#660066">String</span><span style="color:#000000"> firstname</span><span style="color:#666600">;</span>
  <span style="color:#000088">private</span> <span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">;</span>
  <span style="color:#000088">private</span> <span style="color:#660066">Address</span><span style="color:#000000"> address</span><span style="color:#666600">;</span>

  <span style="color:#880000">// … getters and setters omitted</span>
<span style="color:#666600">}</span></code></span></span>

前面的示例显示了一个简单的域对象。你可以用它来创建一个Example。默认情况下,null将忽略具有值的字段,并使用特定于商店的默认值匹配字符串。可以使用of工厂方法或使用来构建示例ExampleMatcherExample是不可改变的。以下清单显示了一个简单的示例:

例92.简单例子

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">Person</span><span style="color:#000000"> person </span><span style="color:#666600">=</span> <span style="color:#000088">new</span> <span style="color:#660066">Person</span><span style="color:#666600">();</span>                         <span style="color:#000000">
person</span><span style="color:#666600">.</span><span style="color:#000000">setFirstname</span><span style="color:#666600">(</span><span style="color:#008800">"Dave"</span><span style="color:#666600">);</span>                          

<span style="color:#660066">Example</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> example </span><span style="color:#666600">=</span> <span style="color:#660066">Example</span><span style="color:#666600">.</span><span style="color:#000000">of</span><span style="color:#666600">(</span><span style="color:#000000">person</span><span style="color:#666600">);</span>         </code></span></span>
 创建域对象的新实例。
 设置要查询的属性。
 创建Example

理想情况下,使用存储库执行示例。为此,请让您的存储库接口扩展QueryByExampleExecutor<T>。以下清单显示了QueryByExampleExecutor界面的摘录:

例93. QueryByExampleExecutor

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">QueryByExampleExecutor</span><span style="color:#666600"><</span><span style="color:#000000">T</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#666600"><</span><span style="color:#000000">S </span><span style="color:#000088">extends</span><span style="color:#000000"> T</span><span style="color:#666600">></span><span style="color:#000000"> S findOne</span><span style="color:#666600">(</span><span style="color:#660066">Example</span><span style="color:#666600"><</span><span style="color:#000000">S</span><span style="color:#666600">></span><span style="color:#000000"> example</span><span style="color:#666600">);</span>

  <span style="color:#666600"><</span><span style="color:#000000">S </span><span style="color:#000088">extends</span><span style="color:#000000"> T</span><span style="color:#666600">></span> <span style="color:#660066">Iterable</span><span style="color:#666600"><</span><span style="color:#000000">S</span><span style="color:#666600">></span><span style="color:#000000"> findAll</span><span style="color:#666600">(</span><span style="color:#660066">Example</span><span style="color:#666600"><</span><span style="color:#000000">S</span><span style="color:#666600">></span><span style="color:#000000"> example</span><span style="color:#666600">);</span>

  <span style="color:#880000">// … more functionality omitted.</span>
<span style="color:#666600">}</span></code></span></span>

5.6.3。示例匹配器

示例不限于默认设置。您可以使用,为字符串匹配,空值处理和特定于属性的设置指定自己的默认值ExampleMatcher,如以下示例所示:

例94.具有自定义匹配的示例匹配器

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">Person</span><span style="color:#000000"> person </span><span style="color:#666600">=</span> <span style="color:#000088">new</span> <span style="color:#660066">Person</span><span style="color:#666600">();</span>                          <span style="color:#000000">
person</span><span style="color:#666600">.</span><span style="color:#000000">setFirstname</span><span style="color:#666600">(</span><span style="color:#008800">"Dave"</span><span style="color:#666600">);</span>                           

<span style="color:#660066">ExampleMatcher</span><span style="color:#000000"> matcher </span><span style="color:#666600">=</span> <span style="color:#660066">ExampleMatcher</span><span style="color:#666600">.</span><span style="color:#000000">matching</span><span style="color:#666600">()</span>     
  <span style="color:#666600">.</span><span style="color:#000000">withIgnorePaths</span><span style="color:#666600">(</span><span style="color:#008800">"lastname"</span><span style="color:#666600">)</span>                         
  <span style="color:#666600">.</span><span style="color:#000000">withIncludeNullValues</span><span style="color:#666600">()</span>                             
  <span style="color:#666600">.</span><span style="color:#000000">withStringMatcherEnding</span><span style="color:#666600">();</span>                          

<span style="color:#660066">Example</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> example </span><span style="color:#666600">=</span> <span style="color:#660066">Example</span><span style="color:#666600">.</span><span style="color:#000000">of</span><span style="color:#666600">(</span><span style="color:#000000">person</span><span style="color:#666600">,</span><span style="color:#000000"> matcher</span><span style="color:#666600">);</span> </code></span></span>
 创建域对象的新实例。
 设置属性。
 创建一个ExampleMatcher以期望所有值匹配。即使没有进一步配置,它也可以在这个阶段使用。
 构造一个新的ExampleMatcher以忽略lastname属性路径。
 构造一个new ExampleMatcher来忽略lastname属性路径并包含空值。
 构造一个new ExampleMatcher来忽略lastname属性路径,包含空值,并执行后缀字符串匹配。
 Example根据域对象和配置创建新的ExampleMatcher

默认情况下,ExampleMatcher期望探针上设置的所有值都匹配。如果要获得与隐式定义的任何谓词匹配的结果,请使用ExampleMatcher.matchingAny()

您可以为单个属性指定行为(例如“firstname”和“lastname”,或者对于嵌套属性,“address.city”)。您可以使用匹配选项和区分大小写来调整它,如以下示例所示:

例子95.配置匹配器选项

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">ExampleMatcher</span><span style="color:#000000"> matcher </span><span style="color:#666600">=</span> <span style="color:#660066">ExampleMatcher</span><span style="color:#666600">.</span><span style="color:#000000">matching</span><span style="color:#666600">()</span>
  <span style="color:#666600">.</span><span style="color:#000000">withMatcher</span><span style="color:#666600">(</span><span style="color:#008800">"firstname"</span><span style="color:#666600">,</span><span style="color:#000000"> endsWith</span><span style="color:#666600">())</span>
  <span style="color:#666600">.</span><span style="color:#000000">withMatcher</span><span style="color:#666600">(</span><span style="color:#008800">"lastname"</span><span style="color:#666600">,</span><span style="color:#000000"> startsWith</span><span style="color:#666600">().</span><span style="color:#000000">ignoreCase</span><span style="color:#666600">());</span>
<span style="color:#666600">}</span></code></span></span>

配置matcher选项的另一种方法是使用lambda(在Java 8中引入)。此方法创建一个回调,要求实现者修改匹配器。您无需返回匹配器,因为配置选项保存在匹配器实例中。以下示例显示了使用lambdas的匹配器:

例子96.使用lambdas配置matcher选项

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#660066">ExampleMatcher</span><span style="color:#000000"> matcher </span><span style="color:#666600">=</span> <span style="color:#660066">ExampleMatcher</span><span style="color:#666600">.</span><span style="color:#000000">matching</span><span style="color:#666600">()</span>
  <span style="color:#666600">.</span><span style="color:#000000">withMatcher</span><span style="color:#666600">(</span><span style="color:#008800">"firstname"</span><span style="color:#666600">,</span><span style="color:#000000"> match </span><span style="color:#666600">-></span><span style="color:#000000"> match</span><span style="color:#666600">.</span><span style="color:#000000">endsWith</span><span style="color:#666600">())</span>
  <span style="color:#666600">.</span><span style="color:#000000">withMatcher</span><span style="color:#666600">(</span><span style="color:#008800">"firstname"</span><span style="color:#666600">,</span><span style="color:#000000"> match </span><span style="color:#666600">-></span><span style="color:#000000"> match</span><span style="color:#666600">.</span><span style="color:#000000">startsWith</span><span style="color:#666600">());</span>
<span style="color:#666600">}</span></code></span></span>

通过Example使用配置的合并视图创建的查询。可以在ExampleMatcher级别设置默认匹配设置,而可以将单个设置应用于特定属性路径。已设置上的设置ExampleMatcher由属性路径设置继承,除非它们被明确定义。属性修补程序上的设置优先于默认设置。下表描述了各种ExampleMatcher设置的范围:

表4. ExampleMatcher设置范围
设置范围

空操作

ExampleMatcher

字符串匹配

ExampleMatcher 和财产路径

忽略属性

物业路径

区分大小写

ExampleMatcher 和财产路径

价值转变

物业路径

5.6.4。执行一个例子

在Spring Data JPA中,您可以使用Query with Example with Repositories,如以下示例所示:

例97.使用存储库按示例查询

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">PersonRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">,</span> <span style="color:#660066">String</span><span style="color:#666600">></span> <span style="color:#666600">{</span> <span style="color:#666600">…</span> <span style="color:#666600">}</span>

<span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#660066">PersonService</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Autowired</span> <span style="color:#660066">PersonRepository</span><span style="color:#000000"> personRepository</span><span style="color:#666600">;</span>

  <span style="color:#000088">public</span> <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> findPeople</span><span style="color:#666600">(</span><span style="color:#660066">Person</span><span style="color:#000000"> probe</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#000088">return</span><span style="color:#000000"> personRepository</span><span style="color:#666600">.</span><span style="color:#000000">findAll</span><span style="color:#666600">(</span><span style="color:#660066">Example</span><span style="color:#666600">.</span><span style="color:#000000">of</span><span style="color:#666600">(</span><span style="color:#000000">probe</span><span style="color:#666600">));</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>
 目前,只有SingularAttribute属性可用于属性匹配。

属性说明符接受属性名称(例如firstnamelastname)。您可以通过将属性与点(address.city)一起链接来导航。您还可以使用匹配选项和区分大小写来调整它。

下表显示了StringMatcher可以使用的各种选项以及在名为的字段上使用它们的结果firstname

表5. StringMatcher选项
匹配逻辑结果

DEFAULT (区分大小写)

firstname = ?0

DEFAULT (不区分大小写)

LOWER(firstname) = LOWER(?0)

EXACT (区分大小写)

firstname = ?0

EXACT (不区分大小写)

LOWER(firstname) = LOWER(?0)

STARTING (区分大小写)

firstname like ?0 + '%'

STARTING (不区分大小写)

LOWER(firstname) like LOWER(?0) + '%'

ENDING (区分大小写)

firstname like '%' + ?0

ENDING (不区分大小写)

LOWER(firstname) like '%' + LOWER(?0)

CONTAINING (区分大小写)

firstname like '%' + ?0 + '%'

CONTAINING (不区分大小写)

LOWER(firstname) like '%' + LOWER(?0) + '%'

5.7。事务性

默认情况下,存储库实例上的CRUD方法是事务性的。对于读取操作,事务配置readOnly标志设置为true。所有其他配置都使用plain,@Transactional以便应用默认事务配置。有关详细信息,请参阅JavaDoc SimpleJpaRepository。如果需要为存储库中声明的方法之一调整事务配置,请重新声明存储库接口中的方法,如下所示:

例子98. CRUD的自定义事务配置

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">CrudRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Override</span>
  <span style="color:#006666">@Transactional</span><span style="color:#666600">(</span><span style="color:#000000">timeout </span><span style="color:#666600">=</span> <span style="color:#006666">10</span><span style="color:#666600">)</span>
  <span style="color:#000088">public</span> <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findAll</span><span style="color:#666600">();</span>

  <span style="color:#880000">// Further query method declarations</span>
<span style="color:#666600">}</span></code></span></span>

这样做会导致该findAll()方法以10秒的超时运行且没有readOnly标志。

更改事务行为的另一种方法是使用(通常)覆盖多个存储库的外观或服务实现。其目的是为非CRUD操作定义事务边界。以下示例显示如何将此类Facade用于多个存储库:

例子99.使用facade来定义多个存储库调用的事务

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Service</span>
<span style="color:#000088">class</span> <span style="color:#660066">UserManagementImpl</span> <span style="color:#000088">implements</span> <span style="color:#660066">UserManagement</span> <span style="color:#666600">{</span>

  <span style="color:#000088">private</span> <span style="color:#000088">final</span> <span style="color:#660066">UserRepository</span><span style="color:#000000"> userRepository</span><span style="color:#666600">;</span>
  <span style="color:#000088">private</span> <span style="color:#000088">final</span> <span style="color:#660066">RoleRepository</span><span style="color:#000000"> roleRepository</span><span style="color:#666600">;</span>

  <span style="color:#006666">@Autowired</span>
  <span style="color:#000088">public</span> <span style="color:#660066">UserManagementImpl</span><span style="color:#666600">(</span><span style="color:#660066">UserRepository</span><span style="color:#000000"> userRepository</span><span style="color:#666600">,</span>
    <span style="color:#660066">RoleRepository</span><span style="color:#000000"> roleRepository</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#000088">this</span><span style="color:#666600">.</span><span style="color:#000000">userRepository </span><span style="color:#666600">=</span><span style="color:#000000"> userRepository</span><span style="color:#666600">;</span>
    <span style="color:#000088">this</span><span style="color:#666600">.</span><span style="color:#000000">roleRepository </span><span style="color:#666600">=</span><span style="color:#000000"> roleRepository</span><span style="color:#666600">;</span>
  <span style="color:#666600">}</span>

  <span style="color:#006666">@Transactional</span>
  <span style="color:#000088">public</span> <span style="color:#000088">void</span><span style="color:#000000"> addRoleToAllUsers</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> roleName</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>

    <span style="color:#660066">Role</span><span style="color:#000000"> role </span><span style="color:#666600">=</span><span style="color:#000000"> roleRepository</span><span style="color:#666600">.</span><span style="color:#000000">findByName</span><span style="color:#666600">(</span><span style="color:#000000">roleName</span><span style="color:#666600">);</span>

    <span style="color:#000088">for</span> <span style="color:#666600">(</span><span style="color:#660066">User</span><span style="color:#000000"> user </span><span style="color:#666600">:</span><span style="color:#000000"> userRepository</span><span style="color:#666600">.</span><span style="color:#000000">findAll</span><span style="color:#666600">())</span> <span style="color:#666600">{</span><span style="color:#000000">
      user</span><span style="color:#666600">.</span><span style="color:#000000">addRole</span><span style="color:#666600">(</span><span style="color:#000000">role</span><span style="color:#666600">);</span><span style="color:#000000">
      userRepository</span><span style="color:#666600">.</span><span style="color:#000000">save</span><span style="color:#666600">(</span><span style="color:#000000">user</span><span style="color:#666600">);</span>
    <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

此示例使调用addRoleToAllUsers(…)在事务内部运行(参与现有事务或创建新事务(如果尚未运行))。然后忽略存储库中的事务配置,因为外部事务配置确定所使用的实际配置。请注意,必须激活<tx:annotation-driven />@EnableTransactionManagement明确使用才能使基于注释的外观配置起作用。此示例假定您使用组件扫描。

5.7.1。事务性查询方法

要让查询方法成为事务性的,请@Transactional在您定义的存储库接口中使用,如以下示例所示:

示例100.在查询方法中使用@Transactional

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Transactional</span><span style="color:#666600">(</span><span style="color:#000000">readOnly </span><span style="color:#666600">=</span> <span style="color:#000088">true</span><span style="color:#666600">)</span>
<span style="color:#000088">public</span> <span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">JpaRepository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>

  <span style="color:#006666">@Modifying</span>
  <span style="color:#006666">@Transactional</span>
  <span style="color:#006666">@Query</span><span style="color:#666600">(</span><span style="color:#008800">"delete from User u where u.active = false"</span><span style="color:#666600">)</span>
  <span style="color:#000088">void</span><span style="color:#000000"> deleteInactiveUsers</span><span style="color:#666600">();</span>
<span style="color:#666600">}</span></code></span></span>

通常,您希望将readOnly标志设置为true,因为大多数查询方法只读取数据。与此相反,deleteInactiveUsers()使用@Modifying注释并覆盖事务配置。因此,该方法在readOnly标志设置为的情况下运行false

 

您可以将事务用于只读查询,并通过设置readOnly标志来标记它们。这样做不会,不过,充当支票,你不会触发操纵查询(尽管某些数据库拒绝INSERTUPDATE只读事务中的语句)。readOnly而是将该标志作为提示传播到底层JDBC驱动程序以进行性能优化。此外,Spring对底层JPA提供程序执行了一些优化。例如,当与Hibernate一起使用时,刷新模式设置为NEVER当您将事务配置为时readOnly,这会导致Hibernate跳过脏检查(对大型对象树的显着改进)。

5.8。锁定

要指定要使用的锁定模式,可以@Lock在查询方法上使用注释,如以下示例所示:

例101.在查询方法上定义锁元数据

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#880000">// Plain query method</span>
  <span style="color:#006666">@Lock</span><span style="color:#666600">(</span><span style="color:#660066">LockModeType</span><span style="color:#666600">.</span><span style="color:#000000">READ</span><span style="color:#666600">)</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findByLastname</span><span style="color:#666600">(</span><span style="color:#660066">String</span><span style="color:#000000"> lastname</span><span style="color:#666600">);</span>
<span style="color:#666600">}</span></code></span></span>

这个方法声明将导致触发查询中配备了LockModeTypeREAD。您还可以通过在存储库界面中重新声明CRUD方法并添加@Lock注释来定义CRUD方法的锁定,如以下示例所示:

例子102.在CRUD方法上定义锁元数据

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">interface</span> <span style="color:#660066">UserRepository</span> <span style="color:#000088">extends</span> <span style="color:#660066">Repository</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">,</span> <span style="color:#660066">Long</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#880000">// Redeclaration of a CRUD method</span>
  <span style="color:#006666">@Lock</span><span style="color:#666600">(</span><span style="color:#660066">LockModeType</span><span style="color:#666600">.</span><span style="color:#000000">READ</span><span style="color:#666600">);</span>
  <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span><span style="color:#000000"> findAll</span><span style="color:#666600">();</span>
<span style="color:#666600">}</span></code></span></span>

5.9。审计

5.9.1。基本

Spring Data提供了复杂的支持,可以透明地跟踪创建或更改实体的人员以及更改发生的时间。要从该功能中受益,您必须为您的实体类配备审计元数据,该元数据可以使用注释或通过实现接口来定义。

基于注释的审计元数据

我们提供@CreatedBy@LastModifiedBy捕捉谁创建或修改的实体以及用户@CreatedDate@LastModifiedDate捕捉时的变化发生了。

例子103.一个被审计的实体

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">Customer</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@CreatedBy</span>
  <span style="color:#000088">private</span> <span style="color:#660066">User</span><span style="color:#000000"> user</span><span style="color:#666600">;</span>

  <span style="color:#006666">@CreatedDate</span>
  <span style="color:#000088">private</span> <span style="color:#660066">DateTime</span><span style="color:#000000"> createdDate</span><span style="color:#666600">;</span>

  <span style="color:#880000">// … further properties omitted</span>
<span style="color:#666600">}</span></code></span></span>

如您所见,可以有选择地应用注释,具体取决于您要捕获的信息。更改时间捕捉注释可以在类型乔达,时间,性质使用DateTime,遗留Java DateCalendar,JDK8日期和时间类型,以及longLong

基于接口的审计元数据

如果您不想使用注释来定义审核元数据,可以让您的域类实现该Auditable接口。它公开了所有审计属性的setter方法。

还有一个便利基类,AbstractAuditable您可以扩展它以避免手动实现接口方法。这样做会增加域类与Spring Data的耦合,这可能是您想要避免的。通常,基于注释的定义审计元数据的方式是优选的,因为它具有较小的侵入性和更灵活性。

AuditorAware

如果你使用@CreatedBy或者@LastModifiedBy,审计基础设施需要知道当前的主体。为此,我们提供了一个AuditorAware<T>SPI接口,您必须实现该接口,以告知基础架构当前用户或系统与应用程序交互的人员。泛型类型T定义了带注释@CreatedBy@LastModifiedBy必须注释的属性的类型。

以下示例显示了使用Spring Security Authentication对象的接口的实现:

例子104.基于Spring Security的AuditorAware的实现

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">SpringSecurityAuditorAware</span> <span style="color:#000088">implements</span> <span style="color:#660066">AuditorAware</span><span style="color:#666600"><</span><span style="color:#660066">User</span><span style="color:#666600">></span> <span style="color:#666600">{</span>

  <span style="color:#000088">public</span> <span style="color:#660066">User</span><span style="color:#000000"> getCurrentAuditor</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>

    <span style="color:#660066">Authentication</span><span style="color:#000000"> authentication </span><span style="color:#666600">=</span> <span style="color:#660066">SecurityContextHolder</span><span style="color:#666600">.</span><span style="color:#000000">getContext</span><span style="color:#666600">().</span><span style="color:#000000">getAuthentication</span><span style="color:#666600">();</span>

    <span style="color:#000088">if</span> <span style="color:#666600">(</span><span style="color:#000000">authentication </span><span style="color:#666600">==</span> <span style="color:#000088">null</span> <span style="color:#666600">||</span> <span style="color:#666600">!</span><span style="color:#000000">authentication</span><span style="color:#666600">.</span><span style="color:#000000">isAuthenticated</span><span style="color:#666600">())</span> <span style="color:#666600">{</span>
      <span style="color:#000088">return</span> <span style="color:#000088">null</span><span style="color:#666600">;</span>
    <span style="color:#666600">}</span>

    <span style="color:#000088">return</span> <span style="color:#666600">((</span><span style="color:#660066">MyUserDetails</span><span style="color:#666600">)</span><span style="color:#000000"> authentication</span><span style="color:#666600">.</span><span style="color:#000000">getPrincipal</span><span style="color:#666600">()).</span><span style="color:#000000">getUser</span><span style="color:#666600">();</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

该实现访问AuthenticationSpring Security提供的对象,并查找UserDetails您在UserDetailsService实现中创建的自定义实例。我们在此假设您通过UserDetails实现公开域用户,但根据Authentication找到的,您也可以从任何地方查找它。:leveloffset:-1

5.9.2。JPA审计

一般审计配置

Spring Data JPA附带了一个实体监听器,可用于触发审计信息的捕获。首先,您必须注册AuditingEntityListener要用于orm.xml文件中持久性上下文中的所有实体,如以下示例所示:

例105.审计配置orm.xml

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><persistence-unit-metadata></span>
  <span style="color:#000088"><persistence-unit-defaults></span>
    <span style="color:#000088"><entity-listeners></span>
      <span style="color:#000088"><entity-listener</span> <span style="color:#660066">class</span><span style="color:#666600">=</span><span style="color:#008800">"….data.jpa.domain.support.AuditingEntityListener"</span> <span style="color:#000088">/></span>
    <span style="color:#000088"></entity-listeners></span>
  <span style="color:#000088"></persistence-unit-defaults></span>
<span style="color:#000088"></persistence-unit-metadata></span></code></span></span>

您还可以AuditingEntityListener使用@EntityListeners注释在每个实体的基础上启用,如下所示:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Entity</span>
<span style="color:#006666">@EntityListeners</span><span style="color:#666600">(</span><span style="color:#660066">AuditingEntityListener</span><span style="color:#666600">.</span><span style="color:#000088">class</span><span style="color:#666600">)</span>
<span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#660066">MyEntity</span> <span style="color:#666600">{</span>

<span style="color:#666600">}</span></code></span></span>
 审核功能需要spring-aspects.jar在类路径上。

通过orm.xml适当修改并spring-aspects.jar在类路径上,激活审计功能是将Spring Data JPA auditing命名空间元素添加到配置中,如下所示:

例子106.使用XML配置激活审计

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><jpa:auditing</span> <span style="color:#660066">auditor-aware-ref</span><span style="color:#666600">=</span><span style="color:#008800">"yourAuditorAwareBean"</span> <span style="color:#000088">/></span></code></span></span>

As of Spring Data JPA 1.5, you can enable auditing by annotating a configuration class with the @EnableJpaAuditingannotation. You must still modify the orm.xml file and have spring-aspects.jar on the classpath. The following example shows how to use the @EnableJpaAuditing annotation:

Example 107. Activating auditing with Java configuration

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#006666">@Configuration</span>
<span style="color:#006666">@EnableJpaAuditing</span>
<span style="color:#000088">class</span> <span style="color:#660066">Config</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Bean</span>
  <span style="color:#000088">public</span> <span style="color:#660066">AuditorAware</span><span style="color:#666600"><</span><span style="color:#660066">AuditableUser</span><span style="color:#666600">></span><span style="color:#000000"> auditorProvider</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
    <span style="color:#000088">return</span> <span style="color:#000088">new</span> <span style="color:#660066">AuditorAwareImpl</span><span style="color:#666600">();</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

If you expose a bean of type AuditorAware to the ApplicationContext, the auditing infrastructure automatically picks it up and uses it to determine the current user to be set on domain types. If you have multiple implementations registered in the ApplicationContext, you can select the one to be used by explicitly setting the auditorAwareRef attribute of @EnableJpaAuditing.

5.10. Miscellaneous Considerations

5.10.1. Using JpaContext in Custom Implementations

When working with multiple EntityManager instances and custom repository implementations, you need to wire the correct EntityManager into the repository implementation class. You can do so by explicitly naming the EntityManager in the @PersistenceContext annotation or, if the EntityManager is @Autowired, by using @Qualifier.

As of Spring Data JPA 1.9, Spring Data JPA includes a class called JpaContext that lets you obtain the EntityManager by managed domain class, assuming it is managed by only one of the EntityManager instances in the application. The following example shows how to use JpaContext in a custom repository:

Example 108. Using JpaContext in a custom repository implementation

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">UserRepositoryImpl</span> <span style="color:#000088">implements</span> <span style="color:#660066">UserRepositoryCustom</span> <span style="color:#666600">{</span>

  <span style="color:#000088">private</span> <span style="color:#000088">final</span> <span style="color:#660066">EntityManager</span><span style="color:#000000"> em</span><span style="color:#666600">;</span>

  <span style="color:#006666">@Autowired</span>
  <span style="color:#000088">public</span> <span style="color:#660066">UserRepositoryImpl</span><span style="color:#666600">(</span><span style="color:#660066">JpaContext</span><span style="color:#000000"> context</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#000088">this</span><span style="color:#666600">.</span><span style="color:#000000">em </span><span style="color:#666600">=</span><span style="color:#000000"> context</span><span style="color:#666600">.</span><span style="color:#000000">getEntityManagerByManagedType</span><span style="color:#666600">(</span><span style="color:#660066">User</span><span style="color:#666600">.</span><span style="color:#000088">class</span><span style="color:#666600">);</span>
  <span style="color:#666600">}</span>

  <span style="color:#666600">…</span>
<span style="color:#666600">}</span></code></span></span>

此方法的优点是,如果将域类型分配给不同的持久性单元,则不必触及存储库来更改对持久性单元的引用。

5.10.2。合并持久性单元

Spring支持具有多个持久性单元。但是,有时您可能希望模块化您的应用程序,但仍然确保所有这些模块在单个持久性单元内运行。为了实现该行为,Spring Data JPA提供了一种PersistenceUnitManager实现,可以根据名称自动合并持久性单元,如以下示例所示:

例子109.使用MergingPersistenceUnitmanager

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><bean</span> <span style="color:#660066">class</span><span style="color:#666600">=</span><span style="color:#008800">"….LocalContainerEntityManagerFactoryBean"</span><span style="color:#000088">></span>
  <span style="color:#000088"><property</span> <span style="color:#660066">name</span><span style="color:#666600">=</span><span style="color:#008800">"persistenceUnitManager"</span><span style="color:#000088">></span>
    <span style="color:#000088"><bean</span> <span style="color:#660066">class</span><span style="color:#666600">=</span><span style="color:#008800">"….MergingPersistenceUnitManager"</span> <span style="color:#000088">/></span>
  <span style="color:#000088"></property></span>
<span style="color:#000088"></bean></span></code></span></span>

@Entity类和JPA映射文件的类路径扫描

普通的JPA设置需要列出所有注释映射的实体类orm.xml。这同样适用于XML映射文件。Spring Data JPA提供了ClasspathScanningPersistenceUnitPostProcessor一个配置基础包的程序,并可选择采用映射文件名模式。然后,它会扫描给定的包带注解的类@Entity或者@MappedSuperclass,加载匹配文件名模式的配置文件,并把它们递给JPA配置。后处理器必须配置如下:

例子110.使用ClasspathScanningPersistenceUnitPostProcessor

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><bean</span> <span style="color:#660066">class</span><span style="color:#666600">=</span><span style="color:#008800">"….LocalContainerEntityManagerFactoryBean"</span><span style="color:#000088">></span>
  <span style="color:#000088"><property</span> <span style="color:#660066">name</span><span style="color:#666600">=</span><span style="color:#008800">"persistenceUnitPostProcessors"</span><span style="color:#000088">></span>
    <span style="color:#000088"><list></span>
      <span style="color:#000088"><bean</span> <span style="color:#660066">class</span><span style="color:#666600">=</span><span style="color:#008800">"org.springframework.data.jpa.support.ClasspathScanningPersistenceUnitPostProcessor"</span><span style="color:#000088">></span>
        <span style="color:#000088"><constructor-arg</span> <span style="color:#660066">value</span><span style="color:#666600">=</span><span style="color:#008800">"com.acme.domain"</span> <span style="color:#000088">/></span>
        <span style="color:#000088"><property</span> <span style="color:#660066">name</span><span style="color:#666600">=</span><span style="color:#008800">"mappingFileNamePattern"</span> <span style="color:#660066">value</span><span style="color:#666600">=</span><span style="color:#008800">"**/*Mapping.xml"</span> <span style="color:#000088">/></span>
      <span style="color:#000088"></bean></span>
    <span style="color:#000088"></list></span>
  <span style="color:#000088"></property></span>
<span style="color:#000088"></bean></span></code></span></span>
 从Spring 3.1开始,可以直接配置要扫描的包LocalContainerEntityManagerFactoryBean以启用实体类的类路径扫描。有关详细信息,请参阅JavaDoc

5.10.3。CDI集成

Instances of the repository interfaces are usually created by a container, for which Spring is the most natural choice when working with Spring Data. Spring offers sophisticated support for creating bean instances, as documented in Creating Repository Instances. As of version 1.1.0, Spring Data JPA ships with a custom CDI extension that allows using the repository abstraction in CDI environments. The extension is part of the JAR. To activate it, include the Spring Data JPA JAR on your classpath.

You can now set up the infrastructure by implementing a CDI Producer for the EntityManagerFactory and EntityManager, as shown in the following example:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">EntityManagerFactoryProducer</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Produces</span>
  <span style="color:#006666">@ApplicationScoped</span>
  <span style="color:#000088">public</span> <span style="color:#660066">EntityManagerFactory</span><span style="color:#000000"> createEntityManagerFactory</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
    <span style="color:#000088">return</span> <span style="color:#660066">Persistence</span><span style="color:#666600">.</span><span style="color:#000000">createEntityManagerFactory</span><span style="color:#666600">(</span><span style="color:#008800">"my-presistence-unit"</span><span style="color:#666600">);</span>
  <span style="color:#666600">}</span>

  <span style="color:#000088">public</span> <span style="color:#000088">void</span><span style="color:#000000"> close</span><span style="color:#666600">(</span><span style="color:#006666">@Disposes</span> <span style="color:#660066">EntityManagerFactory</span><span style="color:#000000"> entityManagerFactory</span><span style="color:#666600">)</span> <span style="color:#666600">{</span><span style="color:#000000">
    entityManagerFactory</span><span style="color:#666600">.</span><span style="color:#000000">close</span><span style="color:#666600">();</span>
  <span style="color:#666600">}</span>

  <span style="color:#006666">@Produces</span>
  <span style="color:#006666">@RequestScoped</span>
  <span style="color:#000088">public</span> <span style="color:#660066">EntityManager</span><span style="color:#000000"> createEntityManager</span><span style="color:#666600">(</span><span style="color:#660066">EntityManagerFactory</span><span style="color:#000000"> entityManagerFactory</span><span style="color:#666600">)</span> <span style="color:#666600">{</span>
    <span style="color:#000088">return</span><span style="color:#000000"> entityManagerFactory</span><span style="color:#666600">.</span><span style="color:#000000">createEntityManager</span><span style="color:#666600">();</span>
  <span style="color:#666600">}</span>

  <span style="color:#000088">public</span> <span style="color:#000088">void</span><span style="color:#000000"> close</span><span style="color:#666600">(</span><span style="color:#006666">@Disposes</span> <span style="color:#660066">EntityManager</span><span style="color:#000000"> entityManager</span><span style="color:#666600">)</span> <span style="color:#666600">{</span><span style="color:#000000">
    entityManager</span><span style="color:#666600">.</span><span style="color:#000000">close</span><span style="color:#666600">();</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

The necessary setup can vary depending on the JavaEE environment. You may need to do nothing more than redeclare a EntityManager as a CDI bean, as follows:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">CdiConfig</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Produces</span>
  <span style="color:#006666">@RequestScoped</span>
  <span style="color:#006666">@PersistenceContext</span>
  <span style="color:#000088">public</span> <span style="color:#660066">EntityManager</span><span style="color:#000000"> entityManager</span><span style="color:#666600">;</span>
<span style="color:#666600">}</span></code></span></span>

在前面的示例中,容器必须能够创建JPA EntityManagers本身。所有配置都将JPA重新导出EntityManager为CDI bean。

Spring Data JPA CDI扩展将所有可用EntityManager实例作为CDI bean获取,并在容器请求存储库类型的bean时为Spring Data存储库创建代理。因此,获取Spring Data存储库的实例是声明@Injected属性的问题,如以下示例所示:

<span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-java"><span style="color:#000088">class</span> <span style="color:#660066">RepositoryClient</span> <span style="color:#666600">{</span>

  <span style="color:#006666">@Inject</span>
  <span style="color:#660066">PersonRepository</span><span style="color:#000000"> repository</span><span style="color:#666600">;</span>

  <span style="color:#000088">public</span> <span style="color:#000088">void</span><span style="color:#000000"> businessMethod</span><span style="color:#666600">()</span> <span style="color:#666600">{</span>
    <span style="color:#660066">List</span><span style="color:#666600"><</span><span style="color:#660066">Person</span><span style="color:#666600">></span><span style="color:#000000"> people </span><span style="color:#666600">=</span><span style="color:#000000"> repository</span><span style="color:#666600">.</span><span style="color:#000000">findAll</span><span style="color:#666600">();</span>
  <span style="color:#666600">}</span>
<span style="color:#666600">}</span></code></span></span>

附录

附录A:命名空间参考

<repositories />元素

<repositories />元素触发Spring Data存储库基础结构的设置。最重要的属性是base-package,它定义了扫描Spring Data存储库接口的包。请参阅“ XML配置 ”。下表描述了<repositories />元素的属性:

表6.属性
名称描述

base-package

定义要扫描的包,以便*Repository在自动检测模式下扩展(实际接口由特定Spring Data模块确定)的存储库接口。也会扫描配置包下面的所有包。允许使用通配符。

repository-impl-postfix

定义后缀以自动检测自定义存储库实现。名称以配置的后缀结尾的类被视为候选。默认为Impl

query-lookup-strategy

确定用于创建查找程序查询的策略。有关详细信息,请参阅“ 查询查找策略 ”。默认为create-if-not-found

named-queries-location

定义搜索包含外部定义查询的Properties文件的位置。

consider-nested-repositories

是否应考虑嵌套存储库接口定义。默认为false

附录B:Populators命名空间参考

<populator />元素

<populator />元素允许通过Spring Data存储库基础结构填充数据存储。[ 1 ]

表7.属性
名称描述

locations

从哪里可以找到要从存储库中读取对象的文件。

附录C:存储库查询关键字

支持的查询关键字

下表列出了Spring Data存储库查询派生机制通常支持的关键字。但是,请查阅特定于商店的文档以获取支持的关键字的确切列表,因为此处列出的某些关键字可能在特定商店中不受支持。

表8.查询关键字
逻辑关键字关键字表达式

AND

And

OR

Or

AFTER

After, IsAfter

BEFORE

Before, IsBefore

CONTAINING

ContainingIsContainingContains

BETWEEN

Between, IsBetween

ENDING_WITH

EndingWithIsEndingWithEndsWith

EXISTS

Exists

FALSE

False, IsFalse

GREATER_THAN

GreaterThan, IsGreaterThan

GREATER_THAN_EQUALS

GreaterThanEqual, IsGreaterThanEqual

IN

In, IsIn

IS

IsEquals,(或无关键字)

IS_EMPTY

IsEmpty, Empty

IS_NOT_EMPTY

IsNotEmpty, NotEmpty

IS_NOT_NULL

NotNull, IsNotNull

IS_NULL

Null, IsNull

LESS_THAN

LessThan, IsLessThan

LESS_THAN_EQUAL

LessThanEqual, IsLessThanEqual

LIKE

Like, IsLike

NEAR

Near, IsNear

NOT

Not, IsNot

NOT_IN

NotIn, IsNotIn

NOT_LIKE

NotLike, IsNotLike

REGEX

RegexMatchesRegexMatches

STARTING_WITH

StartingWithIsStartingWithStartsWith

TRUE

True, IsTrue

WITHIN

Within, IsWithin

附录D:存储库查询返回类型

支持的查询返回类型

下表列出了Spring Data存储库通常支持的返回类型。但是,请查阅特定于商店的文档以获取支持的返回类型的确切列表,因为此处列出的某些类型可能在特定商店中不受支持。

 地理空间类型(如GeoResultGeoResults,和GeoPage)是仅对支持地理空间查询数据存储可用。
表9.查询返回类型
返回类型描述

void

表示没有返回值。

基元

Java原语。

包装类型

Java包装器类型。

T

一个独特的实体。期望查询方法最多返回一个结果。如果未找到结果,null则返回。不止一个结果会触发一个IncorrectResultSizeDataAccessException

Iterator<T>

Iterator

Collection<T>

Collection

List<T>

List

Optional<T>

Java 8或Guava Optional。期望查询方法最多返回一个结果。如果未找到结果,Optional.empty()Optional.absent()返回结果。不止一个结果会触发一个IncorrectResultSizeDataAccessException

Option<T>

Scala或Javaslang Option类型。在语义上与Optional前面描述的Java 8相同。

Stream<T>

A Java 8 Stream.

Future<T>

Future. Expects a method to be annotated with @Async and requires Spring’s asynchronous method execution capability to be enabled.

CompletableFuture<T>

A Java 8 CompletableFuture. Expects a method to be annotated with @Async and requires Spring’s asynchronous method execution capability to be enabled.

ListenableFuture

org.springframework.util.concurrent.ListenableFuture. Expects a method to be annotated with @Async and requires Spring’s asynchronous method execution capability to be enabled.

Slice

A sized chunk of data with an indication of whether there is more data available. Requires a Pageable method parameter.

Page<T>

Slice with additional information, such as the total number of results. Requires a Pageable method parameter.

GeoResult<T>

A result entry with additional information, such as the distance to a reference location.

GeoResults<T>

A list of GeoResult<T> with additional information, such as the average distance to a reference location.

GeoPage<T>

Page with GeoResult<T>, such as the average distance to a reference location.

Mono<T>

A Project Reactor Mono emitting zero or one element using reactive repositories. Expects the query method to return one result at most. If no result is found, Mono.empty() is returned. More than one result triggers an IncorrectResultSizeDataAccessException.

Flux<T>

A Project Reactor Flux emitting zero, one, or many elements using reactive repositories. Queries returning Flux can emit also an infinite number of elements.

Single<T>

A RxJava Single emitting a single element using reactive repositories. Expects the query method to return one result at most. If no result is found, Mono.empty() is returned. More than one result triggers an IncorrectResultSizeDataAccessException.

Maybe<T>

Maybe使用反应式存储库发出零或一个元素的RxJava 。期望查询方法最多返回一个结果。如果未找到结果,Mono.empty()则返回。不止一个结果会触发一个IncorrectResultSizeDataAccessException

Flowable<T>

RxJava Flowable使用反应式存储库发出零个,一个或多个元素。返回的查询Flowable也可以发出无限数量的元素。

附录E:常见问题

共同

  1. 我想获得更详细的日志信息,了解内部调用的方法JpaRepository(例如)。我怎样才能获得它们?

    您可以使用CustomizableTraceInterceptorSpring提供的,如以下示例所示:

    <span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><bean</span> <span style="color:#660066">id</span><span style="color:#666600">=</span><span style="color:#008800">"customizableTraceInterceptor"</span> <span style="color:#660066">class</span><span style="color:#666600">=</span><span style="color:#008800">"
      org.springframework.aop.interceptor.CustomizableTraceInterceptor"</span><span style="color:#000088">></span>
      <span style="color:#000088"><property</span> <span style="color:#660066">name</span><span style="color:#666600">=</span><span style="color:#008800">"enterMessage"</span> <span style="color:#660066">value</span><span style="color:#666600">=</span><span style="color:#008800">"Entering $[methodName]($[arguments])"</span><span style="color:#000088">/></span>
      <span style="color:#000088"><property</span> <span style="color:#660066">name</span><span style="color:#666600">=</span><span style="color:#008800">"exitMessage"</span> <span style="color:#660066">value</span><span style="color:#666600">=</span><span style="color:#008800">"Leaving $[methodName](): $[returnValue]"</span><span style="color:#000088">/></span>
    <span style="color:#000088"></bean></span>
    
    <span style="color:#000088"><aop:config></span>
      <span style="color:#000088"><aop:advisor</span> <span style="color:#660066">advice-ref</span><span style="color:#666600">=</span><span style="color:#008800">"customizableTraceInterceptor"</span>
        <span style="color:#660066">pointcut</span><span style="color:#666600">=</span><span style="color:#008800">"execution(public * org.springframework.data.jpa.repository.JpaRepository+.*(..))"</span><span style="color:#000088">/></span>
    <span style="color:#000088"></aop:config></span></code></span></span>

基础设施

  1. 目前我已经实现了基于的存储库层HibernateDaoSupport。我SessionFactory用Spring 创建了一个AnnotationSessionFactoryBean。如何在此环境中使用Spring Data存储库?

    必须更换AnnotationSessionFactoryBeanHibernateJpaSessionFactoryBean如下:

    例111. SessionFactory从a中查找aHibernateEntityManagerFactory

    <span style="color:#34302d"><span style="color:rgba(0, 0, 0, 0.9)"><code class="language-xml"><span style="color:#000088"><bean</span> <span style="color:#660066">id</span><span style="color:#666600">=</span><span style="color:#008800">"sessionFactory"</span> <span style="color:#660066">class</span><span style="color:#666600">=</span><span style="color:#008800">"org.springframework.orm.jpa.vendor.HibernateJpaSessionFactoryBean"</span><span style="color:#000088">></span>
      <span style="color:#000088"><property</span> <span style="color:#660066">name</span><span style="color:#666600">=</span><span style="color:#008800">"entityManagerFactory"</span> <span style="color:#660066">ref</span><span style="color:#666600">=</span><span style="color:#008800">"entityManagerFactory"</span><span style="color:#000088">/></span>
    <span style="color:#000088"></bean></span></code></span></span>

审计

  1. 我想使用Spring Data JPA审计功能,但我的数据库已配置为在实体上设置修改和创建日期。如何防止Spring Data以编程方式设置日期。

    将namespace元素的set-dates属性设置auditingfalse

附录F:术语表

AOP

面向方面编程

Commons DBCP

Commons DataBase连接池 - 来自Apache基础的库,提供DataSource接口的池实现。

CRUD

创建,读取,更新,删除 - 基本持久性操作。

DAO

数据访问对象 - 用于将持久化逻辑与要持久化的对象分开的模式

依赖注入

模式将组件的依赖关系从外部传递给组件,释放组件以查找从属组件本身。有关更多信息,请参阅http://en.wikipedia.org/wiki/Dependency_Injection

EclipseLink

实现JPA的对象关系映射器 - http://www.eclipselink.org

Hibernate

实现JPA的对象关系映射器 - http://www.hibernate.org

JPA

Java Persistence API

Spring

Java应用程序框架 - http://projects.spring.io/spring-framework


转自:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值