Spring Data Commons 官方文档学习

时间:2023-12-29 13:34:20
Spring Data Commons 官方文档学习   -by LarryZeal
Version 1.12.6.Release, 2017-07-27
Table of Contents

前言

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
  1. <dependencyManagement>
  2. <dependencies>
  3. <dependency>
  4. <groupId>org.springframework.data</groupId>
  5. <artifactId>spring-data-releasetrain</artifactId>
  6. <version>${release-train}</version>
  7. <scope>import</scope>
  8. <type>pom</type>
  9. </dependency>
  10. </dependencies>
  11. </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依赖
  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework.data</groupId>
  4. <artifactId>spring-data-jpa</artifactId>
  5. </dependency>
  6. </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,只需要以下四个步骤:

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

    interfacePersonRepositoryextendsRepository<Person,Long>{…}
  2. 在该接口中声明查询方法。

    interfacePersonRepositoryextendsRepository<Person,Long>{List<Person> findByLastname(String lastname);}
  3. 设置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>

    该例子中使用了JPA namespace。如果使用其他存储的repository abstraction,你需要切换到相应的namespace,例如使用 mongodb.来代替 jpa。

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

  4. 获取注入的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接口会继承 RepositoryCrudRepository 或者 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定义:

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

  2. 如果该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>{…}

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

例子 9. 使用泛泛的接口进行Repository定义
interfaceAmbiguousRepositoryextendsRepository<User,Long>{…}@NoRepositoryBeaninterfaceMyBaseRepository<T, ID extendsSerializable>extendsCrudRepository<T, ID>{…}interfaceAmbiguousUserRepositoryextendsMyBaseRepository<User,Long>{…}

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

例子 10. 使用注解的Domain Classes进行Repository定义
interfacePersonRepositoryextendsRepository<Person,Long>{…}@EntitypublicclassPerson{…}interfaceUserRepositoryextendsRepository<User,Long>{…}@DocumentpublicclassUser{…}

PersonRepository 引用了带有JPA注解 @EntityPerson ,因此,该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 (默认):结合了 CREATEUSE_DECLARED_QUERY

2.4.2. Query 创建

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

例子 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,结合操作符(可能连接到一起)。你可以使用 ANDOR拼接。你也可以使用诸如 BetweenLessThanGreaterThanLike 之类的操作符。不同的数据存储可能支持不同的操作符,所以,最好查阅相关参考文档。

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

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

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. 限制查询结果

查询方法的结果可以使用关键字 firsttop来限制,这两个可以互换。一个可选的数值可以追加到 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>

在上面的例子中,Spring被配置成扫描 com.acme.repositories 和其所有子包,以查找所有继承自 Repository 或其子接口的接口。对于每个找到的接口,infrastructure会注册持久化技术特定的 FactoryBean ,以创建恰当的代理,从而处理查询方法的调用。每个bean都被注册了一个bean name,默认从接口名字中获取,因此一个 UserRepository 接口,可能被注册成 userRepositorybase-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=…).
3 Define the binding for the username property to be a simple contains binding.
4 Define the default binding for String properties to be a case insensitive contains match.
5 Exclude 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>

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>

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。

  • ExampleMatcherExampleMatcher 携带了如何匹配特定的字段的详细信息。可以在多个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
Setting Scope

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 DateCalendar、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
Name Description

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
Name Description

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 keyword Keyword 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 type Description

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.