- 内容提要
- 序
- 前言
- 第一部分 背景知识
- 第 1 章 Spring Data 项目
- 第 2 章 Repository:便利的数据访问层
- 第 3 章 使用 Querydsl 实现类型安全的查询
- 第二部分 关系型数据库
- 第 4 章 JPA Repository
- 第 5 章 借助 Querydsl SQL 实现类型安全的 JDBC 编程
- 第三部分 NoSQL
- 第 6 章 MongoDB: 文档存储
- 第 7 章 Neo4j:图数据库
- 第 8 章 Redis:键/值存储
- 第四部分 快速应用开发
- 第 9 章 使用 Spring Roo 实现持久层
- 第 10 章 REST Repository 导出器
- 第五部分 大数据
- 第 11 章 Spring for Apache Hadoop
- 第 12 章 使用 Hadoop 分析数据
- 第 13 章 使用 Spring Batch 和 Spring Integration 创建大数据管道
- 第六部分 数据网格
- 第 14 章 分布式数据网格:GemFire
- 关于封面
2.3 定义 Repository
到目前为止,我们看到了带有查询方法的 Repository 接口,这些查询有的是从方法名中衍生出来的,有的是手动声明的,这取决于 Spring Data 为实际存储类型所提供的使用方式。为了衍生出这些查询,我们必须扩展 Spring Data 的特定标识接口:Repository。除了查询以外,在你的 Repository 中还需要一些其他的功能:存储对象,删除对象,根据 ID 进行查找,返回所有存储的实体或按页对它们进行访问。通过 Repository 接口来暴露这些功能的最简单方式就是使用一个 Spring Data 所提供的更为高级的 Repository 接口。
Repository
一个简单的标识接口,允许 Spring Data 的基础设施获取用户定义的 Repository。
CrudRepository
扩展自 Repository 并添加了基本的持久化方法如对实体的保存、查找以及删除。
PagingAndSortingRepositories
扩展自 CrudRepository 并添加了按页访问实体以及根据给定的条件(criteria)进行排序的方法。
假设我们想让 CustomerRepository 暴露基本的 CRUD 方法,所需要做就是修改其声明,如示例 2-12 所示。
示例 2-12 暴露 CRUD 方法的 CustomerRepository
CrudRepository 接口如示例 2-13 所示。它包括了保存单个实体以及多个 Iterable 实体的方法、获取单个实体或所有实体的方法以及不同形式的 delete(...) 方法。
示例 2-13 CrudRepository
支持 Repository 方式的每个 Spring Data 模块都提供了这个接口的实现。因此,我们声明的命名空间元素会触发基础设施,这些设施不仅会启动那些用于执行查询方法的合适代码,同时还会使用一个通用 Repository 实现类的实例来在背后执行 CrudRepository 中所声明的方法,最终会将 save(...)、findAll() 等方法的调用委托给该实例。PagingAndSortingRepository(如示例 2-14 所示)扩展了 CrudRepository 并为通用的 findAll(...) 添加了处理 Pageable 和 Sort 实例的方法,从而能够实现逐页访问实体。
示例 2-14 PagingAndSortingRepository
要将这些功能引入到 CustomerRepository 中,只需简单地扩展 PagingAndSorting Repository 来取代 CrudRepository 即可。
2.3.1 调整 Repository 接口
正如我们在前面所见,通过扩展合适的 Spring Data 接口,可以很容易地引入大量预先定义的功能。这种级别的粒度实际上是一种权衡,那就是如果为所有的查找方法、所有的保存方法等都定义单独的接口,我们会暴露接口的数量(以及因此导致的复杂性)以及开发人员使用的便利性之间的权衡。
但是,可能会有这样的场景,那就是只想暴露读方法(CRUD 中的 R)或者只想在 Repository 接口中将删除方法屏蔽掉。如今,Spring Data 允许定义个性化的基础 Repository,只需按照以下的步骤操作即可。
1.创建一个接口,这个接口要么扩展自 Repository,要么添加 @RepositoryDefinition 注解。
2.添加想要暴露的方法并确保它们与 Spring Data 基础 Repository 接口所提供的方法签名相同。
3.对于实体所对应的接口声明,要使用这个接口作为基础接口。
为了阐述这一点,假设我们只想暴露接收 Pageable 的 findAll(...) 方法以及 save 方法。这个基础接口看起来可能如示例 2-15 所示。
示例 2-15 自定义基础 Repository 接口
需要注意的一点是我们为这个接口添加了一个额外的注解 @NoRepositoryBean,从而确保 Spring Data Repository 的基础设施不会试图为其创建 Bean 的实例。让 CustomerRepository 扩展这个接口就能精确做到只暴露你所定义的 API。
接下来可以定义出各种基本的接口(如 ReadOnlyRepository 或 SaveOnlyRepository)甚至组成它们的继承体系,这取决于项目的需要。通常建议本地定义的 CRUD 方法在开始的时候直接位于每个实体的具体 Repository 中,必要的话,再将它们要么转移到 Spring Data 提供的基础 Repository 中,要么转移到特制的 Repository 中。按照这种方式,可以保证随着项目复杂性的增长,构件(artifact)的数量能够自然地增长。
2.3.2 手动实现 Repository 方法
到目前为止,看到了两种类型的 Repository 方法:CRUD 方法和查询方法。每种类型都是由 Spring Data 的基础设施实现的,要么通过背后的实现类,要么通过查询执行引擎。当构建应用程序的时候,这两种场景可能会覆盖你所面临的很大范围的数据访问操作。但是,有些场景需要手动实现代码。现在,让我们看一下如何做到这一点。
我们开始只实现那些需要手动实现的功能并在实现类中遵循一些命名的约定,如示例 2-16 所示。
示例 2-16 为 Repository 实现自定义功能
接口和实现类均不需要了解 Spring Data 的任何事情。它与使用 Spring 手动实现代码非常类似。按照 Spring Data 来看,这个代码片段最有意思的地方在于实现类的名字遵循了命名的约定,也就是在核心 Repository 接口(在我们的场景中就是 CustomerRepository)的名字上加 Impl 后缀。同时需要注意,我们将接口和实现类都设为包内私有(package private),从而阻止从包外访问它们。
最后一步是修改初始 Repository 接口的声明,使其扩展刚刚引入的接口,如示例 2-17 所示。
示例 2-17 在 CustomerRepository 中包含自定义功能
现在,我们已经将 CustomerRepositoryCustom 暴露的 API 引入到 CustomerRepository 之中了,这会使其成为 Customer 数据访问 API 的中心点。客户端代码现在就可以调用 CustomerRepository.myCustomMethod(...) 了。但是,这个实现类会如何被发现并置于最终执行的代理之中的呢?实际上,Repository 的启动过程看起来是这样的。
1.发现 repository 接口(如 CustomerRepository)。
2.尝试寻找一个 Bean 定义,这个 Bean 的名字为接口的小写形式并添加 Impl 后缀(如 customerRepositoryImpl)。如果能够找到,就使用它。
3.如果没有找到,我们会扫描寻找一个类,这个类的名字为核心 Repository 接口的名字并添加 Impl 后缀(例如,在这个例子中 CustomerRepositoryImpl 会被找到)。如果找到了这样的类,那么将其注册为 Spring Bean 并使用它。
4.找到的自定义实现类将会装配到被发现接口的代理配置之中并且在方法调用时会作为潜在的目标类。
这种机制可以很容易地为特定 Repository 实现自定义代码。用于进行实现查找的后缀可以在 XML 命名空间中或启用 Repository 的注解属性中(查看各种存储相关的章节来了解更多)进行个性化设置。参考文档( https://bit.ly/VzYToo )中也包含了一些关于如何将自定义的行为应用于多个 Repositor 的学习材料。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论