5 Repository 层接口

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 文件,代码如下(这部分完全是你手写的):

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 save(S entity) | S | entity : 要保存的实体对象 | 新增或更新 。如果实体的 ID 在 ES 中不存在,则执行新增;如果已存在,则执行全量更新(会覆盖原文档)。返回保存后的实体对象。 |
| Iterable saveAll(Iterable entities) | 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 最强大的特性之一。

概述

一个派生查询方法的名称,基本上遵循以下这个模式:

java 复制代码
关键字 + 实体属性名 + 条件关键字 + (属性名)
  • 关键字 (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 或删除的数量。
  1. 条件关键字 (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" } |

  1. 组合查询 (多条件)
    你可以使用 And 或 Or 来组合多个查询条件。
  • And:表示 "并且",两个条件必须同时满足。
  • Or:表示 "或者",两个条件满足一个即可。
    示例:
  1. findByUserIdAndFileNameContaining(Long userId, String keyword)
  • 规则:findBy + UserId + And + FileName + Containing
  • 含义:查询 userId 等于给定值 并且 fileName 包含给定关键字的文档。
  1. findByFileSizeGreaterThanOrFileNameLike(long size, String pattern)
  • 规则:findBy + FileSize + GreaterThan + Or + FileName + Like
  • 含义:查询 fileSize 大于给定值 或者 fileName 匹配给定模式的文档。
  1. 分页、排序和限制结果
    这是让查询更强大的附加功能。
  • 分页:在方法的最后一个参数位置传入 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。
相关推荐
电棍2334 小时前
在docker a100云服务器运行vulkan->sapien->robotwin的经验(报错segmentation fault)
运维·docker·容器
Elastic 中国社区官方博客5 小时前
Elasticsearch:如何为 Elastic Stack 部署 E5 模型 - 下载及隔离环境
大数据·数据库·人工智能·elasticsearch·搜索引擎·ai·全文检索
alexhilton5 小时前
在Jetpack Compose中创建CRT屏幕效果
android·kotlin·android jetpack
云动雨颤6 小时前
访问宝塔面板安全入口404?SSH命令轻松解决
linux·运维·安全
NPE~6 小时前
[Linux命令分享]日志查看 — — less
linux·运维·less·常用命令·日志查看
high20116 小时前
【Git】-- Rebase 减少 Commit 次数指南
大数据·git·elasticsearch
2501_940094027 小时前
emu系列模拟器最新汉化版 安卓版 怀旧游戏模拟器全集附可运行游戏ROM
android·游戏·安卓·模拟器
大锦终7 小时前
【Linux】网络层与数据链路层中重点介绍
linux·运维·服务器·网络
下位子7 小时前
『OpenGL学习滤镜相机』- Day9: CameraX 基础集成
android·opengl