您的位置:首页 > 编程语言 > Java开发

Spring Data Commons 官方文档学习

2017-08-03 21:31 633 查看
Spring Data Commons 官方文档学习 -by LarryZeal
Version 1.12.6.Release, 2017-07-27

为知笔记版本在这里,带格式。

Table of Contents

前言

参考文档

1. 依赖

1.1. 使用Spring Boot 进行依赖管理

1.2. Spring框架

2. 使用Spring Data Repositories

2.1. 核心概念

2.2. Query methods 查询方法

2.3. 定义repository interfaces

2.3.1. 微调repository定义

2.3.2. 多个Spring Data modules 情况下使用Repositories

2.4. 定义查询方法

2.4.1. Query 查找策略

2.4.2. Query 创建

2.4.3. Property expressions

2.4.4. 特殊参数处理

2.4.5. 限制查询结果

2.4.6. 流式查询结果

2.4.7. 异步查询结果

2.5. 创建repository实例

2.5.1. XML configuration

2.5.2. JavaConfig

2.5.3. 单独使用

2.6. 自定义实现Spring Data repositories

2.6.1. 为某个repositories 添加自定义行为

2.6.2. 为所有 repositories添加自定义行为

2.7. 从aggregate roots发布事件

2.8. Spring Data extensions

2.8.1. Querydsl Extension

2.8.2. Web support

2.8.3. Repository populators

2.8.4. Legacy web support

3. 使用Example进行查询

3.1. 介绍

3.2. 使用

3.3. Example matchers

4. Auditing

4.1. 基础

4.1.1. 基于注解的auditing metadata

4.1.2. 基于接口的auditing metadata

4.1.3. AuditorAware

附录

附录 A: Namespace reference

The <repositories /> element

附录 B: Populators namespace reference

The <populator /> element

附录 C: Repository查询关键字

支持的查询关键字

附录 D: Repository查询的返回类型

支持的查询的返回类型

前言

Spring Data Commons project 将core Spring concepts应用到了很多关系型和非关系型数据存储的解决方案开发。

参考文档

1. 依赖

由于Spring Data modules不同的开发时间,所以它们大多数都有不同的主版本号和小版本号。想要找到兼容版本的最佳方式是依赖Spring Data Release Train BOM,已经提供了兼容版本定义。在Maven项目中,你可以在POM的 <dependencyManagement />部分声明该依赖:

例子1. 使用Spring Data release train BOM

<dependencyManagement>


<dependencies>


<dependency>


<groupId>org.springframework.data</groupId>


<artifactId>spring-data-releasetrain</artifactId>


<version>${release-train}</version>


<scope>import</scope>


<type>pom</type>


</dependency>


</dependencies>


</dependencyManagement>


当前的release train版本是
Ingalls-SR6
。 该train的名字是按照字母顺序升序的,当前可用的版本罗列在这里 here。版本的名字遵循这种格式:
${name}-${release}
,其中release可能是下列之一:

BUILD-SNAPSHOT
- current snapshots

M1
,
M2
etc. - milestones

RC1
,
RC2
etc. - release candidates

RELEASE
- GA release

SR1
,
SR2
etc. - service releases

你可以在我们的 Spring Data examples repository 找到实际使用的例子。如果是就地(in place)声明Spring Data modules,你可能喜欢这样:

例子2. 声明一个Spring Data module依赖

<dependencies>


<dependency>


<groupId>org.springframework.data</groupId>


<artifactId>spring-data-jpa</artifactId>


</dependency>


</dependencies>


1.1. 使用Spring Boot 进行依赖管理

Spring Boot已经为你选择了非常新的Spring Data modules版本。如果你想要升级到更新的版本,只需要简单的配置下 property
spring-data-releasetrain.version
to the train name and iteration ,配置成你想使用的。

1.2. Spring框架

当前版本的Spring Data modules要求Spring框架版本是4.3.10.RELEASE,或更高。这些模块,也可能运行在老版本上面,但是,强烈推荐使用新的版本。

2. 使用Spring Data Repositories

Spring Data repository abstraction的目标是明显地减少呆板代码(指加载驱动、创建/关闭链接之类的事情)的数量。

2.1. 核心概念

Spring Data repository abstraction的核心接口是
Repository
。它使用domain class以及domain class的id类型作为类型参数来管理(见接口泛型)。该接口主要是作为一个标记接口,来捕获使用的类型,并帮助用户来发现继承自该接口的接口。
CrudRepository
为其管理的entities提供了复杂的CRUD功能。

例子 3. CrudRepository interface

publicinterfaceCrudRepository<T, ID extendsSerializable>extendsRepository<T, ID>{<S extends T> S save(S entity);(1)

T findOne(ID primaryKey);(2)Iterable<T> findAll();(3)Long count();(4)voiddelete(T entity);(5)boolean exists(ID primaryKey);(6)// … more functionality omitted.}


① 保存给定的 entity。
② 返回通过id查找到的entity。
③ 返回所有的entities。
④ 返回entities的数量。
⑤ 删除给定的entity。
⑥ 根据给定的id判断entity是否存在。

我们也提供了特定持久化技术的抽象,如
JpaRepository
or
MongoRepository
。这些接口继承自
CrudRepository
,并暴露出底层持久化技术的能力,而不仅仅是泛泛的持久化技术接口(如CrudRepository )的能力。

基于
CrudRepository
还有一个
PagingAndSortingRepository
abstraction,添加了分页相关的功能:

例子4. PagingAndSortingRepository

publicinterfacePagingAndSortingRepository<T, ID extendsSerializable>extendsCrudRepository<T, ID>{Iterable<T> findAll(Sort sort);Page<T> findAll(Pageable pageable);}


如果你想访问User的第二页(每页20条记录),你可以这样做:

PagingAndSortingRepository<User,Long> repository =// … get access to a beanPage<User> users = repository.findAll(newPageRequest(1,20));


除了查询方法,还有count和delete查询的衍生:

例子5. 衍生的Count查询

publicinterfaceUserRepositoryextendsCrudRepository<User,Long>{Long countByLastname(String lastname);}


例子6. 衍生的Delete查询

publicinterfaceUserRepositoryextendsCrudRepository<User,Long>{Long deleteByLastname(String lastname);List<User> removeByLastname(String lastname);}


2.2. Query methods 查询方法

标准的CRUD functionality repositories,通常会查询底层数据存储。使用Spring Data,只需要以下四个步骤:

声明一个接口,继承Repository 接口或其子接口,填上entity类型和id类型(主键类型)。

interfacePersonRepositoryextendsRepository<Person,Long>{…}


在该接口中声明查询方法。

interfacePersonRepositoryextendsRepository<Person,Long>{List<Person> findByLastname(String lastname);}


设置Spring,以创建这些接口的代理实例。可以使用 JavaConfig:

import org.springframework.data.jpa.repository.config.EnableJpaRepositories;@EnableJpaRepositoriesclassConfig{}


也可以使用 XML configuration:

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="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:repositoriesbase-package="com.acme.repositories"/></beans>[/code] 
该例子中使用了JPA namespace。如果使用其他存储的repository abstraction,你需要切换到相应的namespace,例如使用
mongodb
.来代替 jpa。

还需要注意,JavaConfig没有显式地配置一个package。想要自定义需要扫描的package,可以使用@EnableJpaRepositories(或@EnableMongodbRepositories等)注解的basePackage属性。

获取注入的repository实例,并使用它。

publicclassSomeClient{@AutowiredprivatePersonRepository repository;publicvoid doSomething(){List<Person> persons = repository.findByLastname("Matthews");}}


下面的章节会详细解释每一步。

2.3. 定义repository接口

第一步,必须定义一个特定domain class的repository接口。该接口必须继承Repository,并填写domain class和ID type。如果你想暴露CRUD methods,可以继承
CrudRepository
,而非
Repository


2.3.1. 微调repository定义

通常,你的repository接口会继承
Repository
CrudRepository
或者
PagingAndSortingRepository
。或者,如果你不想继承Spring Data interfaces的话,你可以在你的repository接口上面注解
@RepositoryDefinition
。 继承
CrudRepository
会暴露一个完整的方法集合,可以操作你的entities。如果你只想有选择地暴露一些方法,可以从
CrudRepository
中复制到你的domain repository。

这允许你定义你自己的abstraction,基于Spring Data Repositories的功能。

例子7. 有选择地暴露 CRUD methods

@NoRepositoryBeaninterfaceMyBaseRepository<T, ID extendsSerializable>extendsRepository<T, ID>{

T findOne(ID id);

T save(T entity);}interfaceUserRepositoryextendsMyBaseRepository<User,Long>{User findByEmailAddress(EmailAddress emailAddress);}


在这里的第一步,你定义了一个通用的base接口,暴露了
findOne(…)
save(…)


注意:别忘了注解 @RepositoryDefinition 。

2.3.2. 多个Spring Data modules情况下使用Repositories

在应用中仅使用一个Spring Data module,是一件很简单的事,在定义的范围内的所有的repository接口 都被绑定到这个Spring Data module。然而,有时候,应用需要使用多个Spring Data module。在这种情况下,就要求一个repository定义能够区分出不同的持久化技术。Spring Data会进入strict repository configuration mode,因为它会在classpath中探测到多个repository factories。Strict configuration要求 repository 或 domain class的详细信息,来决定Spring Data module绑定一个repository定义:

如果该repository定义 继承自特定模块的repository( extends the module-specific repository),那么它就是特定Spring Data module的一个有效的候选。

如果该domain class 被注解了特定模块的类注解( annotated with the module-specific type annotation),那么它就是特定Spring Data module的一个有效的候选。Spring Data modules接受第三方注解 (如 JPA的
@Entity
),或者提供自己的注解如针对 Spring Data MongoDB/Spring Data Elasticsearch的
@Document
注解。

例子 8. 使用特定模块的Repository 定义

interfaceMyRepositoryextendsJpaRepository<User,Long>{}@NoRepositoryBeaninterfaceMyBaseRepository<T, ID extendsSerializable>extendsJpaRepository<T, ID>{…}interfaceUserRepositoryextendsMyBaseRepository<User,Long>{…}


MyRepository
UserRepository
继承自
JpaRepository
。他们都是Spring Data JPA module的有效的候选。

例子 9. 使用泛泛的接口进行Repository定义

interfaceAmbiguousRepositoryextendsRepository<User,Long>{…}@NoRepositoryBeaninterfaceMyBaseRepository<T, ID extendsSerializable>extendsCrudRepository<T, ID>{…}interfaceAmbiguousUserRepositoryextendsMyBaseRepository<User,Long>{…}


AmbiguousRepository
AmbiguousUserRepository
分别继承自
Repository
CrudRepository
。单独使用时没有问题,但多个模块并存时,就无法区分repositories该使用哪个模块。

例子 10. 使用注解的Domain Classes进行Repository定义

interfacePersonRepositoryextendsRepository<Person,Long>{…}@EntitypublicclassPerson{…}interfaceUserRepositoryextendsRepository<User,Long>{…}@DocumentpublicclassUser{…}


PersonRepository
引用了带有JPA注解
@Entity
Person
,因此,该repository明显属于 Spring Data JPA。
UserRepository
使用了带有 Spring Data MongoDB’s
@Document
注解的
User。


例子 11. 使用代理混合注解的Domain Classes进行Repository定义

interfaceJpaPersonRepositoryextendsRepository<Person,Long>{…}interfaceMongoDBPersonRepositoryextendsRepository<Person,Long>{…}@Entity@DocumentpublicclassPerson{…}


该例子,示意了一个同时带有JPA注解和Spring Data MongoDB注解的domain class。它定义了两个repositories:
JpaPersonRepository
and
MongoDBPersonRepository
。一个是用于JPA,另一个用于MongoDB。Spring Data不能区分respositories,这会导致未定义的行为!

Repository type detailsidentifying domain class annotations 是被用于 strict repository configuration identify repository candidates for a particular Spring Data module。 在同一个domain type上使用多个持久化技术的注解,是可能在多个持久化技术之间复用domain types,但此时 Spring Data 不再能够决定绑定到repository的唯一的module。

区分repositories的最后一种方式是scoping repository的base packages。 Base packages 定义了扫描repository接口的出发点。默认,注解驱动的配置使用该配置类所在的package。 在 XML-based configuration中,base package 是强制填写的。

例子 12. 注解驱动的base packages的配置

@EnableJpaRepositories(basePackages ="com.acme.repositories.jpa")@EnableMongoRepositories(basePackages ="com.acme.repositories.mongo")interfaceConfiguration{}


2.4. 定义查询方法

repository代理,有两种方式来从方法名中获取一个特定存储的查询。它可以从方法名中直接获取查询,或者,可以通过使用一个自定义的查询。具体可用的选项取决于具体的存储。无论什么方式,总有一种策略来决定创建什么样的实际查询。让我们来看一下这些可用的选项。

2.4.1. Query 查找策略

下面的策略对于repository infrastructure来说,可以用于解析查询。你可以在namespace中配置该策略,通过
query-lookup-strategy
attribute (XML);也可以在Enable ${store} Repositories 注解的
queryLookupStrategy
attribute中配置(javaConfig)。一些策略可能不支持特定的数据存储。

CREATE
:试图从查询方法名中构建一个特定存储的查询。一般的方法是从方法中移除一个给定的大家熟知的前缀集合,并解析剩余的部分。更多关于query construction的内容,详见 Query 创建

USE_DECLARED_QUERY
:试图找到一个声明了的查询,如果找不到 会抛出异常。改查下可以通过一个注解来定义,或者通过其他手段声明。查阅特定存储的文档,以找到可用的选项。 如果repository infrastructure在启动期间最终没有找到一个声明过的查询,就会失败。

CREATE_IF_NOT_FOUND
(默认):结合了
CREATE
USE_DECLARED_QUERY


2.4.2. Query 创建

Spring Data repository infrastructure中内置的query builder机制,在repository的entities上构建限制查询(constraining queries )时很有用。该机制从方法名中脱去了前缀
find…By
read…By
query…By
count…By
、 以及
get…By
,并解析剩余的部分。引入的语句,可以包含更多表达式,如
Distinct
。然而,第一个
By
扮演了分隔符角色,标明了实际criteria 的开始。在一个非常基础的级别上,你可以定义一些条件,并使用
And
Or
来连接它们。

例子 13. 从方法名中创建查询

publicinterfacePersonRepositoryextendsRepository<User,Long>{List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress,String lastname);// Enables the distinct flag for the queryList<Person> findDistinctPeopleByLastnameOrFirstname(String lastname,String firstname);List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname,String firstname);// Enabling ignoring case for an individual propertyList<Person> findByLastnameIgnoreCase(String lastname);// Enabling ignoring case for all suitable propertiesList<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname,String firstname);// Enabling static ORDER BY for a queryList<Person> findByLastnameOrderByFirstnameAsc(String lastname);List<Person> findByLastnameOrderByFirstnameDesc(String lastname);}


解析方法名的具体结果,取决于你要为哪个持久存储创建查询。不过,这里有一些通用的事情需要注意。

表达式通常遍历property,结合操作符(可能连接到一起)。你可以使用
AND
OR
拼接。你也可以使用诸如
Between
LessThan
GreaterThan
Like
之类的操作符。不同的数据存储可能支持不同的操作符,所以,最好查阅相关参考文档。

方法解析器支持
IgnoreCase
flag,可以为单独的 properties设置 (例如,
findByLastnameIgnoreCase(…)
),也可以为所有的 properties设置 (一般是
String
instances,例如,
findByLastnameAndFirstnameAllIgnoreCase(…)
)。是否支持忽略大小写,不同的存储可能不同的结果,所以,请查阅相关的参考文档。

你可以使用静态排序,只需要在方法名上追加一个
OrderBy
语句,需要引用一个property,还要提供排序方式 (
Asc
Desc
)。如果想创建支持动态排序的查询,见 特殊参数处理

2.4.3. Property expressions

Property expressions 只能指向被管理entity的直接property,前面的例子已有体现。在查询创建时间,你已经确定了被解析的property是被管理的domain class的一个property。然而,你还可以为嵌套的properties定义现在(constraints)。假定
Person
有一个
Address
property,该property本身还有一个
ZipCode
property。这种情况下,下面的方法名

List<Person> findByAddressZipCode(ZipCode zipCode);


创建了 property traversal
x.address.zipCode
。 该解析算法,从将整个部分 (
AddressZipCode
)解释为 property 开始,检查 domain class中是否有那个名字的 property (uncapitalized)。如果该算法成功了,它就会使用那个property。如果失败了,该算法会按驼峰拆分该部分 - 从右侧开始,拆分成head和tail,然后查找head相应的property,在我们的例子中就是
AddressZip
and
Code
。如果该算法 根据head找到了一个property,它会拿着tail继续构建下面的分支,按照前面的方式拆分tail。如果第一个拆分不匹配,算法会将拆分点移动到左侧 (
Address
,
ZipCode
),并继续。

虽然这应该已经足够应付大多数情况,该算法仍然可能选择出错误的property。假定
Person
class 也有一个
addressZip
property。该算法会在第一次拆分时就匹配,然后选择错误的property,并最终失败(因为
addressZip
的类型可能没有
code
property)。

为了解决这种模糊,你可以在方法名中使用
_
,以手动地定义遍历点。因此,我们的方法名可能是这样的:

List<Person> findByAddress_ZipCode(ZipCode zipCode);


注意:因为我们将下划线_作为保留字符,我们强烈建议用户的开发遵守标准Java命名惯例(例如,不在property名字中使用下划线,而是用驼峰代替)。

2.4.4. 特殊参数处理

为了处理在你的查询中的参数,你可以简单地定义方法参数,如同上面所见到的那样。除此之外,the infrastructure 还可以识别特定的特殊类型,如
Pageable
and
Sort
,以动态的应用分页和排序。

例子 14. 在查询方法中使用Pageable、 Slice 和Sort

Page<User> findByLastname(String lastname,Pageable pageable);Slice<User> findByLastname(String lastname,Pageable pageable);List<User> findByLastname(String lastname,Sort sort);List<User> findByLastname(String lastname,Pageable pageable);


第一个方法,允许你传入一个
org.springframework.data.domain.Pageable
instance,来动态添加分页功能。一个
Page
包含元素的总数量和可用的页数。它能这样,是因为 the infrastructure 触发了一个count query,从而能够计算总数量。取决于不同的存储,这可能耗费巨大,所以可以返回Slice。一个
Slice
仅包括是否有下一个可用的
Slice


排序选项,也可以通过
Pageable
instance 处理。如果你只需要排序,那你只需要给你的方法添加一个
org.springframework.data.domain.Sort
参数。如你所见,直接返回一个
List
也是可以的。在这种情况下,构建
Page
instance所需的元数据就不需要了(就是说不需要额外的count query了),但只能查询给定返回的entities。

2.4.5. 限制查询结果

查询方法的结果可以使用关键字
first
top
来限制,这两个可以互换。一个可选的数值可以追加到 top/first ,以标明最大返回的数量。如果留空,默认是1。

例子 15. L使用
Top
and
First限制查询结果


User findFirstByOrderByLastnameAsc();User findTopByOrderByAgeDesc();Page<User> queryFirst10ByLastname(String lastname,Pageable pageable);Slice<User> findTop3ByLastname(String lastname,Pageable pageable);List<User> findFirst10ByLastname(String lastname,Sort sort);List<User> findTop10ByLastname(String lastname,Pageable pageable);


限制表达式,也支持关键字
Distinct
。另外,也支持
Optional


If pagination or slicing is applied to a limiting query pagination (and the calculation of the number of pages available) then it is applied within the limited result. -- 这句嘛意思?

Note that limiting the results in combination with dynamic sorting via a
Sort
parameter allows to express query methods for the 'K' smallest as well as for the 'K' biggest elements.

2.4.6. 流式查询结果

通过使用Java 8
Stream<T>
作为返回返回类型,查询方法的结果可以被加速处理。

例子 16. 使用Java 8
Stream<T>处理返回结果


@Query("select u from User u")Stream<User> findAllByCustomQueryAndStream();Stream<User> readAllByFirstnameNotNull();@Query("select u from User u")Stream<User> streamAllPaged(Pageable pageable);


注意:一个Stream 底层会封装数据存储的特定资源,因此,必须在使用完毕之后关闭。可以使用 close() 手动关闭,或者也可以使用Java 7 try-with-resources 代码块。

例子 17. 在try-with-resources代码块中使用
Stream<T>
result

try(Stream<User> stream = repository.findAllByCustomQueryAndStream()){
stream.forEach(…);}


注意:目前,不是所有的Spring Data modules都支持Stream<T> 作为返回类型。

2.4.7. 异步查询结果 (不翻译了)

Repository queries can be executed asynchronously using Spring’s asynchronous method execution capability. This means the method will return immediately upon invocation and the actual query execution will occur in a task that has been submitted to a Spring TaskExecutor.

@AsyncFuture<User> findByFirstname(String firstname);(1)@AsyncCompletableFuture<User> findOneByFirstname(String firstname);(2)@AsyncListenableFuture<User> findOneByLastname(String lastname);(3)


① 使用java.util.concurrent.Future 作为返回类型。
② 使用Java 8的java.util.concurrent.CompletableFuture 作为返回类型。
③ 使用org.springframework.util.concurrent.ListenableFuture 作为返回类型。

2.5. 创建 repository instances

在本部分,你会创建 定义过的repository接口的实例和bean 定义。一种方式是使用 Spring namespace,但我们一般都推荐使用Java-Config 形式的配置。

2.5.1. XML configuration

每个 Spring Data module 都包含一个 repositories element,允许你简单地定义一个base package -- Spring会扫描它。

例子 18. 启用 Spring Data repositories -- 通过 XML

<?xml version="1.0" encoding="UTF-8"?><beans:beansxmlns:beans="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="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"><repositoriesbase-package="com.acme.repositories"/></beans:beans>[/code] 
在上面的例子中,Spring被配置成扫描
com.acme.repositories
和其所有子包,以查找所有继承自
Repository
或其子接口的接口。对于每个找到的接口,infrastructure会注册持久化技术特定的
FactoryBean
,以创建恰当的代理,从而处理查询方法的调用。每个bean都被注册了一个bean name,默认从接口名字中获取,因此一个
UserRepository
接口,可能被注册成
userRepository
base-package
attribute 允许通配符,所以,你还可以定义一个pattern。

使用过滤器

默认,the infrastructure会捡起每一个继承自持久化技术特定的
Repository
及其子接口的接口 -- 要在base package之下,并创建一个bean实例。但是,你或许想要更细化的控制。这时,你需要使用 <repositories /> 中的
<include-filter />
<exclude-filter />
elements。详见 Spring 参考文档

例如,想要排除特定接口,你可能会使用下面的配置:

例子 19. 使用 exclude-filter element

<repositoriesbase-package="com.acme.repositories"><context:exclude-filtertype="regex"expression=".*SomeRepository"/></repositories>


该例子排除了所有以
SomeRepository
结尾的内容。

2.5.2. JavaConfig

The repository infrastructure 也可以通过在一个JavaConfig class上使用
@Enable${store}Repositories
注解来开启。

下面是一个简单的例子。

例子 20. 样例 基于注解的repository configuration

@Configuration@EnableJpaRepositories("com.acme.repositories")classApplicationConfiguration{@BeanpublicEntityManagerFactory entityManagerFactory(){// …}}


该样例,使用了JPA特有的注解,你可能会切换成其他的。同样的情况也适用于
EntityManagerFactory
bean。

2.5.3. 单独使用 Standalone usage

你可以在Spring container之外使用 the repository infrastructure,例如,在CDI 环境中。你仍然需要一些Spring lib,但一般,你也可以通过编码设置repositories。

例子 21. 单独使用 repository factory

RepositoryFactorySupport factory =…// Instantiate factory hereUserRepository repository = factory.getRepository(UserRepository.class);


2.6. Spring Data repositories的自定义实现

经常,需要为几个repository方法提供一个自定义的实现。Spring Data repositories允许你提供自定义的 repository 代码,并将其集成到 generic CRUD abstraction中。

2.6.1. 为某个repositories 添加自定义行为

为了丰富一个带有自定义功能的repository,你需要先定义一个接口,以及接口中自定义功能的实现。用你的repository 接口来继承这个自定义的接口。

例子 22. 自定义repository功能的接口

interfaceUserRepositoryCustom{publicvoid someCustomMethod(User user);}


例子 23. 自定义repository功能的实现

classUserRepositoryImplimplementsUserRepositoryCustom{publicvoid someCustomMethod(User user){// Your custom implementation}}


注意:这里最重要的就是后缀
Impl

该实现,不依赖于 Spring Data,可以作为一个常规的 Spring bean。因此,你可以使用标准的依赖注入方式将其注入到其他bean中,如 JdbcTemplate。

例子 24. 更改你的基础repository 接口

interfaceUserRepositoryextendsCrudRepository<User,Long>,UserRepositoryCustom{// Declare query methods here}


让你的标准的 repository interface继承这个自定义的接口。

Configuration 配置

如果你使用namespace 配置, the repository infrastructure会试着自动探测自定义的实现,通过扫描base package中的classes。这些classes,需要遵守命名惯例,即在namespace element的 attribute
repository-impl-postfix
中设置的。该后缀默认是
Impl


例子 25. 配置例子

<repositoriesbase-package="com.acme.repository"/><repositoriesbase-package="com.acme.repository"repository-impl-postfix="FooBar"/>


第一个配置,会试着查找一个class:
com.acme.repository.UserRepositoryImpl
,将其作为自定义的repository 实现;第二个配置,则会试着查找
com.acme.repository.UserRepositoryFooBar


Manual wiring 手动注入 (略)

The approach just shown works well if your custom implementation uses annotation-based configuration and autowiring only, as it will be treated as any other Spring bean. If your custom implementation bean needs special wiring, you simply declare the bean and name it after the conventions just described. The infrastructure will then refer to the manually defined bean definition by name instead of creating one itself.

Example 26. Manual wiring of custom implementations

<repositoriesbase-package="com.acme.repository"/><beans:beanid="userRepositoryImpl"class="…"><!-- further configuration --></beans:bean>


2.6.2. 为所有repositories 添加自定义行为 (暂不需要,略)

The preceding approach is not feasible when you want to add a single method to all your repository interfaces. To add custom behavior to all repositories, you first add an intermediate interface to declare the shared behavior.

Example 27. An interface declaring custom shared behavior

@NoRepositoryBeanpublicinterfaceMyRepository<T, ID extendsSerializable>extendsPagingAndSortingRepository<T, ID>{void sharedCustomMethod(ID id);}


Now your individual repository interfaces will extend this intermediate interface instead of the
Repository
interface to include the functionality declared. Next, create an implementation of the intermediate interface that extends the persistence technology-specific repository base class. This class will then act as a custom base class for the repository proxies.

Example 28. Custom repository base class

publicclassMyRepositoryImpl<T, ID extendsSerializable>extendsSimpleJpaRepository<T, ID>implementsMyRepository<T, ID>{privatefinalEntityManager entityManager;publicMyRepositoryImpl(JpaEntityInformation entityInformation,EntityManager entityManager){super(entityInformation, entityManager);// Keep the EntityManager around to used from the newly introduced methods.this.entityManager = entityManager;}publicvoid sharedCustomMethod(ID id){// implementation goes here}}


The class needs to have a constructor of the super class which the store-specific repository factory implementation is using. In case the repository base class has multiple constructors, override the one taking an
EntityInformation
plus a store specific infrastructure object (e.g. an
EntityManager
or a template class).
The default behavior of the Spring
<repositories />
namespace is to provide an implementation for all interfaces that fall under the
base-package
. This means that if left in its current state, an implementation instance of
MyRepository
will be created by Spring. This is of course not desired as it is just supposed to act as an intermediary between
Repository
and the actual repository interfaces you want to define for each entity. To exclude an interface that extends
Repository
from being instantiated as a repository instance, you can either annotate it with
@NoRepositoryBean
(as seen above) or move it outside of the configured
base-package
.

The final step is to make the Spring Data infrastructure aware of the customized repository base class. In JavaConfig this is achieved by using the
repositoryBaseClass
attribute of the
@Enable…Repositories
annotation:

Example 29. Configuring a custom repository base class using JavaConfig

@Configuration@EnableJpaRepositories(repositoryBaseClass =MyRepositoryImpl.class)classApplicationConfiguration{…}


A corresponding attribute is available in the XML namespace.

Example 30. Configuring a custom repository base class using XML

<repositoriesbase-package="com.acme.repository"base-class="….MyRepositoryImpl"/>


2.7. 从aggregate roots发布事件

被repositories管理的entities,被称作aggregate roots。在一个Domain-Driven Design 应用中,这些aggregate roots 一般会发布domain events。Spring Data提供了一个注解
@DomainEvents
,你可以用于一个方法上,从而使得发布事件足够简单。

Example 31. Exposing domain events from an aggregate root

classAnAggregateRoot{@DomainEvents //(1)Collection<Object> domainEvents(){// … return events you want to get published here}@AfterDomainEventsPublication //(2)void callbackMethod(){// … potentially clean up domain events list}}


① 该方法使用注解
@DomainEvents
,既可以返回一个事件实例,也可以返回事件的一个集合。注意,必须无参数。
② 当所有的事件都被发布之后,带有注解
@AfterDomainEventsPublication
的方法。它,例如,可以用于潜在地清理事件列表。

该方法,会在Spring Data repository’s
save(…)
每次被调用时执行。

2.8. Spring Data extensions (没啥意思 略)

This section documents a set of Spring Data extensions that enable Spring Data usage in a variety of contexts. Currently most of the integration is targeted towards Spring MVC.

2.8.1. Querydsl Extension

Querydsl is a framework which enables the construction of statically typed SQL-like queries via its fluent API.

Several Spring Data modules offer integration with Querydsl via
QueryDslPredicateExecutor
.

Example 32. QueryDslPredicateExecutor interface

publicinterfaceQueryDslPredicateExecutor<T>{

T findOne(Predicate predicate);             //(1)Iterable<T> findAll(Predicate predicate);   //(2)long count(Predicate predicate);            //(3)boolean exists(Predicate predicate);        //(4)// … more functionality omitted.}


① 查找并返回一个能够匹配Predicate 的entity。
② 查找并返回所有能够匹配Predicate 的entities。
③ 返回能够匹配Predicate 的entities的数量。
④ 返回是否存在能够匹配Predicate 的entity。

To make use of Querydsl support simply extend
QueryDslPredicateExecutor
on your repository interface.

Example 33. Querydsl integration on repositories

interfaceUserRepositoryextendsCrudRepository<User,Long>,QueryDslPredicateExecutor<User>{}


The above enables to write typesafe queries using Querydsl
Predicate
s.

Predicate predicate = user.firstname.equalsIgnoreCase("dave").and(user.lastname.startsWithIgnoreCase("mathews"));

userRepository.findAll(predicate);


2.8.2. Web support (没啥意思 略)

This section contains the documentation for the Spring Data web support as it is implemented as of Spring Data Commons in the 1.6 range. As it the newly introduced support changes quite a lot of things we kept the documentation of the former behavior in Legacy web support.
Spring Data modules ships with a variety of web support if the module supports the repository programming model. The web related stuff requires Spring MVC JARs on the classpath, some of them even provide integration with Spring HATEOAS [2]. In general, the integration support is enabled by using the
@EnableSpringDataWebSupport
annotation in your JavaConfig configuration class.

Example 34. Enabling Spring Data web support

@Configuration@EnableWebMvc@EnableSpringDataWebSupportclassWebConfiguration{}


The
@EnableSpringDataWebSupport
annotation registers a few components we will discuss in a bit. It will also detect Spring HATEOAS on the classpath and register integration components for it as well if present.

Alternatively, if you are using XML configuration, register either
SpringDataWebSupport
or
HateoasAwareSpringDataWebSupport
as Spring beans:

Example 35. Enabling Spring Data web support in XML

<beanclass="org.springframework.data.web.config.SpringDataWebConfiguration"/><!-- If you're using Spring HATEOAS as well register this one *instead* of the former --><beanclass="org.springframework.data.web.config.HateoasAwareSpringDataWebConfiguration"/>


Basic web support

The configuration setup shown above will register a few basic components:

A
DomainClassConverter
to enable Spring MVC to resolve instances of repository managed domain classes from request parameters or path variables.

HandlerMethodArgumentResolver
implementations to let Spring MVC resolve Pageable and Sort instances from request parameters.

DomainClassConverter

The
DomainClassConverter
allows you to use domain types in your Spring MVC controller method signatures directly, so that you don’t have to manually lookup the instances via the repository:

Example 36. A Spring MVC controller using domain types in method signatures

@Controller@RequestMapping("/users")publicclassUserController{@RequestMapping("/{id}")publicString showUserForm(@PathVariable("id")User user,Model model){

model.addAttribute("user", user);return"userForm";}}


As you can see the method receives a User instance directly and no further lookup is necessary. The instance can be resolved by letting Spring MVC convert the path variable into the id type of the domain class first and eventually access the instance through calling
findOne(…)
on the repository instance registered for the domain type.

Currently the repository has to implement
CrudRepository
to be eligible to be discovered for conversion.
HandlerMethodArgumentResolvers for Pageable and Sort

The configuration snippet above also registers a
PageableHandlerMethodArgumentResolver
as well as an instance of
SortHandlerMethodArgumentResolver
. The registration enables
Pageable
and
Sort
being valid controller method arguments

Example 37. Using Pageable as controller method argument

@Controller@RequestMapping("/users")publicclassUserController{@AutowiredUserRepository repository;@RequestMappingpublicString showUsers(Model model,Pageable pageable){

model.addAttribute("users", repository.findAll(pageable));return"users";}}


This method signature will cause Spring MVC try to derive a Pageable instance from the request parameters using the following default configuration:

Table 1. Request parameters evaluated for Pageable instances

page


Page you want to retrieve, 0 indexed and defaults to 0.

size


Size of the page you want to retrieve, defaults to 20.

sort


Properties that should be sorted by in the format
property,property(,ASC|DESC)
. Default sort direction is ascending. Use multiple
sort
parameters if you want to switch directions, e.g.
?sort=firstname&sort=lastname,asc
.

To customize this behavior extend either
SpringDataWebConfiguration
or the HATEOAS-enabled equivalent and override the
pageableResolver()
or
sortResolver()
methods and import your customized configuration file instead of using the
@Enable
-annotation.

In case you need multiple
Pageable
or
Sort
instances to be resolved from the request (for multiple tables, for example) you can use Spring’s
@Qualifier
annotation to distinguish one from another. The request parameters then have to be prefixed with
${qualifier}_
. So for a method signature like this:

publicString showUsers(Model model,@Qualifier("foo")Pageable first,@Qualifier("bar")Pageable second){…}


you have to populate
foo_page
and
bar_page
etc.

The default
Pageable
handed into the method is equivalent to a
new PageRequest(0, 20)
but can be customized using the
@PageableDefaults
annotation on the
Pageable
parameter.

Hypermedia support for Pageables

Spring HATEOAS ships with a representation model class
PagedResources
that allows enriching the content of a
Page
instance with the necessary
Page
metadata as well as links to let the clients easily navigate the pages. The conversion of a Page to a
PagedResources
is done by an implementation of the Spring HATEOAS
ResourceAssembler
interface, the
PagedResourcesAssembler
.

Example 38. Using a PagedResourcesAssembler as controller method argument

@ControllerclassPersonController{@AutowiredPersonRepository repository;@RequestMapping(value ="/persons", method =RequestMethod.GET)HttpEntity<PagedResources<Person>> persons(Pageable pageable,PagedResourcesAssembler assembler){Page<Person> persons = repository.findAll(pageable);returnnewResponseEntity<>(assembler.toResources(persons),HttpStatus.OK);}}


Enabling the configuration as shown above allows the
PagedResourcesAssembler
to be used as controller method argument. Calling
toResources(…)
on it will cause the following:

The content of the
Page
will become the content of the
PagedResources
instance.

The
PagedResources
will get a
PageMetadata
instance attached populated with information form the
Page
and the underlying
PageRequest
.

The
PagedResources
gets
prev
and
next
links attached depending on the page’s state. The links will point to the URI the method invoked is mapped to. The pagination parameters added to the method will match the setup of the
PageableHandlerMethodArgumentResolver
to make sure the links can be resolved later on.

Assume we have 30 Person instances in the database. You can now trigger a request
GET http://localhost:8080/persons
and you’ll see something similar to this:

{"links":[{"rel":"next","href":"http://localhost:8080/persons?page=1&size=20 }],"content":[…// 20 Person instances rendered here],"pageMetadata":{"size":20,"totalElements":30,"totalPages":2,"number":0}}


You see that the assembler produced the correct URI and also picks up the default configuration present to resolve the parameters into a
Pageable
for an upcoming request. This means, if you change that configuration, the links will automatically adhere to the change. By default the assembler points to the controller method it was invoked in but that can be customized by handing in a custom
Link
to be used as base to build the pagination links to overloads of the
PagedResourcesAssembler.toResource(…)
method.

Querydsl web support

For those stores having QueryDSL integration it is possible to derive queries from the attributes contained in a
Request
query string.

This means that given the
User
object from previous samples a query string

?firstname=Dave&lastname=Matthews


can be resolved to

QUser.user.firstname.eq("Dave").and(QUser.user.lastname.eq("Matthews"))


using the
QuerydslPredicateArgumentResolver
.

The feature will be automatically enabled along
@EnableSpringDataWebSupport
when Querydsl is found on the classpath.
Adding a
@QuerydslPredicate
to the method signature will provide a ready to use
Predicate
which can be executed via the
QueryDslPredicateExecutor
.

Type information is typically resolved from the methods return type. Since those information does not necessarily match the domain type it might be a good idea to use the
root
attribute of
QuerydslPredicate
.
@ControllerclassUserController{@AutowiredUserRepository repository;@RequestMapping(value ="/", method =RequestMethod.GET)String index(Model model,@QuerydslPredicate(root =User.class)Predicate predicate,    //(1)Pageable pageable,@RequestParamMultiValueMap<String,String> parameters){

model.addAttribute("users", repository.findAll(predicate, pageable));return"index";}}


① Resolve query string arguments to matching
Predicate
for
User
.

The default binding is as follows:

Object
on simple properties as
eq
.

Object
on collection like properties as
contains
.

Collection
on simple properties as
in
.

Those bindings can be customized via the
bindings
attribute of
@QuerydslPredicate
or by making use of Java 8
default methods
adding the
QuerydslBinderCustomizer
to the repository interface.

interfaceUserRepositoryextendsCrudRepository<User,String>,QueryDslPredicateExecutor<User>,(1)QuerydslBinderCustomizer<QUser>{(2)@Overridedefaultpublicvoid customize(QuerydslBindings bindings,QUser user){

bindings.bind(user.username).first((path, value)-> path.contains(value))(3)
bindings.bind(String.class).first((StringPath path,String value)-> path.containsIgnoreCase(value));(4)
bindings.excluding(user.password);(5)}}


1
QueryDslPredicateExecutor
provides access to specific finder methods for
Predicate
.
2
QuerydslBinderCustomizer
defined on the repository interface will be automatically picked up and shortcuts
@QuerydslPredicate(bindings=…)
.
3Define the binding for the
username
property to be a simple contains binding.
4Define the default binding for
String
properties to be a case insensitive contains match.
5Exclude the password property from
Predicate
resolution.

2.8.3. Repository populators (没啥意思 略)

If you work with the Spring JDBC module, you probably are familiar with the support to populate a
DataSource
using SQL scripts. A similar abstraction is available on the repositories level, although it does not use SQL as the data definition language because it must be store-independent. Thus the populators support XML (through Spring’s OXM abstraction) and JSON (through Jackson) to define data with which to populate the repositories.

Assume you have a file
data.json
with the following content:

Example 39. Data defined in JSON

[{"_class":"com.acme.Person","firstname":"Dave","lastname":"Matthews"},{"_class":"com.acme.Person","firstname":"Carter","lastname":"Beauford"}]


You can easily populate your repositories by using the populator elements of the repository namespace provided in Spring Data Commons. To populate the preceding data to your PersonRepository , do the following:

Example 40. Declaring a Jackson repository populator

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:repository="http://www.springframework.org/schema/data/repository"xsi:schemaLocation="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"><repository:jackson2-populatorlocations="classpath:data.json"/></beans>[/code] 
This declaration causes the
data.json
file to be read and deserialized via a Jackson
ObjectMapper
.

The type to which the JSON object will be unmarshalled to will be determined by inspecting the
_class
attribute of the JSON document. The infrastructure will eventually select the appropriate repository to handle the object just deserialized.

To rather use XML to define the data the repositories shall be populated with, you can use the
unmarshaller-populator
element. You configure it to use one of the XML marshaller options Spring OXM provides you with. See the Spring 参考文档 for details.

Example 41. Declaring an unmarshalling repository populator (using JAXB)

<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:repository="http://www.springframework.org/schema/data/repository"xmlns:oxm="http://www.springframework.org/schema/oxm"xsi:schemaLocation="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"><repository:unmarshaller-populatorlocations="classpath:data.json"unmarshaller-ref="unmarshaller"/><oxm:jaxb2-marshallercontextPath="com.acme"/></beans>[/code] 

2.8.4. Legacy web support (没啥意思 略)

Domain class web binding for Spring MVC

Given you are developing a Spring MVC web application you typically have to resolve domain class ids from URLs. By default your task is to transform that request parameter or URL part into the domain class to hand it to layers below then or execute business logic on the entities directly. This would look something like this:

@Controller@RequestMapping("/users")publicclassUserController{privatefinalUserRepository userRepository;@AutowiredpublicUserController(UserRepository userRepository){Assert.notNull(repository,"Repository must not be null!");this.userRepository = userRepository;}@RequestMapping("/{id}")publicString showUserForm(@PathVariable("id")Long id,Model model){// Do null check for idUser user = userRepository.findOne(id);// Do null check for user

model.addAttribute("user", user);return"user";}}


First you declare a repository dependency for each controller to look up the entity managed by the controller or repository respectively. Looking up the entity is boilerplate as well, as it’s always a
findOne(…)
call. Fortunately Spring provides means to register custom components that allow conversion between a
String
value to an arbitrary type.

PropertyEditors

For Spring versions before 3.0 simple Java
PropertyEditors
had to be used. To integrate with that, Spring Data offers a
DomainClassPropertyEditorRegistrar
, which looks up all Spring Data repositories registered in the
ApplicationContext
and registers a custom
PropertyEditor
for the managed domain class.

<beanclass="….web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"><propertyname="webBindingInitializer"><beanclass="….web.bind.support.ConfigurableWebBindingInitializer"><propertyname="propertyEditorRegistrars"><beanclass="org.springframework.data.repository.support.DomainClassPropertyEditorRegistrar"/></property></bean></property></bean>


If you have configured Spring MVC as in the preceding example, you can configure your controller as follows, which reduces a lot of the clutter and boilerplate.

@Controller@RequestMapping("/users")publicclassUserController{@RequestMapping("/{id}")publicString showUserForm(@PathVariable("id")User user,Model model){

model.addAttribute("user", user);return"userForm";}}


3. Query by Example

3.1.介绍

本章节会介绍 使用Example进行查询,并解释如何使用。

Query by Example (QBE) ,是一种用户友好的查询技术。它允许动态查询创建,不需要写包含字段名字的查询。实际上,QBE完全不要求写特定存储的查询语言。

3.2.使用

使用Example查询由三部分组成:

Probe: 一个domain object的具体example。

ExampleMatcher
:
ExampleMatcher
携带了如何匹配特定的字段的详细信息。可以在多个Examples之间复用。

Example
: 由probe 和
ExampleMatcher
构成的Example。被用于创建查询。

Query by Example 适合一些用例,但仍有其局限性:

什么时候使用

使用一组静态或动态限制(constraints)来查询时。

经常重构 domain objects,而不需要担心破坏现有查询。

独立于底层的数据存储API。

局限

不支持嵌套的/分组的 property constraints,如
firstname = ?0 or (firstname = ?1 and lastname = ?2)


仅支持字符串的 starts/contains/ends/regex 匹配和其他类型的精确匹配。

在开始使用 Query by Example之前,你需要有一个 domain object。先为你的repository简单地创建一个接口:

例子 42. 样例 Person object

publicclassPerson{@IdprivateString id;privateString firstname;privateString lastname;privateAddress address;// … getters and setters omitted}


这是一个简单的 domain object。你可以使用它来创建一个
Example
。默认,值为null的字段会被忽略,字符串使用存储的默认匹配。可以使用Example.of() (这是一个工厂方法)创建Examples,也可以使用
ExampleMatcher
.。
Example
是不可改变的。

例子 43. 简单 Example

Person person =newPerson();(1)
person.setFirstname("Dave");(2)Example<Person> example =Example.of(person);(3)


① 创建domain object的实例。
② 设置要查询的properties。
③ 创建Example。

Examples are ideally be executed with repositories. To do so, let your repository interface extend
QueryByExampleExecutor<T>
. Here’s an excerpt from the
QueryByExampleExecutor
interface:

Example 44. The
QueryByExampleExecutor


publicinterfaceQueryByExampleExecutor<T>{<S extends T> S findOne(Example<S> example);<S extends T>Iterable<S> findAll(Example<S> example);// … more functionality omitted.}


You can read more about Query by Example Execution below.

3.3. Example matchers

Examples are not limited to default settings. You can specify own defaults for string matching, null handling and property-specific settings using the
ExampleMatcher
.

Example 45. Example matcher with customized matching

Person person =newPerson();(1)
person.setFirstname("Dave");(2)ExampleMatcher matcher =ExampleMatcher.matching()(3).withIgnorePaths("lastname")(4).withIncludeNullValues()(5).withStringMatcherEnding();(6)Example<Person> example =Example.of(person, matcher);(7)


① 创建domain object的一个实例。
② 设置properties。
③ 创建一个
ExampleMatcher
,期望所有的values都匹配。即便没有更多的配置,它也是可用的。
④ 构造一个新的
ExampleMatcher
,忽略property path lastname 。
⑤ 构造一个新的
ExampleMatcher
,忽略property path lastname,并包含所有的null values。
⑥ 构造一个 新的
ExampleMatcher
,忽略property path lastname,并包含所有的null values,并使用末尾字符串匹配。
⑦ 创建一个新的
Example
,基于domain object和配置好的
ExampleMatcher


默认,the
ExampleMatcher
会期望probe里设置的所有的 values 都匹配。你还可以使用
ExampleMatcher.matchingAny()
.

你可以为单独的properties指定行为 (e.g. "firstname" and "lastname", "address.city" for nested properties)。你可以调节匹配选项和大小写敏感性。

例子 46. 配置matcher选项

ExampleMatcher matcher =ExampleMatcher.matching().withMatcher("firstname", endsWith()).withMatcher("lastname", startsWith().ignoreCase());}


另一种方式来配置 matcher选项,是使用 Java 8 lambdas。这种一种回调,要求实现者修改matcher。不要求返回matcher,因为配置选项是在matcher实例中保持的。

例子 47. 使用lambdas来配置 matcher选项

ExampleMatcher matcher =ExampleMatcher.matching().withMatcher("firstname", match -> match.endsWith()).withMatcher("firstname", match -> match.startsWith());}


使用Example
创建的查询,使用了一种融合的配置view。默认的匹配设置,可以在
ExampleMatcher
级别上设置,同时单个的设置也可以应用到特定的property paths上。设置值
ExampleMatcher
上的设置,会被property path settings继承,除非它们( property path settings)有显式地定义-- 这样有更高的优先级。

Table 2. Scope of
ExampleMatcher
settings
SettingScope
Null-handling

ExampleMatcher


String matching

ExampleMatcher
and property path

Ignoring properties

Property path

Case sensitivity

ExampleMatcher
and property path

Value transformation

Property path

4. Auditing 审计

4.1. 基础

Spring Data提供了复杂的支持,以透明地跟踪谁创建或更改了entity,以及发生的时间。为了利用这种功能,你需要为你的entity classes装备上auditing metadata -- 可以使用注解,或者通过实现特定的接口。

4.1.1. 基于注解的auditing metadata

我们提供了
@CreatedBy
@LastModifiedBy
来捕获创建或修改entity的用户,还提供了
@CreatedDate
@LastModifiedDate
来捕获发生的时间。

例子 48. 一个被审计的entity

classCustomer{@CreatedByprivateUser user;@CreatedDateprivateDateTime createdDate;// … further properties omitted}


如你所见,可以有选择地适用注解,取决于你想捕获哪些信息。捕获时间的注解,可以用于properties of type JodaTimes
DateTime
、传统的 Java
Date
Calendar
、JDK8 date/time types,以及
long
/
Long


4.1.2. 基于接口的auditing metadata

如果你不想使用注解来定义auditing metadata,你可以让你的domain class实现
Auditable
接口。它暴露了所有auditing properties的setter methods。

还有一个便捷的基类
AbstractAuditable
可以继承,以避免需要手动实现接口方法。注意,这增加了与Spring的耦合,这是我们极力避免的。通常推荐使用注解方式,因为更少侵入,更加弹性。

4.1.3. AuditorAware

当你使用
@CreatedBy
@LastModifiedBy
时,the auditing infrastructure需要知道当前的 principal。我们提供了一个
AuditorAware<T>
SPI 接口,你必须实现,以告诉the infrastructure当前用户或者系统是什么。泛型
T
定义了什么类型的 properties注解了
@CreatedBy
@LastModifiedBy


这里是一个示例实现了该接口,使用Spring Security的
Authentication
object:

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

classSpringSecurityAuditorAwareimplementsAuditorAware<User>{publicUser getCurrentAuditor(){Authentication authentication =SecurityContextHolder.getContext().getAuthentication();if(authentication ==null||!authentication.isAuthenticated()){returnnull;}return((MyUserDetails) authentication.getPrincipal()).getUser();}}


附录

附录 A: Namespace reference

The <repositories /> element

The
<repositories />
element triggers the setup of the Spring Data repository infrastructure. The most important attribute is
base-package
which defines the package to scan for Spring Data repository interfaces.[3]

Table 3. Attributes
NameDescription
base-package


Defines the package to be used to be scanned for repository interfaces extending *Repository (actual interface is determined by specific Spring Data module) in auto detection mode. All packages below the configured package will be scanned, too. Wildcards are allowed.

repository-impl-postfix


Defines the postfix to autodetect custom repository implementations. Classes whose names end with the configured postfix will be considered as candidates. Defaults to
Impl
.

query-lookup-strategy


Determines the strategy to be used to create finder queries. See Query lookup strategies for details. Defaults to
create-if-not-found
.

named-queries-location


Defines the location to look for a Properties file containing externally defined queries.

consider-nested-repositories


Controls whether nested repository interface definitions should be considered. Defaults to
false
.

附录 B: Populators namespace reference

The <populator /> element

The
<populator />
element allows to populate the a data store via the Spring Data repository infrastructure.[4]

Table 4. Attributes
NameDescription
locations


Where to find the files to read the objects from the repository shall be populated with.

附录 C: Repository查询关键字

支持的查询关键字

The following table lists the keywords generally supported by the Spring Data repository query derivation mechanism. However, consult the store-specific documentation for the exact list of supported keywords, because some listed here might not be supported in a particular store.

Table 5. Query keywords
Logical keywordKeyword expressions
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
, (or no keyword)

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: Repository查询的返回类型

支持的查询的返回类型

The following table lists the return types generally supported by Spring Data repositories. However, consult the store-specific documentation for the exact list of supported return types, because some listed here might not be supported in a particular store.

Geospatial types like (
GeoResult
,
GeoResults
,
GeoPage
) are only available for data stores that support geospatial queries.
Table 6. Query return types
Return typeDescription
void


Denotes no return value.

Primitives

Java primitives.

Wrapper types

Java wrapper types.

T


An unique entity. Expects the query method to return one result at most. In case no result is found
null
is returned. More than one result will trigger an
IncorrectResultSizeDataAccessException
.

Iterator<T>


An
Iterator
.

Collection<T>


A
Collection
.

List<T>


A
List
.

Optional<T>


A Java 8 or Guava
Optional
. Expects the query method to return one result at most. In case no result is found
Optional.empty()
/
Optional.absent()
is returned. More than one result will trigger an
IncorrectResultSizeDataAccessException
.

Option<T>


An either Scala or JavaSlang
Option
type. Semantically same behavior as Java 8’s
Optional
described above.

Stream<T>


A Java 8
Stream
.

Future<T>


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

CompletableFuture<T>


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

ListenableFuture


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

Slice


A sized chunk of data with information whether there is more data available. Requires a
Pageable
method parameter.

Page<T>


A
Slice
with additional information, e.g. the total number of results. Requires a
Pageable
method parameter.

GeoResult<T>


A result entry with additional information, e.g. distance to a reference location.

GeoResults<T>


A list of
GeoResult<T>
with additional information, e.g. average distance to a reference location.

GeoPage<T>


A
Page
with
GeoResult<T>
, e.g. average distance to a reference location.

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: