5 Repository 层接口
摘要:本文详细介绍了Spring Data Elasticsearch中Repository层的设计与实现。核心是通过继承ElasticsearchRepository接口来快速获得基础CRUD功能,无需编写实现类。主要内容包括:1)何时需要创建Repository接口;2)接口定义规范(继承关系与泛型参数);3)框架自动提供的CRUD方法(增删改查及分页查询);4)派生查询方法的命名规则和使用示
·
5 Repository 层接口
5.1 简介
1.何时写?
当你需要对 Elasticsearch (ES) 中的某个 “数据表”(称为 “索引”)进行增删改查(CRUD)时。需要写这个接口。即:当我的项目中有一个实体类(比如 UserFileDocument),并且我想把这个实体类的数据存到 ES 里,或者从 ES 里查询它的数据时。
2.怎么写?
创建一个接口,继承 ElasticsearchRepository<实体类名, 主键类型> 即可。
3.为什么这么写?
继承后,Spring Data ES 框架会自动帮你实现所有基础的 CRUD 方法,你不用写任何实现代码。
4.类比
相当于 SSM 中为一张表创建一个 Dao 接口。
- SSM Dao -> 操作 MySQL 表
- 这个接口 -> 操作 ES 索引
以 3 搜索功能代码实现中的代码为例。
核心结论:
UserFileDocumentRepository 是 你手写的接口,但继承了 Spring Data Elasticsearch 框架提供的 ElasticsearchRepository 接口,因此无需手写实现类,就能直接获得 ES 文档的基础 CRUD 操作能力(框架自动生成实现)。
如果上面的东西可以看懂,就可以停止了,看不懂在继续看。
5.2 接口的 “手写部分”
你需要手动创建这个接口,并完成以下 3 件核心事情(仅需接口定义,无需写实现类):
1.手写接口本身
创建 UserFileDocumentRepository.java 文件,代码如下(这部分完全是你手写的):
package com.snapan.es.repository; // 你项目的包路径(手写)
import com.snapan.es.entity.UserFileDocument; // 你项目的 ES 实体类(手写导入)
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; // 框架提供的接口(导入)
// 你手写的接口,继承框架的 ElasticsearchRepository
public interface UserFileDocumentRepository extends ElasticsearchRepository<UserFileDocument, Long> {
// 这里可以手写自定义查询方法(可选,基础 CRUD 无需写)
}
2.关键:继承框架的 ElasticsearchRepository
这是 “不用手写实现类” 的核心 ——你手写的接口继承了 Spring Data Elasticsearch 提供的通用接口,相当于 “借用” 了框架内置的基础 CRUD 方法。
3.指定泛型参数(手写)
继承时必须指定 2 个泛型(框架规定的规则,你需要正确填写):
- 第一个泛型 UserFileDocument:你项目中对应的 ES 实体类(比如映射 ES 索引 user_file 的文档结构,类比 SSM 中 Dao 操作的 POJO);
- 第二个泛型 Long:UserFileDocument 实体的主键类型(比如实体中 id 字段是 Long 类型,类比 SSM 中 POJO 的主键类型)。
5.3 框架提供的部分
你无需关心以下内容的实现,Spring Data Elasticsearch框架全自动化处理:
1.基础 CRUD 方法的实现
ElasticsearchRepository 接口内部已经定义了一套完整的 CRUD 方法(框架写好的),你的接口继承后,就自动拥有这些方法的实现。具体方法见5.4
2.动态代理生成实现类
Spring Boot 启动时,框架会通过 动态代理技术,在运行时自动生成 UserFileDocumentRepository 接口的实现类(你看不到 .class 文件),并将其注册为 Spring Bean,供 Service 层注入使用(类比 SSM 中 MyBatis 为 Dao 接口生成代理实现)。
5.4 ElasticsearchRepository 核心方法
ElasticsearchRepository 是 Spring Data Elasticsearch 提供的核心接口,定义了操作 ES 文档的通用方法。你的自定义 Repository 接口继承它后,即可直接调用这些方法,无需关心实现。
泛型说明:ElasticsearchRepository
- T: 你要操作的 实体类(如 UserFileDocument)。
- ID: 实体类中主键(@Id 标注的字段)的 数据类型(如 Long)。
1.创建(Create)与更新(Update)
|
方法签名
|
返回值类型
|
入参说明
|
功能说明
|
|
|
S
|
entity
: 要保存的实体对象
|
新增或更新
。如果实体的 ID 在 ES 中不存在,则执行新增;如果已存在,则执行全量更新(会覆盖原文档)。返回保存后的实体对象。
|
|
|
Iterable
|
entities
: 实体对象的集合(如 List)
|
批量新增或更新
。对集合中的每个实体执行
save
操作。返回批量操作后的实体对象集合。
|
类比 SSM:save 对应 insert 或 update,saveAll 对应 batchInsert 或 batchUpdate。
2.读取(Read / Retrieve)
|
方法签名
|
返回值类型
|
入参说明
|
功能说明
|
|
Optional findById(ID id)
|
Optional
|
id
: 实体的主键 ID
|
根据 ID 查询单个实体。返回一个
Optional
对象,它可能包含查询到的实体(存在时),也可能为空(不存在时),用于避免空指针异常。
|
|
boolean existsById(ID id)
|
boolean
|
id
: 实体的主键 ID
|
判断具有指定 ID 的实体是否存在于 ES 中。存在返回
true
,否则返回
false
。
|
|
Iterable findAll()
|
Iterable
|
无
|
查询索引中的
所有
实体。返回一个可迭代的集合。
注意:数据量大时慎用
。
|
|
Iterable findAllById(Iterable ids)
|
Iterable
|
ids
: 主键 ID 的集合
|
根据一组 ID
批量查询
实体。返回包含所有找到实体的集合(未找到的 ID 会被忽略)。
|
|
Page findAll(Pageable pageable)
|
Page
|
pageable
: 分页参数(如
PageRequest.of(0, 10)
)
|
分页查询
所有实体。返回一个
Page
对象,包含了当前页的数据列表、总页数、总条数等分页信息。
|
|
long count()
|
long
|
无
|
统计索引中
所有
实体的总数量。
|
类比 SSM:findById 对应 selectById,findAll 对应 selectAll,count 对应 selectCount,Page 对应手动进行 limit 和 count(*) 查询。
3.删除(Delete)
|
方法签名
|
返回值类型
|
入参说明
|
功能说明
|
|
void deleteById(ID id)
|
void
|
id
: 实体的主键 ID
|
根据 ID
删除
单个实体。
|
|
void delete(T entity)
|
void
|
entity
: 要删除的实体对象
|
根据传入的实体对象(必须包含 ID)来删除 ES 中的对应文档。
|
|
void deleteAllById(Iterable ids)
|
void
|
ids
: 主键 ID 的集合
|
根据一组 ID
批量删除
实体。
|
|
void deleteAll(Iterable entities)
|
void
|
entities
: 要删除的实体对象集合
|
批量删除传入的实体对象集合。
|
|
void deleteAll()
|
void
|
无
|
删除索引中的所有文档
。
此操作不可逆,请极度谨慎使用!
它不会删除索引结构,只删除数据。
|
类比 SSM:deleteById 对应 deleteById,deleteAll 对应 deleteAll。
4.Spring Data 派生查询(Derived Query Methods)
除了继承来的方法,你还可以在 自己的接口 中直接声明遵循特定命名规则的方法,Spring Data ES 会自动为你生成实现。这是 Spring Data 最强大的特性之一。
概述
一个派生查询方法的名称,基本上遵循以下这个模式:
关键字 + 实体属性名 + 条件关键字 + (属性名)
- 关键字 (Keyword):方法的前缀,用来表示你要做什么操作。
- 实体属性名 (Property Name):你要查询的实体类中的字段名。注意:这里必须使用实体类的属性名,而不是 ES 索引中的字段名(如果两者不一致的话)。
- 条件关键字 (Condition Keyword):用来限定查询的条件(如等于、大于、包含等)。
- (属性名):如果条件需要一个值来比较(比如 “大于 100”),那么这个值就是方法的参数。
详细拆解与示例:
用 UserFileDocument 实体类来举例,假设它有以下属性:
- Long id
- String fileName
- Long fileSize
- Long userId
- LocalDateTime uploadTime
1. 关键字 (Keyword)
最常用的关键字是 findBy。
- findBy...:查询并返回符合条件的实体列表或单个实体。
- existsBy...:查询是否存在符合条件的实体,返回 boolean。
- countBy...:查询符合条件的实体总数,返回 long。
- deleteBy...:删除符合条件的实体,返回 void 或删除的数量。
2. 条件关键字 (Condition Keyword) 详解
这是规则的核心,不同的条件关键字对应不同的查询逻辑。
|
条件关键字 (部分)
|
含义 (类比 SQL)
|
方法名示例
|
ES 查询含义 (简化)
|
|
(无,直接跟属性)
|
等于 (
=
)
|
findByFileName(String name)
|
fileName: "传入的name"
|
|
Is
|
等于 (
=
) (与上面等价,更清晰)
|
findByFileNameIs(String name)
|
fileName: "传入的name"
|
|
Not
|
不等于 (
!=
)
|
findByFileNameNot(String name)
|
fileName: { "not": "传入的name" }
|
|
Like
|
模糊匹配 (
LIKE
)
|
findByFileNameLike(String pattern)
|
fileName: "pattern"
(需自己加
%
,如
%test%
)
|
|
Containing
|
包含 (等价于
LIKE '%...%'
)
|
findByFileNameContaining(String keyword)
|
fileName: "*keyword*"
|
|
StartingWith
|
以... 开头 (
LIKE '...%'
)
|
findByFileNameStartingWith(String prefix)
|
fileName: "prefix*"
|
|
EndingWith
|
以... 结尾 (
LIKE '%...'
)
|
findByFileNameEndingWith(String suffix)
|
fileName: "*suffix"
|
|
GreaterThan
|
大于 (
>
)
|
findByFileSizeGreaterThan(long size)
|
fileSize: { "gt": size }
|
|
GreaterThanEqual
|
大于等于 (
>=
)
|
findByFileSizeGreaterThanEqual(long size)
|
fileSize: { "gte": size }
|
|
LessThan
|
小于 (
<
)
|
findByFileSizeLessThan(long size)
|
fileSize: { "lt": size }
|
|
LessThanEqual
|
小于等于 (
<=
)
|
findByFileSizeLessThanEqual(long size)
|
fileSize: { "lte": size }
|
|
Between
|
在... 之间 (
BETWEEN ... AND ...
)
|
findByFileSizeBetween(long min, long max)
|
fileSize: { "gte": min, "lte": max }
|
|
In
|
在集合中 (
IN (...)
)
|
findByUserIdIn(Collection userIds)
|
userId: { "in": [1, 2, 3] }
|
|
NotIn
|
不在集合中 (
NOT IN (...)
)
|
findByUserIdNotIn(Collection userIds)
|
userId: { "not": { "in": [1, 2, 3] } }
|
|
OrderBy...Asc
|
按... 升序排列
|
findByUserIdOrderByUploadTimeAsc(Long userId)
|
sort: { "uploadTime": "asc" }
|
|
OrderBy...Desc
|
按... 降序排列
|
findByUserIdOrderByUploadTimeDesc(Long userId)
|
sort: { "uploadTime": "desc" }
|
3. 组合查询 (多条件)
你可以使用 And 或 Or 来组合多个查询条件。
- And:表示 “并且”,两个条件必须同时满足。
- Or:表示 “或者”,两个条件满足一个即可。
示例:
- findByUserIdAndFileNameContaining(Long userId, String keyword)
- 规则:findBy + UserId + And + FileName + Containing
- 含义:查询 userId 等于给定值 并且 fileName 包含给定关键字的文档。
- findByFileSizeGreaterThanOrFileNameLike(long size, String pattern)
- 规则:findBy + FileSize + GreaterThan + Or + FileName + Like
- 含义:查询 fileSize 大于给定值 或者 fileName 匹配给定模式的文档。
4. 分页、排序和限制结果
这是让查询更强大的附加功能。
- 分页:在方法的最后一个参数位置传入 Pageable 类型的对象。返回值通常使用 Page,它包含了数据列表、总页数、总条数等信息。
- 示例:Page findByUserId(Long userId, Pageable pageable);
- 调用:repository.findByUserId(1L, PageRequest.of(0, 10)); // 查询第 1 页,每页 10 条
- 排序:有两种方式,一是在方法名中使用 OrderBy...Asc/Desc;二是在 Pageable 中指定排序规则。
- 示例 (方法名):List findByUserIdOrderByUploadTimeDesc(Long userId);
- 示例 (Pageable):repository.findByUserId(1L, PageRequest.of(0, 10, Sort.by("uploadTime").descending()));
- 限制结果数量:可以在方法名中加入 First 或 Top 来限制返回结果的条数。
- 示例:List findTop5ByUserIdOrderByUploadTimeDesc(Long userId);
- 含义:查询 userId 为给定值的、按 uploadTime 降序排列的 前 5 条 记录。
应用示例
核心要点:
- 属性名必须正确:方法名中的属性名必须和你的 UserFileDocument 类中的成员变量名完全一致(大小写敏感)。例如,fileName 不能写成 Filename。
- 多单词属性:如果属性名是多个单词组成的(如 userName),在方法名中直接连写即可,Spring Data 会自动识别(这称为驼峰命名法)。
练习 1:我想查询 userId 为 100,并且 fileSize 大于 1024 的所有文件。
- 关键字:findBy
- 条件 1:UserId (等于)
- 组合:And
- 条件 2:FileSize + GreaterThan
- 方法名:findByUserIdAndFileSizeGreaterThan(Long userId, long size);
练习 2:我想查询 fileName 以 "report_" 开头的,并且按 uploadTime 最新排序的前 10 条文件。
- 关键字:findBy
- 条件:FileName + StartingWith
- 排序:OrderByUploadTimeDesc
- 限制:Top10
- 方法名:findTop10ByFileNameStartingWithOrderByUploadTimeDesc(String prefix);
练习 3:我想统计 userId 在 [1, 2, 3] 列表中的文件总数。
- 关键字:countBy
- 条件:UserId + In
- 方法名:countByUserIdIn(Collection userIds);
只要你记住 findBy... + 属性名 + 条件 + And/Or + 属性名 + 条件 ... 这个模式,并结合上面的条件关键字表,你就可以构建出几乎所有你需要的查询方法,无需写一行 SQL 或 ES 查询 DSL。
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)