Spring Data Commons - 中文参考文档
Oliver GierkeThomas DarimontChristoph StroblMark PollackThomas Risberg版本1.13.4.RELEASE,2017-06-08
©2008-2015原作者.
| 注注注 | 只要您不对这些副本收取任何费用,并且进一步规定,每个副本都包含本版权声明,无论是以印刷版还是电子版分发,本文档的副本可供您自己使用并分发给他人. |
| ———– | —————————————- |
| | |
目录
- 前言
- 参考文献
- 依赖关系
- 3.使用Spring数据存储库
- 3.1.核心概念
- 3.2.查询方式
- 3.3.定义存储库接口
- 3.4.定义查询方法
- 3.5.创建存储库实例
- 3.6.Spring数据存储库的自定义实现
- 3.7.从集合根发布事件
- 3.8.Spring数据扩展
- 4.按示例查询
- 5.审计
- 附录
前言
Spring Data Commons项目将核心Spring概念应用到使用许多关系和非关系数据存储的解决方案的开发中.
项目元数据
- 版本控制 - http://github.com/spring-projects/spring-data-commons
- Bugtracker - https://jira.spring.io/browse/DATACMNS
- 版本库 - https://repo.spring.io/libs-release
- 里程碑存储库 - https://repo.spring.io/libs-milestone
- 快照库 - https://repo.spring.io/libs-snapshot
参考文献
依赖关系
由于个别Spring数据模块的初始日期不同,其中大多数都带有不同的主要和次要版本号.找到兼容版本的最简单的方法是依靠我们随附的兼容版本定义的Spring Data Release Train BOM.在Maven项目<dependencyManagement />
中,您可以在POM部分中声明此依赖关系:
示例1.使用Spring数据发布列表BOM
1 | <dependencyManagement> |
目前的发布火车版本是Ingalls-SR4
.火车名称按字母顺序升序,目前可用的火车名称列在这里.版本名称遵循以下模式:${name}-${release}
其中release可以是以下之一:
BUILD-SNAPSHOT
- 当前快照M1
,M2
等等-里程碑RC1
,RC2
等等-候选发布版RELEASE
- GA发布SR1
,SR2
等等-服务版本
使用BOM的一个工作示例可以在我们的Spring Data示例存储库中找到.如果这样就可以声明要在<dependencies />
块中使用没有版本的Spring数据模块.
示例2.声明对Spring数据模块的依赖
1 | <dependencies> |
2.1.使用Spring Boot进行依赖管理
Spring Boot已经为您选择了最新版本的Spring数据模块.如果您想要升级到较新版本,只需将该属性配置spring-data-releasetrain.version
为您要使用的列车名称和迭代.
2.2.Spring框架
当前版本的Spring Data模块需要Spring Framework在4.3.9.RELEASE或更好的版本中.这些模块也可能会使用该较小版本的旧版本错误版本.但是,强烈建议您使用该版本中的最新版本.
3.使用Spring数据存储库
Spring数据库抽象的目标是大大减少为各种持久性存储实现数据访问层所需的样板代码量.
注 | Spring数据存储库文档和您的模块本章介绍了Spring Data存储库的核心概念和接口.本章中的信息是从Spring Data Commons模块中获取的.它使用Java Persistence API(JPA)模块的配置和代码示例.将XML命名空间声明和要扩展的类型调整为您正在使用的特定模块的等效项.命名空间参考涵盖支持存储库API的所有Spring数据模块支持的XML配置,Repository查询关键字涵盖了一般由存储库抽象支持的查询方法关键字.有关模块特定功能的详细信息,请参阅本文档该模块的一章. |
---|---|
3.1.核心概念
Spring数据库抽象中的中央接口Repository
(可能不是什么惊喜).管理域类以及域类的id类型作为类型参数.此接口主要作为标记接口捕获要使用的类型,并帮助您发现扩展此接口的接口.该CrudRepository
规定对于正在管理的实体类复杂的CRUD功能.
示例3. CrudRepository接口
1 | public interface CrudRepository<T, ID extends Serializable> |
1 | 保存给定的实体. |
---|---|
2 | 返回由给定的ID标识的实体. |
3 | 返回所有实体. |
4 | 返回实体数. |
5 | 删除给定的实体. |
6 | 指示是否存在具有给定id的实体. |
注 | 我们还提供持久性技术特定的抽象,例如JpaRepository 或MongoRepository .CrudRepository 除了相当通用的持久化技术不可知的接口,例如CrudRepository,这些接口还扩展和暴露了底层持久性技术的功能. |
---|---|
除此之外,CrudRepository
还有一个PagingAndSortingRepository
抽象方法可以添加其他方法来简化对实体的分页访问:
示例4. PagingAndSortingRepository
1 | public interface PagingAndSortingRepository<T, ID extends Serializable> |
访问第二页的User
页面大小为20,你可以简单地做这样的事情:
1 | PagingAndSortingRepository<User, Long> repository = // … get access to a bean |
除了查询方法之外,还可以查询计数和删除查询的推导.
示例5.派生计数查询
1 | public interface UserRepository extends CrudRepository<User, Long> { |
派生删除查询
1 | public interface UserRepository extends CrudRepository<User, Long> { |
3.2.查询方式
标准CRUD功能库通常在基础数据存储上有查询.使用Spring数据,声明这些查询将成为四个步骤:
声明扩展Repository或其一个子接口的接口,并将其键入将要处理的域类和ID类型.
1
interface PersonRepository extends Repository<Person, Long> { … }
在接口上声明查询方法.
1
2
3interface PersonRepository extends Repository<Person, Long> {
List<Person> findByLastname(String lastname);
}设置Spring为这些接口创建代理实例.通过JavaConfig:
1
2
3
4import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
class Config {}或通过XML配置:
1
2
3
4
5
6
7
8
9
10
11
12<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="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">
<jpa:repositories base-package="com.acme.repositories"/>
</beans>在这个例子中使用JPA命名空间.如果您正在为任何其他商店使用存储库抽象,则需要将其更改为商店模块的相应命名空间声明,该名称空间声明应该进行交换
jpa
,例如mongodb
.另外,请注意,JavaConfig变体不会明确地配置程序包,因为默认情况下使用注解类的程序包.要自定义要扫描的软件包,请使用
basePackage…
数据存储特定存储库-annotation的@Enable…
属性之一.获取注册表实例并使用它.
1
2
3
4
5
6
7
8
9public class SomeClient {
private PersonRepository repository;
public void doSomething() {
List<Person> persons = repository.findByLastname("Matthews");
}
}
下面的部分详细说明每一步.
3.3.定义存储库接口
作为第一步,您定义一个域类别的存储库接口.该接口必须扩展Repository并输入到域类和ID类型.如果要公开该域类型的CRUD方法,则扩展CrudRepository
而不是Repository
.
3.3.1.微调存储库定义
通常情况下,你的资料库接口将延长Repository
,CrudRepository
或PagingAndSortingRepository
.或者,如果您不想扩展Spring数据接口,还可以使用它来注解存储库接口@RepositoryDefinition
.扩展CrudRepository
公开了一套完整的方法来操纵您的实体.如果您希望对所暴露的方法有选择性,只需将要暴露的方法复制CrudRepository
到您的域库中即可.
注 | 这允许您在提供的Spring数据存储库功能之上定义自己的抽象. |
---|---|
示例7.选择性地暴露CRUD方法
1 |
|
在此第一步中,您为所有域存储库定义了一个公共基础接口,并将其暴露出来findOne(…)
.save(…)
这些方法将被路由到由Spring Data提供的您选择的存储库的基本存储库实现中,例如在JPA的情况下SimpleJpaRepository
,因为他们正在匹配方法签名CrudRepository
.所以UserRepository
现在将能够保存用户,并通过id查找单个,以及触发查询以Users
通过其电子邮件地址查找.
注 | 请注意,中间版本库接口被注解为@NoRepositoryBean .确保将该注解添加到Spring Data不应在运行时创建实例的所有存储库接口. |
---|---|
3.3.2.使用多个Spring数据模块的存储库
在应用程序中使用唯一的Spring数据模块使事情变得简单,因此定义范围内的所有存储库接口都绑定到Spring数据模块.有时应用程序需要使用多个Spring数据模块.在这种情况下,存储库定义需要区分持久性技术.Spring Data进入严格的存储库配置模式,因为它在类路径上检测到多个存储库工厂.严格的配置需要有关存储库或域类的详细信息来决定用于存储库定义的Spring数据模块绑定:
- 如果存储库定义扩展了模块特定的存储库,那么它是特定 Spring数据模块的有效候选者.
- 如果域类使用模块特定类型注解进行注解,那么它是特定Spring数据模块的有效候选项.Spring数据模块接受第三方注解(如JPA
@Entity
)或提供自己的注解,例如@Document
Spring Data MongoDB / Spring Data Elasticsearch.
示例8.使用模块特定接口的存储库定义
1 | interface MyRepository extends JpaRepository<User, Long> { } |
MyRepository
并UserRepository
延长JpaRepository
他们的类型层次.它们是Spring Data JPA模块的有效候选.
示例9.使用通用接口的存储库定义
1 | interface AmbiguousRepository extends Repository<User, Long> { |
AmbiguousRepository
和AmbiguousUserRepository
仅延伸Repository
,并CrudRepository
在他们的类型层次.虽然使用独特的Spring数据模块是非常好的,但是多个模块无法区分哪些特定的Spring Data这些存储库应该绑定.
示例10.使用带有注解的域类的存储库定义
1 | interface PersonRepository extends Repository<Person, Long> { |
PersonRepository
引用Person
用JPA注解注解@Entity
,因此这个存储库显然属于Spring Data JPA.UserRepository
使用User
注解与Spring数据MongoDB的@Document
注解.
示例11.使用具有混合注解的域类的存储库定义
1 | interface JpaPersonRepository extends Repository<Person, Long> { |
此示例显示使用JPA和Spring Data MongoDB注解的域类.它定义了两个仓库,JpaPersonRepository
和MongoDBPersonRepository
.一个用于JPA,另一个用于MongoDB使用.Spring数据不再能够分辨出存储库导致未定义的行为.
存储库类型详细信息和标识域类注解用于严格的存储库配置,以识别特定Spring数据模块的存储库候选.在同一个域类型上使用多个持久性技术特定的注解可能会跨多个持久性技术重用域类型,但是Spring Data不再能够确定绑定存储库的唯一模块.
区分资源库的最后一个方法是定义库基础包.基本包定义了扫描存储库接口定义的起点,这意味着存储库定义位于相应的包中.默认情况下,注解驱动的配置使用配置类的包.基于XML的配置中的基本包是强制性的.
示例12.基本包的注解驱动配置
1 | "com.acme.repositories.jpa") (basePackages = |
3.4.定义查询方法
存储库代理有两种方法从方法名称中导出特定于存储的查询.它可以直接从方法名称导出查询,或通过使用手动定义的查询.可用选项取决于实际存储.但是,必须有一个策略来决定创建什么实际的查询.我们来看看可用的选项.
3.4.1.查询查询策略
以下策略可用于存储库基础架构来解决查询.在配置query-lookup-strategy
XML的情况下,您可以通过属性配置命名空间中的策略,也可以通过queryLookupStrategy
Java配置中启用$ {store}存储库注解的属性来配置策略.特定数据存储可能不支持某些策略.
CREATE
尝试从查询方法名称构造特定于商店的查询.一般的方法是从方法名称中删除一组已知的前缀,并解析该方法的其余部分.详细了解查询创建中的查询构造.USE_DECLARED_QUERY
尝试找到一个声明的查询,并将抛出一个异常,以防万一找不到它.查询可以由某处的注解定义,也可以通过其他方式声明.请参阅特定商店的文档以查找该商店的可用选项.如果存储库基础架构在引导时没有找到方法的声明查询,则它将失败.CREATE_IF_NOT_FOUND
(默认)组合CREATE
和USE_DECLARED_QUERY
.它首先查找声明的查询,如果没有找到声明的查询,它将创建一个基于名称的自定义查询.这是默认的查找策略,因此如果您没有明确配置任何内容.它允许通过方法名称快速查询定义,但也可以根据需要引入声明的查询来定制这些查询.
3.4.2.查询创建
构建在Spring数据存储库基础架构中的查询生成器机制对于在存储库的实体上构建约束查询很有用.该机制条前缀find…By
,read…By
,query…By
,count…By
,和get…By
从所述方法和开始分析它的其余部分.引入子句可以包含其他表达式,例如在Distinct
要创建的查询上设置不同的标志.然而,第一个By
作为分隔符来指示实际标准的开始.在一个非常基本的水平,你可以定义实体性条件,并与它们串联And
和Or
.
示例13.从方法名称创建查询
1 | public interface PersonRepository extends Repository<User, Long> { |
解析方法的实际结果取决于创建查询的持久性存储.但是,有一些一般的事情要注意.
- 表达式通常是可以连接的运算符的属性遍历.您可以使用组合属性表达式
AND
和OR
.您还可以得到这样的运营商为支撑Between
,LessThan
,GreaterThan
,Like
为属性表达式.受支持的操作员可能因数据存储而异,因此请参阅参考文档的相应部分. - 该方法解析器支持设置一个
IgnoreCase
标志个别特性(例如,findByLastnameIgnoreCase(…)
)或对于支持忽略大小写(通常是一个类型的所有属性String
情况下,例如,findByLastnameAndFirstnameAllIgnoreCase(…)
).是否支持忽略案例可能会因存储而异,因此请参阅参考文档中相关章节,了解特定于商店的查询方法. - 您可以通过
OrderBy
在引用属性和提供排序方向(Asc
或Desc
)的查询方法中附加一个子句来应用静态排序.要创建支持动态排序的查询方法,请参阅特殊参数处理.
3.4.3.属性表达式
属性表达式只能引用被管实体的直接属性,如前面的例子所示.在查询创建时,您已经确保已解析属性是受管域类的属性.但是,您还可以通过遍历嵌套属性来定义约束.假设一个Person
有Address
一个ZipCode
.在这种情况下,方法名称为
1 | List<Person> findByAddressZipCode(ZipCode zipCode); |
创建属性遍历x.address.zipCode
.解析算法首先将整个part(AddressZipCode
)解释为属性,并使用该名称(uncapitalized)检查域类的属性.如果算法成功,则使用该属性.如果不是,则算法拆分了从右侧的驼峰部分的信号源到头部和尾部,并试图找出相应的属性,在我们的例子,AddressZip
和Code
.如果算法找到一个具有该头部的属性,那么它需要尾部,并从那里继续构建树,然后按照刚刚描述的方式将尾部分割.如果第一个分割不匹配,则算法将分割点移动到左(Address
,ZipCode
),然后继续.
虽然这在大多数情况下应该起作用,但算法可能会选择错误的属性.假设Person
该类也有一个addressZip
属性.该算法将在第一个分割轮中匹配,并且基本上选择错误的属性,最后失败(因为该类型addressZip
可能没有code
属性).
要解决这个歧义,您可以_
在方法名称中使用手动定义遍历点.所以我们的方法名称最终会如此:
1 | List<Person> findByAddress_ZipCode(ZipCode zipCode); |
当我们将下划线视为保留字符时,我们强烈建议遵循标准Java命名约定(即注不**使用属性名称中的下划线,而是使用骆驼案例).
3.4.4.特殊参数处理
要处理查询中的参数,您只需定义方法参数,如上述示例中所示.此外,基础设施将会识别某些特定类型,Pageable
并动态Sort
地将分页和排序应用于查询.
示例14.在查询方法中使用Pageable,Slice和Sort
1 | Page<User> findByLastname(String lastname, Pageable pageable); |
第一种方法允许您将org.springframework.data.domain.Pageable
实例传递给查询方法,以动态地将分页添加到静态定义的查询中.A Page
知道可用的元素和页面的总数.它通过基础设施触发计数查询来计算总数.由于这可能是昂贵的,这取决于所使用的商店,Slice
可以用作返回.A Slice
只知道是否有下一个Slice
可用的,当走过较大的结果集时可能只是足够的.
排序选项也通过Pageable
实例处理.如果只需要排序,只需在org.springframework.data.domain.Sort
参数中添加一个参数即可.正如你也可以看到的,只需返回一个List
也是可能的.在这种情况下,Page
将不会创建构建实际实例所需的附加元数据(这反过来意味着必须不被发布的附加计数查询),而仅仅是限制查询仅查找给定范围的实体.
注 | 要查找完整查询的页面数量,您必须触发额外的计数查询.默认情况下,此查询将从您实际触发的查询派生. |
---|---|
3.4.5.限制查询结果
的查询方法的结果可以通过关键字来限制first
或top
,其可以被可互换地使用.可选的数值可以追加到顶部/第一个以指定要返回的最大结果大小.如果数字被省略,则假设结果大小为1.
示例15.使用Top
和限制查询的结果大小First
1 | User findFirstByOrderByLastnameAsc(); |
限制表达式也支持Distinct
关键字.此外,对于将结果集限制为一个实例的查询,支持将结果包装到一个实例中Optional
.
如果将分页或切片应用于限制查询分页(以及可用页数的计算),则在限制结果中应用.
注 | 请注意,通过参数将结果限制为动态排序,Sort 可以表示最小的“K”以及“K”最大元素的查询方法. |
---|---|
3.4.6.流式查询结果
可以通过使用Java 8 Stream<T>
作为返回类型来逐步处理查询方法的结果.而不是简单地将查询结果包装在Stream
数据存储中,特定的方法用于执行流.
示例16.使用Java 8流式传输查询的结果 Stream<T>
1 | "select u from User u") ( |
注 | 在Stream 潜在封装底层数据存储特定资源和使用后必须因此被关闭.您可以手动关闭Stream 使用该close() 方法或使用Java 7 try-with-resources块. |
---|---|
示例17. Stream<T>
在try-with-resources块中使用结果
1 | try (Stream<User> stream = repository.findAllByCustomQueryAndStream()) { |
注 | 目前并不是所有的Spring数据模块都支持Stream<T> 返回类型. |
---|---|
3.4.7.异步查询结果
可以使用Spring的异步方法执行功能异步执行存储库查询.这意味着方法将在调用时立即返回,并且实际的查询执行将发生在已提交给Spring TaskExecutor的任务中.
1 |
|
1 | 使用java.util.concurrent.Future 的返回类型. |
---|---|
2 | 使用Java 8 java.util.concurrent.CompletableFuture 作为返回类型. |
3 | 使用a org.springframework.util.concurrent.ListenableFuture 作为返回类型. |
3.5.创建存储库实例
在本节中,您将为定义的存储库接口创建实例和bean定义.一种方法是使用支持存储库机制的每个Spring数据模块附带的Spring命名空间,尽管我们通常建议使用Java-Config样式配置.
3.5.1.XML配置
每个Spring Data模块都包含一个存储库元素,它允许您简单地定义Spring为您扫描的基础包.
示例18.通过XML启用Spring数据存储库
1 | <?xml version="1.0" encoding="UTF-8"?> |
在上述示例中,指示Spring扫描com.acme.repositories
其所有接口扩展的子包Repository
或其子接口之一.对于发现的每个接口,基础架构注册特定FactoryBean
于持久性技术,以创建处理查询方法调用的适当代理.每个bean都是从接口名称导出的bean名称下注册的,所以接口UserRepository
将被注册userRepository
.该base-package
属性允许通配符,以便您可以定义扫描包的模式.
使用过滤器
默认情况下,基础设施拾取每个接口Repository
,扩展位于配置的基础包下面的持久性技术特定子接口,并为其创建一个bean实例.但是,您可能需要对要为其创建哪些接口bean实例进行更细粒度的控制.要做到这一点你使用<include-filter />
和<exclude-filter />
元素里面<repositories />
.语义与Spring的上下文命名空间中的元素完全相同.有关详细信息,请参阅有关这些元素的Spring参考文档.
例如,要将某些接口从实例化中排除为存储库,可以使用以下配置:
示例19.使用exclude-filter元素
1 | <repositories base-package="com.acme.repositories"> |
此示例排除所有SomeRepository
从实例化开始的接口.
3.5.2.JavaConfig
也可以使用@Enable${store}Repositories
JavaConfig类上的特定于商店的注解触发存储库基础架构.有关Spring容器的基于Java的配置的介绍,请参阅参考文档.[ 1 ]
启用Spring Data存储库的示例配置看起来像这样.
示例20.基于样本注解的存储库配置
1 |
|
注 | 该示例使用JPA特定的注解,您可以根据实际使用的存储模块进行更改.这同样适用于EntityManagerFactory bean的定义.请参阅涵盖商店特定配置的部分. |
---|---|
3.5.3.独立使用
您还可以使用Spring容器外部的存储库基础架构,例如在CDI环境中.您在类路径中仍然需要一些Spring库,但通常可以通过编程方式设置存储库.提供存储库支持的Spring数据模块提供了一个持久性技术特定的RepositoryFactory,可以使用如下所示的RepositoryFactory.
示例21.存储库工厂的独立使用
1 | RepositoryFactorySupport factory = … // Instantiate factory here |
3.6.Spring数据存储库的自定义实现
通常有必要为几个存储库方法提供自定义实现.Spring数据存储库可以轻松地允许您提供自定义存储库代码,并将其与通用CRUD抽象和查询方法功能集成.
3.6.1.将自定义行为添加到单个存储库
要使用自定义功能丰富资源库,首先要定义一个接口和自定义功能的实现.使用您提供的存储库接口来扩展自定义接口.
示例22.自定义存储库功能的接口
1 | interface UserRepositoryCustom { |
示例23.自定义存储库功能的实现
1 | class UserRepositoryImpl implements UserRepositoryCustom { |
注 | 找到的类的最重要的一点是Impl 与核心存储库接口相比的名称的后缀(见下文). |
---|---|
实现本身并不依赖于Spring Data,而且可以是一个常规的Spring bean.因此,您可以使用标准依赖注入行为来注入其他bean的引用,如a JdbcTemplate
,参与方面等等.
示例24.更改您的基本存储库接口
1 | interface UserRepository extends CrudRepository<User, Long>, UserRepositoryCustom { |
让您的标准存储库接口扩展自定义库.这样做结合了CRUD和自定义功能,并将其提供给客户端.
组态
如果使用命名空间配置,存储库基础设施会尝试通过扫描我们找到存储库的包下的类来自动检测自定义实现.这些类需要遵循将命名空间元素的属性附加repository-impl-postfix
到找到的存储库接口名称的命名约定.此后缀默认为Impl
.
示例25.配置示例
1 | <repositories base-package="com.acme.repository" /> |
第一个配置示例将尝试查找一个类com.acme.repository.UserRepositoryImpl
作为自定义存储库实现,而第二个示例将尝试查找com.acme.repository.UserRepositoryFooBar
.
手动接线
如果您的自定义实现仅使用基于注解的配置和自动布线,那么刚才显示的方法效果很好,因为它将被视为任何其他Spring bean.如果您的自定义实现bean需要特殊布线,那么您只需简单地声明该bean并将其命名为刚刚描述的约定.然后,基础设施将通过名称引用手动定义的bean定义,而不是创建一个本身.
示例26.自定义实现的手动接线
1 | <repositories base-package="com.acme.repository" /> |
3.6.2.将自定义行为添加到所有存储库
当您想将一个方法添加到所有的存储库接口时,上述方法是不可行的.要将自定义行为添加到所有存储库,您首先添加一个中间接口来声明共享行为.
示例27.声明定制共享行为的接口
1 |
|
现在,您的各个存储库接口将扩展此中间接口,而不是扩展Repository
接口以包含声明的功能.接下来,创建扩展了持久性技术特定的存储库基类的中间接口的实现.然后,该类将用作存储库代理的自定义基类.
自定义库基础类
1 | public class MyRepositoryImpl<T, ID extends Serializable> |
注 | 该类需要具有专门的存储库工厂实现使用的超级类的构造函数.如果存储库基类有多个构造函数,则覆盖一个EntityInformation 加上特定于存储的基础架构对象(例如,一个EntityManager 或一个模板类). |
---|---|
Spring <repositories />
命名空间的默认行为是为所有接下来的接口提供一个实现base-package
.这意味着如果保持当前状态,MyRepository
Spring将创建一个实现实例.这当然是不希望的,因为它只是作为一个中介,Repository
以及您想为每个实体定义的实际存储库接口.要排除Repository
从被实例化为存储库实例的接口,您可以使用@NoRepositoryBean
(如上所示)注解它,或将其移动到已配置的外部base-package
.
最后一步是使Spring数据基础架构了解定制的库基类.在JavaConfig中,这是通过使用repositoryBaseClass
注解的@Enable…Repositories
属性来实现的:
示例29.使用JavaConfig配置自定义存储库基类
1 |
|
相应的属性在XML命名空间中可用.
示例30.使用XML配置自定义存储库基类
1 | <repositories base-package="com.acme.repository" |
3.7.从集合根发布事件
存储库管理的实体是聚合根.在域驱动设计应用程序中,这些聚合根通常会发布域事件.Spring Data提供了一个注解,@DomainEvents
您可以使用聚合根的方法来使该发布尽可能简单.
示例31.从聚合根中暴露域事件
1 | class AnAggregateRoot { |
1 | 使用的方法@DomainEvents 可以返回单个事件实例或事件集合.它不能采取任何论据. |
---|---|
2 | 在所有事件发布之后,注解了一个方法@AfterDomainEventsPublication .它可以用于潜在地清理要发布的事件列表. |
每次调用Spring数据存储库的save(…)
方法之一时,都会调用这些方法.
3.8.Spring数据扩展
本节介绍一组Spring数据扩展,可以在各种上下文中启用Spring数据使用.目前大部分的集成针对Spring MVC.
3.8.1.Querydsl扩展
Querydsl是一个框架,可以通过流畅的API构建静态类型的类SQL查询.
几个Spring数据模块提供与Querydsl的集成QueryDslPredicateExecutor
.
示例32. QueryDslPredicateExecutor接口
1 | public interface QueryDslPredicateExecutor<T> { |
1 | 查找并返回一个匹配的实体Predicate . |
---|---|
2 | 查找并返回匹配的所有实体Predicate . |
3 | 返回匹配的实体数Predicate . |
4 | 如果匹配的实体Predicate 存在则返回. |
要使用Querydsl支持,只需QueryDslPredicateExecutor
在您的存储库接口上扩展.
实例33.在存储库上进行Querydsl整合
1 | interface UserRepository extends CrudRepository<User, Long>, QueryDslPredicateExecutor<User> { |
以上使用Querydsl可以编写类型安全的查询Predicate
.
1 | Predicate predicate = user.firstname.equalsIgnoreCase("dave") |
3.8.2.网络支持
注 | 本部分包含Spring数据Web支持的文档,因为它在1.6范围内与Spring Data Commons相同.由于新引入的支持更改了很多事情,因此我们保留了旧版Web支持中前一行为的文档. |
---|---|
如果模块支持仓库编程模型,Spring数据模块将附带各种Web支持.Web相关的东西需要Spring MVC JAR在类路径上,其中一些甚至提供与Spring HATEOAS的集成[ 2 ].通常,通过@EnableSpringDataWebSupport
在JavaConfig配置类中使用注解来启用集成支持.
示例34.启用Spring Data Web支持
1 |
|
该@EnableSpringDataWebSupport
批注注册几个组件,我们将在一个位讨论.它还将在类路径上检测Spring HATEOAS,并注册集成组件(如果存在).
或者,如果您使用XML配置,请注册SpringDataWebSupport
或HateoasAwareSpringDataWebSupport
作为Spring bean:
示例35.启用XML中的Spring Data Web支持
1 | <bean class="org.springframework.data.web.config.SpringDataWebConfiguration" /> |
基本网络支持
上述配置设置将注册几个基本组件:
- A
DomainClassConverter
使Spring MVC能够从请求参数或路径变量解析存储库管理域类的实例. HandlerMethodArgumentResolver
实现让Spring MVC从请求参数中解析Pageable和Sort实例.
DomainClassConverter
将DomainClassConverter
让你在你的Spring MVC控制器方法签名直接使用域类型,这样就不必通过库手动查找实例:
示例36. Spring MVC控制器在方法签名中使用域类型
1 |
|
您可以看到该方法直接接收User实例,不需要进一步的查找.实例可以通过让Spring MVC首先将路径变量转换为域类型,最终通过调用findOne(…)
注册为域类型的存储库实例来访问该实例来解决.
注 | 目前,资源库CrudRepository 必须实施才能被发现以进行转换. |
---|---|
HandlerMethodArgumentResolvers for Pageable和Sort
上面的配置代码片段也注册了一个PageableHandlerMethodArgumentResolver
以及一个实例SortHandlerMethodArgumentResolver
.注册启用Pageable
并Sort
成为有效的控制器方法参数
使用Pageable作为控制器方法参数
1 |
|
此方法签名将导致Spring MVC尝试使用以下默认配置从请求参数派生一个Pageable实例:
page |
您要检索的页面,0已编入索引并默认为0. | |
---|---|---|
size |
您要检索的页面的大小,默认为20. | |
sort |
应以格式排序的属性`property,property(,ASC | DESC).默认排序方向是上升. sort如果要切换路线,请使用多个参数,例如 ?sort=firstname&sort=lastname,asc`. |
要自定义此行为可扩展SpringDataWebConfiguration
或启用HATEOAS启用的等效项,并覆盖pageableResolver()
或sortResolver()
方法并导入自定义配置文件,而不是使用@Enable
-annotation.
如果您需要从请求中解析多个Pageable
或Sort
实例(例如,对于多个表),则可以使用Spring的@Qualifier
注解来区分出来.然后请求参数必须加上前缀${qualifier}_
.所以对于像这样的方法签名:
1 | public String showUsers(Model model, |
你有填充foo_page
和bar_page
等.
该Pageable
方法的默认值相当于一个,new PageRequest(0, 20)
但可以使用@PageableDefaults
参数上的Pageable
注解进行自定义.
超媒体支持页面
Spring HATEOAS提供了一个表示模型类PagedResources
,它允许Page
使用必要的Page
元数据丰富一个实例的内容,以及让客户端轻松浏览页面的链接.PagedResources
通过Spring HATEOAS ResourceAssembler
接口的实现来完成页面的转换PagedResourcesAssembler
.
示例38.使用PagedResourcesAssembler作为控制器方法参数
1 |
|
启用如上所示的配置允许将PagedResourcesAssembler
其用作控制器方法参数.打电话toResources(…)
会导致以下情况:
- 将内容的内容
Page
变成PagedResources
实例的内容. - 该
PagedResources
会得到一个PageMetadata
附加填充信息形成的实例Page
和基础PageRequest
. - 根据页面的状态
PagedResources
获取prev
和next
链接.链接将指向被调用的方法映射到的URI.添加到方法中的分页参数将匹配设置,PageableHandlerMethodArgumentResolver
以确保以后可以解析链接.
假设我们在数据库中有30个Person实例.您现在可以触发请求,您将看到类似的内容:GET http://localhost:8080/persons
1 | { "links" : [ { "rel" : "next", |
您会看到汇编程序产生了正确的URI,并且还提取了存在的默认配置,以将参数解析Pageable
为即将到来的请求.这意味着,如果您更改该配置,链接将自动遵守更改.默认情况下,汇编程序指向被调用的控制器方法,但可以通过将自定义Link
作为基础来定制,以构建PagedResourcesAssembler.toResource(…)
方法重载的分页链接.
Querydsl网络支持
对于具有QueryDSL集成的那些商店,可以从Request
查询字符串中包含的属性中导出查询.
这意味着给定User
前一个样本的对象一个查询字符串
1 | ?firstname=Dave&lastname=Matthews |
可以解决
1 | QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews")) |
使用QuerydslPredicateArgumentResolver
.
注 | @EnableSpringDataWebSupport 当类路径上找到 Querydsl时,该功能将自动启用. |
---|---|
添加@QuerydslPredicate
到方法签名将提供一个可以使用的Predicate
,可以通过执行QueryDslPredicateExecutor
.
注 | 类型信息通常从方法返回类型中解析出来.由于这些信息不一定与域类型相匹配,所以使用root 属性可能是一个好主意QuerydslPredicate . |
---|---|
1 |
|
1 | 解析查询字符串参数匹配Predicate 的User . |
---|---|
默认绑定如下:
Object
简单的属性为eq
.Object
收集像属性一样contains
.Collection
简单的属性为in
.
这些绑定可以通过bindings
属性@QuerydslPredicate
或通过使用Java 8 default methods
添加QuerydslBinderCustomizer
到存储库接口进行定制.
1 | interface UserRepository extends CrudRepository<User, String>, |
1 | QueryDslPredicateExecutor 提供对特定查找器方法的访问Predicate . |
---|---|
2 | QuerydslBinderCustomizer 在存储库接口上定义将自动拾取和快捷方式@QuerydslPredicate(bindings=…) . |
3 | 将username 属性的绑定定义为简单的包含绑定. |
4 | 将属性的默认绑定定义String 为不区分大小写的包含匹配. |
5 | 从解决方案中排除密码属性Predicate . |
3.8.3.存储库populator
如果您使用Spring JDBC模块,您可能熟悉DataSource
使用SQL脚本填充的支持.尽管它不使用SQL作为数据定义语言,但存储库级别可以使用类似的抽象,因为它必须与存储无关.因此,populator支持XML(通过Spring的OXM抽象)和JSON(通过Jackson)来定义用于填充存储库的数据.
假设你有一个data.json
包含以下内容的文件:
示例39. JSON中定义的数据
1 | [ { "_class" : "com.acme.Person", |
您可以使用Spring Data Commons中提供的存储库命名空间的populator元素轻松填充您的存储库.要将上述数据填充到您的PersonRepository,请执行以下操作:
示例40.声明Jackson存储库填充程序
1 | <?xml version="1.0" encoding="UTF-8"?> |
该声明导致该data.json
文件通过Jackson读取和反序列化ObjectMapper
.
JSON对象将被解组的类型将通过检查_class
JSON文档的属性来确定.基础设施将最终选择适当的存储库来处理刚被反序列化的对象.
要使用XML来定义数据库,应该使用这些unmarshaller-populator
元素来填充这些数据库.您可以将其配置为使用Spring OXM为您提供的一种XML编组器选项.有关详细信息,请参阅Spring参考文档.
示例41.声明一个解组的存储库填充程序(使用JAXB)
1 | <?xml version="1.0" encoding="UTF-8"?> |
3.8.4.传统网络支持
Spring MVC的域类Web绑定
鉴于您正在开发Spring MVC Web应用程序,您通常必须从URL解析域类ID.默认情况下,您的任务是将请求参数或URL部分转换为域类,将其转交到下面的层,然后直接对实体执行业务逻辑.这看起来像这样:
1 |
|
首先,您声明每个控制器的存储库依赖关系,以分别查找由控制器或存储库管理的实体.查看实体也是样板,因为它总是findOne(…)
打电话.幸运的是,Spring提供了注册自定义组件的方法,允许在String
值与任意类型之间进行转换.
属性编辑器
对于3.0之前的Spring版本PropertyEditors
,必须使用简单的Java .要与之进行集成,Spring Data提供了一个DomainClassPropertyEditorRegistrar
查找所有在其中注册的Spring数据存储库ApplicationContext
并注册PropertyEditor
管理域类的自定义.
1 | <bean class="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> |
如果您已经配置了上述示例中的Spring MVC,则可以按如下方式配置控制器,从而减少了大量杂乱和样板.
1 |
|
4.按示例查询
4.1.介绍
本章将为您提供“按示例查询”的介绍,并说明如何使用示例.
示例查询(QBE)是一种用户友好的查询技术,具有简单的接口.它允许动态查询创建,并且不需要编写包含字段名称的查询.实际上,按示例查询,根本不需要使用商店特定的查询语言编写查询.
4.2.用法
由示例API查询由三部分组成:
- Probe:这是具有填充字段的域对象的实际示例.
ExampleMatcher
:ExampleMatcher
载有关于如何匹配特定字段的详细信息.它可以重复使用在多个示例.Example
:Example
由探针和ExampleMatcher
.它用于创建查询.
按示例查询适用于多个用例,但也有限制:
何时使用
- 使用一组静态或动态约束来查询数据存储
- 频繁重构域对象,而不用担心破坏现有查询
- 独立于底层数据存储API
限制
- 不支持嵌套/分组的属性约束,如
firstname = ?0 or (firstname = ?1 and lastname = ?2)
- 只支持对字符串进行启动/包含/结束/正则表达式匹配以及其他属性类型的精确匹配
在开始使用按示例查询之前,您需要有一个域对象.要开始,只需为您的存储库创建一个接口:
示例42. Sample Person对象
1 | public class Person { |
这是一个简单的域对象.你可以用它创建一个Example
.默认情况下,具有null
值的字段将被忽略,字符串将使用存储特定的默认值进行匹配.示例可以通过使用of
工厂方法或使用来构建ExampleMatcher
.Example
是不可变的
示例43.简单示例
1 | Person person = new Person(); *1* |
1 | 创建域对象的新实例 |
---|---|
2 | 设置要查询的属性 |
3 | 创建 Example |
理想情况下,使用存储库执行示例.为此,让您的存储库接口扩展QueryByExampleExecutor<T>
.以下是该QueryByExampleExecutor
接口的摘录:
例44 QueryByExampleExecutor
1 | public interface QueryByExampleExecutor<T> { |
您可以在下面阅读更多关于按示例执行查询.
4.3.示例匹配器
示例不限于默认设置.您可以使用以下命令为字符串匹配,空处理和特定于属性的设置指定自己的默认值ExampleMatcher
.
示例45.具有自定义匹配的示例匹配器
1 | Person person = new Person(); *1* |
1 | 创建域对象的新实例. |
---|---|
2 | 设置属性. |
3 | 创建一个ExampleMatcher 期望所有值匹配.即使没有进一步的配置,在这个阶段也可以使用. |
4 | 构造一个新的ExampleMatcher 来忽略属性路径lastname . |
5 | 构造一个新的ExampleMatcher 来忽略属性路径lastname 并包含空值. |
6 | 构造一个新ExampleMatcher 的忽略属性路径lastname ,包含空值,并使用perform suffix字符串匹配. |
7 | Example 根据域对象和配置创建新的ExampleMatcher . |
默认情况下,ExampleMatcher
将期望探针上设置的所有值都匹配.如果要获取匹配任何隐含定义的谓词的结果,请使用ExampleMatcher.matchingAny()
.
您可以为各个属性指定行为(例如嵌套属性的“firstname”和“lastname”,“address.city”).您可以使用匹配的选项和区分大小写来调整它.
示例46.配置匹配器选项
1 | ExampleMatcher matcher = ExampleMatcher.matching() |
配置匹配器选项的另一种风格是使用Java 8 lambdas.这种方法是一个回调,要求实现者修改匹配器.由于配置选项保持在匹配器实例中,因此不需要返回匹配器.
示例47.使用lambdas配置匹配器选项
1 | ExampleMatcher matcher = ExampleMatcher.matching() |
通过Example
使用合并的配置视图创建的查询.默认匹配设置可以在ExampleMatcher
级别设置,而个别设置可以应用于特定的属性路径.设置的设置ExampleMatcher
由属性路径设置继承,除非它们被明确定义.属性修补程序上的设置的优先级高于默认设置.
设置 | 范围 |
---|---|
空操作 | ExampleMatcher |
字符串匹配 | ExampleMatcher 和物业路径 |
忽略属性 | 物业路径 |
区分大小写 | ExampleMatcher 和物业路径 |
价值转型 | 物业路径 |
5.审计
5.1.基本
Spring Data提供了复杂的支持,以透明地跟踪创建或更改实体的人员以及发生的时间点.为了从该功能中受益,您必须为实体类配备审计元数据,该元数据可以使用注解或实现一个接口进行定义.
5.1.1.基于注解的审计元数据
我们提供@CreatedBy
,@LastModifiedBy
捕捉谁创建或修改的实体以及用户@CreatedDate
和@LastModifiedDate
捕捉一次发生这种情况的地步.
实例48.经审计的实体
1 | class Customer { |
您可以看到,可以选择性地应用注解,具体取决于您要捕获的信息.对于捕获时间点的注解可以用于JodaTimes DateTime
,旧Java Date
和Calendar
JDK8日期/时间类型以及long
/的类型的属性Long
.
5.1.2.基于接口的审计元数据
如果您不想使用注解来定义审核元数据,则可以让您的域类实现该Auditable
接口.它暴露了所有审核属性的setter方法.
还有一个方便的基类AbstractAuditable
,您可以扩展,以避免手动实现接口方法的需要.请注意,这增加了您的域类与Spring数据的耦合,这可能是您想要避免的.通常,基于注解的定义审计元数据的方式是首选的,因为它具有较少的侵入性和更灵活性.
5.1.3.AuditorAware
如果您使用任何一个@CreatedBy
或者@LastModifiedBy
,审计基础设施需要了解当前的主体.为此,我们提供了一个AuditorAware<T>
SPI接口,您必须实现该接口来告知基础架构当前用户或系统与应用程序交互的情况.通用类型T
定义了使用@CreatedBy
或@LastModifiedBy
必须注解的属性类型.
以下是使用Spring Security Authentication
对象的接口示例实现:
示例49.基于Spring Security的AuditorAware的实现
1 | class SpringSecurityAuditorAware implements AuditorAware<User> { |
该实现是访问Authentication
Spring Security提供的对象,并查找UserDetails
您在实现中创建的自定义实例UserDetailsService
.我们在这里假设您通过该UserDetails
实现暴露域用户,但您也可以根据Authentication
发现从任何地方查找.
附录
附录A:命名空间参考
元素
该<repositories />
元素触发了Spring Data存储库基础结构的设置.最重要的属性是base-package
定义要扫描Spring数据存储库接口的包.[ 3 ]
名称 | 描述 |
---|---|
base-package |
定义用于在自动检测模式下扩展*存储库(实际接口由特定的Spring数据模块确定)的存储库接口进行扫描的软件包.所配置的软件包以下的所有软件包也将被扫描.通配符是允许的. |
repository-impl-postfix |
定义后缀自动检测自定义存储库实现.其名称以配置的后缀结尾的类将被视为候选.默认为Impl . |
query-lookup-strategy |
确定用于创建查找器查询的策略.有关详细信息,请参阅查询查询策略.默认为create-if-not-found . |
named-queries-location |
定义查找包含外部定义查询的“属性”文件的位置. |
consider-nested-repositories |
控制是否应考虑嵌套的存储库接口定义.默认为false . |
附录B:Populators命名空间参考
元素
该<populator />
元素允许通过Spring数据库基础架构填充数据存储.[ 4 ]
名称 | 描述 |
---|---|
locations |
在哪里可以找到从存储库读取对象的文件. |
附录C:存储库查询关键字
支持的查询关键字
下表列出了Spring数据库查询推导机制通常支持的关键字.但是,请查阅特定于商店的文档,了解支持的关键字的确切列表,因为某些商店中可能不支持这些列表.
逻辑关键字 | 关键词表达式 |
---|---|
AND |
And |
OR |
Or |
AFTER |
After , IsAfter |
BEFORE |
Before , IsBefore |
CONTAINING |
Containing ,IsContaining ,Contains |
BETWEEN |
Between , IsBetween |
ENDING_WITH |
EndingWith ,IsEndingWith ,EndsWith |
EXISTS |
Exists |
FALSE |
False , IsFalse |
GREATER_THAN |
GreaterThan , IsGreaterThan |
GREATER_THAN_EQUALS |
GreaterThanEqual , IsGreaterThanEqual |
IN |
In , IsIn |
IS |
Is ,Equals (或没有关键词) |
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 |
Regex ,MatchesRegex ,Matches |
STARTING_WITH |
StartingWith ,IsStartingWith ,StartsWith |
TRUE |
True , IsTrue |
WITHIN |
Within , IsWithin |
附录D:存储库查询返回类型
支持的查询返回类型
下表列出了Spring Data仓库通常支持的返回类型.但是,请查阅特定于商店的文档,以获取支持的返回类型的确切列表,因为某些商店中可能不支持这些列表.
注 | 地理空间类型,如(GeoResult ,GeoResults ,GeoPage )只适用于支持地理空间查询的数据存储. |
---|---|
返回类型 | 描述 |
---|---|
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> |
Java 8 Stream . |
Future<T> |
一Future .期待使用@Async 注解的方法,并且需要启用Spring的异步方法执行功能. |
CompletableFuture<T> |
Java 8 CompletableFuture .期待使用@Async 注解的方法,并且需要启用Spring的异步方法执行功能. |
ListenableFuture |
一org.springframework.util.concurrent.ListenableFuture .期待使用@Async 注解的方法,并且需要启用Spring的异步方法执行功能. |
Slice |
大小的数据块与信息是否有更多的数据可用.需要一个Pageable 方法参数. |
Page<T> |
A Slice 附加信息,例如总结果数.需要一个Pageable 方法参数. |
GeoResult<T> |
带有附加信息的结果条目,例如到参考位置的距离. |
GeoResults<T> |
的列表GeoResult<T> 与其他信息,到参考位置例如平均距离. |
GeoPage<T> |
甲Page 带GeoResult<T> ,例如平均距离的参考位置. |
2.SPRINGHATEOAS - https://github.com/SpringSource/spring-hateoas
版本1.13.4.RELEASE
最后更新2017-06-08 10:35:34 MESZ