SpringDataES-6.8
SpringData-ElasticSearch简介
SpringDataES
SpringDataElasticsearch(以后简称SDE)是SpringData项目下的一个子模块
,是Spring提供的操作ElasticSearch的数据层,封装了大量的基础操作,通过它可以很方便的操作ElasticSearch的数据
SpringData的使命是给各种数据访问提供统一的编程接口,不管是关系型数据库(如MySQL),还是非关系数据库(如Redis),或者类似Elasticsearch这样的索引数据库。从而简化开发人员的代码,提高开发效率
SpringDataES特征
- 支持Spring的基于
@Configuration
的Java配置方式,或者XML配置方式 - 提供了用于操作ES的便捷工具类
ElasticsearchTemplate
。包括实现文档到POJO之间的自动智能映射 - 利用Spring的数据转换服务实现丰富的对象映射
- 基于注解的元数据映射方式,而且可扩展以支持更多不同的数据格式
- 根据持久层接口自动生成对应实现方法,无需人工编写基本操作代码(类似mybatis,根据接口自动得到实现)。当然,也支持人工定制查询
创建SpringData-Es工程
通过https://start.spring.io/创建在IDEA中不是网页版的,我就不贴图了,待会在下方给出依赖即可
首先在application.yml
文件中引入elasticsearch的host
和port
spring:
data:
elasticsearch:
# es集群名称
cluster-name: elasticsearch
# 准备连接的es节点tcp地址
cluster-nodes: 127.0.0.1:9300
# 集群配置:192.168.129.139:9301,192.168.129.139:9302,192.168.129.139:9303
需要注意的是,SpringDataElasticsearch底层使用的不是Elasticsearch提供的RestHighLevelClient
,而是TransportClient,并不采用Http协议通信,而是访问elasticsearch对外开放的tcp端口,ElasticSearch默认tcp端口,另外,SpringBoot已经帮我们配置好了各种SDE配置,并且注册了一个ElasticsearchTemplate供我们使用。接下来一起来试试吧
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.16.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
创建索引库和映射
新建实体类Goods,作为与索引库对应的文档,通过实体类上的注解来配置索引库信息的,比如:索引库名、类型名、分片、副本数量、还有映射信息
Goods.java
@Data
@AllArgsConstructor
@NoArgsConstructor
// 与索引库对应的文档实体类型 indexNmae相当于DataBases type相当于Table
// 集群时可以设置:shards:分片数量(默认值:5),replicas:副本数量(默认值:1)
@Document(indexName = "goods", type = "goods")
public class Goods {
/**
* 必须有id,这里的id是全局唯一的标识,等同于es中的“_id”
*/
@Id
private Long id;
/**
* 标题
* type: 字段数据类型
* analyzer: 分词器类型
* index: 是否索引(默认值:true)
* store: 是否存储(默认值:false)
*/
@Field(type = FieldType.Text,analyzer = "ik_max_word")
private String title;
/**
* 分类
*/
@Field(type = FieldType.Keyword)
private String category;
/**
* 品牌
*/
@Field(type = FieldType.Keyword)
private String brand;
/**
* 价格
*/
@Field(type = FieldType.Double)
private Double price;
/**
* 图片地址
*/
@Field(type = FieldType.Keyword,index = false)
private String images;
}
几个用到的注解
@Document
:声明索引库配置- indexName:索引库名称
- type:类型名称,默认是
docs
- shards:分片数量,默认5
- replicas:副本数量,默认1
@Id
:声明实体类的id@Field
:声明字段属性- type:字段的数据类型
- analyzer:指定分词器类型
- index:是否创建索引,默认为
true
- store:是否存储,默认为
false
创建索引库
相当于创建create DataBases, ElasticsearchTemplate:可以用来操作复杂的ES
@SpringBootTest
class SpringbootEsApiApplicationTests {
@Autowired
private ElasticsearchTemplate template;
@Test
void testCreateIndex() throws IOException {
boolean flag = template.createIndex(Goods.class);
System.out.println("创建索引库是否成功" + flag);
boolean mapping = template.putMapping(Goods.class);
System.out.println("创建映射是否成功" + mapping);
}
}
kIbana查看创建结果
GET /goods/_mapping/goods
删除索引
相当于删除drop DataBases
@SpringBootTest
class SpringbootEsApiApplicationTests {
@Autowired
private ElasticsearchTemplate template;
@Test
public void deleteIndex(){
// 根据对象删除
boolean flag = template.deleteIndex(Goods.class);
// 根据索引名称删除
// boolean flagIndex = template.deleteIndex("goods");
System.out.println("删除是否成功" + flag);
}
}
ElasticsearchRepositoryCRUD
SDE的文档索引数据CRUD并没有封装在ElasticsearchTemplate中,而是有一个叫做ElasticsearchRepository
的接口
我们需要自定义接口,继承ElasticsearchRespository
GoodsRepository.java
public interface GoodsRepository extends ElasticsearchRepository<Goods,Long> {
}
创建文档数据
创建索引有单个创建和批量创建之分,先来看单个创建(添加),如果文档已经存在则执行更新操作
- 创建新增文档对象
- 调用GoodsRepository接口保存方法
@SpringBootTest
class SpringbootEsApiApplicationTests {
@Autowired
private GoodsRepository goodsRepository;
/**
* 添加文档
* 不存在就新增,存在就更新
*/
@Test
public void addDoc(){
// 1.创建新增文档对象
Goods goods = new Goods(1l, "小米手机", "手机", "小米", 19999.00, "http://www.baidu.com");
// 2.调用goodsRepository保存
goodsRepository.save(goods);
}
}
再来看批量创建(添加)
- 创建新增文档对象添加至list中
- 调用GoodsRepository接口批量保存方法
@SpringBootTest
class SpringbootEsApiApplicationTests {
@Autowired
private GoodsRepository goodsRepository;
/**
* 批量新增
*/
@Test
public void batchAddDoc(){
// 1.创建新增文档对象添加至list中
List<Goods> goodsList = new ArrayList<>();
for (long i = 2; i < 10; i++) {
Goods goods = new Goods(i, "小米手机" + i, "手机", "小米", 19999.00 + i, "http://www.baidu.com");
goodsList.add(goods);
}
// 2.调用GoodsRepository接口批量保存方法
goodsRepository.saveAll(goodsList);
}
}
通过Kibana查看
GET /goods/_search
查询文档数据
默认提供了根据id查询
,查询所有
两个功能
🐤根据id查询
- 调用goodsRepository根据id查询
- 判断返回的Optional对象中是否有值
- 从Optional对象中获取查询结果
@SpringBootTest
class SpringbootEsApiApplicationTests {
@Autowired
private GoodsRepository goodsRepository;
/**
* 根据id查询
*/
@Test
public void findDocById(){
// 1.调用goodsRepository根据id查询
Optional<Goods> optional = goodsRepository.findById(1L);
// 2.判断返回的Optional对象中是否有值
if(optional.isPresent()){
// 3.从Optional对象中获取查询结果
Goods goods = optional.get();
System.out.println("查询结果" + goods);
}
}
}
🐸查询所有
@SpringBootTest
class SpringbootEsApiApplicationTests {
@Autowired
private GoodsRepository goodsRepository;
/**
* 查询所有文档
*/
@Test
public void findAllDoc(){
// 1.调用goodsRepository查询所有
Iterable<Goods> all = goodsRepository.findAll();
// 2.遍历打印输出查询结果
for (Goods goods : all) {
System.out.println("查询结果" + goods);
}
}
}
search查询
- 构建
QueryBuilder
对象设置查询类型和查询条件 - 调用goodsRepository search方法进行查询
- 遍历打印输出查询结果
@SpringBootTest
class SpringbootEsApiApplicationTests {
@Autowired
private GoodsRepository goodsRepository;
/**
* search查询
*/
@Test
public void findAllDoc(){
// 1.构建QueryBuilder对象设置查询类型和查询条件
TermQueryBuilder queryBuilder = QueryBuilders.termQuery("title", "大米");
// 2.调用goodsRepository search方法进行查询
Iterable<Goods> iterable = goodsRepository.search(queryBuilder);
// 3.遍历打印输出查询结果
for (Goods goods : iterable) {
System.out.println("查询结果" + goods);
}
}
}
search查询并分页排序
- 构建
Sort
排序对象,指定排序字段和排序方式 - 使用PageRequest构建Pageable分页对象,指定分页参数,并将排序对象设置到分页对象中
- 调用goodsRepository search方法进行查询
- 解析结果
@SpringBootTest
class SpringbootEsApiApplicationTests {
@Autowired
private GoodsRepository goodsRepository;
/**
* search查询并实现分页、排序
*/
@Test
public void findAllDoc(){
// 1.构建排序对象,指定排序字段和排序方式
Sort sort = new Sort(Sort.Direction.ASC, "id");
// 2.构建分页对象,指定分页参数,并将排序对象设置到分页对象中
PageRequest pageRequest = PageRequest.of(0, 2, sort);
// 3.调用goodsRepository search方法进行查询
Page<Goods> page = goodsRepository.search(QueryBuilders.matchQuery("title", "小米"), pageRequest);
// 4.解析结果
// 4.1.获取总记录数
long totalElements = page.getTotalElements();
// 4.2.获取总页数
int totalPages = page.getTotalPages();
// 4.3.遍历查询结果
for (Goods goods : page) {
System.out.println("查询结果" + goods);
}
System.out.println("总记录数" + totalElements);
System.out.println("总页数" + totalPages);
}
}
自定义方法查询
GoodsRepository提供的查询方法有限,但是它却提供了非常强大的自定义查询功能;只要遵循SpringData提供的语法,我们可以任意定义方法达到自定义查询
public interface GoodsRepository extends ElasticsearchRepository<Goods,Long> {
/**
* 根据价格范围查询
* @param from 开始价格
* @param to 结束价格
* @return 查询的结果
*/
List<Goods> findByPriceBetween(Double from, Double to);
}
无需写实现,SDE会自动帮我们实现该方法,我们只需要用即可
@SpringBootTest
class SpringbootEsApiApplicationTests {
@Autowired
private GoodsRepository goodsRepository;
/**
* 自定义GoodsRepository查询方法: 根据价格范围查找
*/
@Test
public void findGoodsByPriceRang(){
// 1.调用goodsRepository自定义方法 findByPriceBetween
List<Goods> list = goodsRepository.findByPriceBetween(19999.0, 20006.0);
// 2.遍历输出结果
for (Goods goods : list) {
System.out.println(goods);
}
}
}
支持的一些语法示例
- findGoods By Price Between
- 语法:findBy + 字段名 + Keyword + 字段名 + …
Keyword | Sample | Elasticsearch Query String |
---|---|---|
And |
findByNameAndPrice |
{"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Or |
findByNameOrPrice |
{"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Is |
findByName |
{"bool" : {"must" : {"field" : {"name" : "?"}}}} |
Not |
findByNameNot |
{"bool" : {"must_not" : {"field" : {"name" : "?"}}}} |
Between |
findByPriceBetween |
{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
Before |
findByPriceBefore |
{"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
After |
findByPriceAfter |
{"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Like |
findByNameLike |
{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
StartingWith |
findByNameStartingWith |
{"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
EndingWith |
findByNameEndingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}} |
In |
findByNameIn(Collection<String>names) |
{"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}} |
NotIn |
findByNameNotIn(Collection<String>names) |
{"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}} |
OrderBy |
findByNameOrderByNameDesc |
{"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"name" : "?"}}} |
ElasticsearchTemplate查询
- SDE也支持使用ElasticsearchTemplate进行原生查询
- 而查询条件的构建是通过一个名为
NativeSearchQueryBuilder
的类来完成的,不过这个类的底层还是使用的原生API中的QueryBuilders
、HighlightBuilders
等工具
分页和排序
- 可以通过NativeSearchQueryBuilder类来构建分页和排序、聚合等操作
- queryBuilder.withQuery():设置查询类型和查询条件
- queryBuilder.withPageable():设置分页
- queryBuilder.withSort():设置排序
- 构建NativeSearchQueryBuilder查询对象
- 使用QueryBuilders指定查询类型和查询条件
- 使用SortBuilders指定排序字段和排序方式
- 使用PageRequest对象指定分页参数
- 调用NativeSearchQueryBuilder的build方法完成构建
- 使用ElasticsearchTemplate完成查询
- 解析结果
@SpringBootTest
class SpringbootEsApiApplicationTests {
@Autowired
private ElasticsearchTemplate template;
/**
* 使用ElasticsearchTemplate完成分页排序查询
*/
@Test
public void nativeSearchQuery(){
// 1.构建NativeSearchQueryBuilder查询对象
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
// 2.使用QueryBuilders指定查询类型和查询条件
nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米"));
// 3.使用SortBuilders指定排序字段和排序方式
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort("id").order(SortOrder.DESC));
// 4.使用PageRequest对象指定分页参数
nativeSearchQueryBuilder.withPageable(PageRequest.of(0,2));
// 5.调用NativeSearchQueryBuilder的build方法完成构建
NativeSearchQuery build = nativeSearchQueryBuilder.build();
// 6.使用ElasticsearchTemplate完成查询
AggregatedPage<Goods> page = template.queryForPage(build, Goods.class);
// 7.解析结果
// 7.1获取总记录数
long totalElements = page.getTotalElements();
// 7.2获取总页数
int totalPages = page.getTotalPages();
// 7.3遍历查询结果
for (Goods goods : page) {
System.out.println("查询结果" + goods);
}
System.out.println("总记录数" + totalElements);
System.out.println("总页数" + totalPages);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具