【微服务】SpringBoot 整合 Easy-Es 实战操作详解
目录
一、前言
二、SpringBoot 对接Elasticsearch常用技术方案
2.1 Spring Data Elasticsearch
2.3 Easy-Es
三、Easy-Es 介绍
3.1 Easy-Es 是什么
3.2 Easy-Es 特点
3.3 Easy-Es 优势
四、SpringBoot 操作Easy-Es详细过程
4.1 前置准备
4.1.1 es 服务搭建
4.1.2 导入核心依赖
4.1.3 添加配置文件
4.2 基础操作
4.2.1 增加实体类
4.2.2 添加自定义接口
4.2.3 增加几个测试接口
4.2.4 接口业务实现
4.2.5 接口扫描
4.2.7 接口效果测试
五、写在文末
一、前言
在使用SpringBoot 进行微服务的开发中,Elasticsearch是一种高频使用的文档检索性数据库,在很多需要实时检索,或者对文档检索性能要求比较高的场景。在这样的业务场景中,经常会涉及到对Elasticsearch的文档数据的增删改查操作,对很多程序员来说,写习惯了通过mybatis操作mysql的语法,对于操作es的代码,写起来还是比较头疼的,主要是API的编写比较复杂,语法多样灵活,本篇将介绍另一种适合大多数程序员快速上手用于操作es的技术框架Easy-Es。
二、SpringBoot 对接Elasticsearch常用技术方案
在微服务开发中,如果涉及到与ES的整合,面临着技术选型,选择一种合适的对接技术方案,会给团队的开发成本,后期维护等带来诸多便利,Spring Boot 与 Elasticsearch (ES) 的集成有多种方式,下面介绍几种常用的技术解决方案。
2.1 Spring Data Elasticsearch
这是一种最常用的官方集成方案,提供高级抽象和便捷的 Repository 支持。类似于其他的一些操作数据库的技术组件,比如JPA操作mysql,操作mongodb的jps等类似。这种方式对接起来比较简单,常用的增删改查接口组件中已经封装好了,开发者直接调用即可完成对es的基础操作。
其核心特点如下:
-
自动配置 Elasticsearch 客户端
-
支持基于方法名的查询自动生成
-
提供 ElasticsearchRepository 接口
-
支持注解方式定义映射
核心配置文件:
# application.yml
spring:data:elasticsearch:cluster-name: my-applicationcluster-nodes: localhost:9300repositories:enabled: true
示例操作代码:
-
从代码不难看出,开发者只需要继承一个Repository接口,就可以在业务代码中注入进行调用
// 实体类
@Document(indexName = "products", type = "product")
public class Product {@Idprivate String id;private String name;private double price;// getters/setters
}// Repository接口
public interface ProductRepository extends ElasticsearchRepository<Product, String> {List<Product> findByName(String name);List<Product> findByPriceBetween(double from, double to);
}// 使用示例
@Service
public class ProductService {@Autowiredprivate ProductRepository productRepository;public void saveProduct(Product product) {productRepository.save(product);}public List<Product> searchByName(String name) {return productRepository.findByName(name);}
}
这是官方推荐的 Java 客户端,基于 REST API,适用于需要更细粒度控制的场景。也是早些年在实际项目开发中使用非常高频的一种整合并操作es的方式。官方使用文档,以7.X为例:Compatibility | Java REST Client [7.0] | Elastic
Rest High Level Client的具备如下优点:
-
官方维护,兼容性好
-
由 Elastic 官方开发和维护,与 Elasticsearch 版本同步更新,兼容性有保障。
-
适合生产环境长期使用,API 稳定,减少因版本升级导致的兼容性问题。
-
-
功能全面,支持最新 ES 特性
-
支持 Elasticsearch 的所有 REST API,包括索引、搜索、聚合、脚本、集群管理等。
-
可以访问 Elasticsearch 的最新功能(如 Painless 脚本、向量搜索等)。
-
-
细粒度控制,灵活性高
-
相比 Spring Data Elasticsearch 的抽象封装,Rest High Level Client 提供更底层的 API,可以精确控制查询、索引、批量操作等。
-
适合复杂查询场景(如嵌套聚合、自定义评分、高亮等)。
-
-
性能优化支持
-
支持 Bulk API(批量操作)、Scroll API(大数据集查询)、Async API(异步请求)等高性能操作。
-
可以手动调整 refresh、routing、timeout 等参数优化性能。
-
-
支持异步请求(Async API)
-
提供 RestHighLevelClient 的异步版本(executeAsync),适用于高并发场景,减少线程阻塞。
-
-
适用于微服务架构
-
基于 HTTP/REST,不依赖 Elasticsearch 的 Transport 协议(旧版 Java API 使用 TCP 协议,已被废弃)。
-
适合微服务架构,跨语言调用更友好。
-
操作示例代码:
@Service
public class ProductSearchService {@Autowiredprivate RestHighLevelClient client;public void indexProduct(Product product) throws IOException {IndexRequest request = new IndexRequest("products").id(product.getId()).source("name", product.getName(),"price", product.getPrice());client.index(request, RequestOptions.DEFAULT);}public List<Product> searchProducts(String query) throws IOException {SearchRequest searchRequest = new SearchRequest("products");SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();sourceBuilder.query(QueryBuilders.matchQuery("name", query));searchRequest.source(sourceBuilder);SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);// 处理响应结果}
}
2.3 Easy-Es
Easy-Es(简称 EE)是一款基于 Elasticsearch 官方 RestHighLevelClient 打造的 ORM 开发框架,旨在简化 Elasticsearch 的开发流程,提高开发效率。它的设计理念是"只做增强不做改变",在保留原生 Elasticsearch 功能的同时,提供了更加便捷的开发体验。 该框架具备如下特点:
-
与 MyBatis-Plus 高度相似的语法设计
-
Easy-Es 采用了与 MyBatis-Plus(MP)几乎相同的语法设计(99%相似度),这使得熟悉 MP 的开发者可以几乎零学习成本地使用 EE。EE 可以被视为 MP 在 Elasticsearch 领域的替代版本,在某些方面甚至比 MP 更简单。
-
-
全自动索引托管
-
Easy-Es 首创了全自动索引托管模式,开发者无需关心索引的创建、更新及数据迁移等繁琐步骤。框架可以自动完成索引生命周期的管理,整个过程零停机,用户无感知。
-
-
屏蔽语言差异
-
开发者只需要掌握 MySQL 语法即可使用 Elasticsearch,真正做到"一通百通"。无需学习 Elasticsearch 特有的复杂语法,这对于不常使用 ES 的开发者尤其友好16。
-
-
显著减少代码量
-
与直接使用 RestHighLevelClient 相比,Easy-Es 可以节省 3-8 倍的代码量。例如,复杂的查询条件可以通过简单的链式调用实现,大大提高了开发效率。
-
-
智能字段类型推断
-
框架能够根据索引类型和当前查询类型上下文智能判断查询是否需要拼接 .keyword 后缀,减少开发者误用的可能性。
-
三、Easy-Es 介绍
3.1 Easy-Es 是什么
Easy-Es(简称EE)是一款基于ElasticSearch(简称Es)官方提供的ElasticsearchClient打造的ORM开发框架,在 ElasticsearchClient 的基础上,只做增强不做改变,为简化开发、提高效率而生,您如果有用过Mybatis-Plus(简称MP),那么您基本可以零学习成本直接上手EE,EE是MP的Es平替版,在有些方面甚至比MP更简单,同时也融入了更多Es独有的功能,助力您快速实现各种场景的开发。中文文档:Easy-Es
3.2 Easy-Es 特点
相比其他的操作ES的技术组件或框架,Easy-Es具有如下特点:
-
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
-
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
-
强大的 CRUD 操作:内置通用 Mapper,仅仅通过少量配置即可实现大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
-
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错段
-
支持主键自动生成:支持2 种主键策略,可自由配置,完美解决主键问题
-
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
-
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
-
内置分页插件:基于ElasticsearchClient 物理分页,开发者无需关心具体操作,且无需额外配置插件,写分页等同于普通 List 查询,且保持和PageHelper插件同样的分页返回字段,无需担心命名影响
-
MySQL功能全覆盖:MySQL中支持的功能通过EE都可以轻松实现
-
支持ES高阶语法:支持高亮搜索,分词查询,权重查询,Geo地理位置查询,IP查询,聚合查询等高阶语法
-
良好的拓展性:底层仍使用ElasticsearchClient,可保持其拓展性,开发者在使用EE的同时,仍可使用ElasticsearchClient的功能。
3.3 Easy-Es 优势
使用Easy-Es具有如下优势:
-
全自动索引托管: 全球开源首创的索引托管模式,开发者无需关心索引的创建更新及数据迁移等繁琐步骤,索引全生命周期皆可托管给框架,由框架自动完成,过程零停机,用户无感知,彻底解放开发者
-
智能字段类型推断: 根据索引类型和当前查询类型上下文综合智能判断当前查询是否需要拼接.keyword后缀,减少小白误用的可能
-
屏蔽语言差异: 开发者只需要会MySQL语法即可使用Es,真正做到一通百通,无需学习枯燥易忘的Es语法,Es使用相对MySQL较低频,学了长期不用也会忘,没必要浪费这时间.开发就应该专注于业务,省下的时间去撸铁,去陪女朋友陪家人,不做资本家的韭菜
-
代码量极少: 与直接使用ElasticsearchClient相比,相同的查询平均可以节省3-80倍左右的代码量
-
零魔法值: 字段名称直接从实体中获取,无需输入字段名称字符串这种魔法值,提高代码可读性,杜绝因字段名称修改而代码漏改带来的Bug
-
拓展性强: 框架内置混合查询,原生查询,无论是索引层面还是CRUD层面,都拥有>=原生API的拓展性,无需担心引入后影响开发,只要是ES本身能支持的功能使用Easy-ES都可以搞定
-
零额外学习成本: 开发者只要会国内最受欢迎的Mybatis-Plus语法,即可无缝迁移至EE,EE采用和前者相同的语法,消除使用者额外学习成本,直接上手,爽
-
降低开发者门槛: Es通常需要中高级开发者才能驾驭,但通过接入EE,即便是只了解ES基础的初学者也可以轻松驾驭ES完成绝大多数需求的开发,可以提高人员利用率,降低企业成本
四、SpringBoot 操作Easy-Es详细过程
接下来通过代码操作,演示下如何在SpringBoot 微服务中通过整合Easy-Es操作es的索引。
4.1 前置准备
4.1.1 es 服务搭建
在代码层面正式开始之前,需要先做下面的2个准备:
-
搭建es服务,可以考虑使用docker搭建,相对简单一些;
-
搭建kibana,为了操作Es 索引方便,方便可视化查看数据;
4.1.2 导入核心依赖
在工程pom中导入下面的核心依赖,其中最核心的依赖文件 easy-es-boot-starter 可以根据自己的项目版本酌情选择,注意版本匹配性。
<properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><satoken.version>1.37.0</satoken.version>
</properties><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.2</version><relativePath/>
</parent><dependencies><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.8.1</version></dependency><dependency><groupId>org.dromara.easy-es</groupId><artifactId>easy-es-boot-starter</artifactId><version>2.0.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>4.4.0</version></dependency></dependencies><build><finalName>${project.artifactId}</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins>
</build>
4.1.3 添加配置文件
在工程配置文件中配置下面的信息,下面并给出关于easy-es的详细参数配置说明,开发者可以结合自身的实际使用情况配置。
server:port: 8087easy-es:enable: trueaddress: IP:9200# 可选配置global-config:print-dsl: true # 打印DSL语句async-process-index-blocking: true # 自动托管索引logging:level:tracer : trace
基础参数配置:
easy-es:compatible: true # 兼容模式开关,默认为false,若您的es客户端版本小于8.x,务必设置为true才可正常使用,8.x及以上则可忽略此项配置enable: true #默认为true,若为false则认为不启用本框架address : 127.0.0.1:9200 # es的连接地址,必须含端口 若为集群,则可以用逗号隔开 例如:127.0.0.1:9200,127.0.0.2:9200username: elastic #若无 则可省略此行配置password: WG7WVmuNMtM4GwNYkyWH #若无 则可省略此行配置
拓展配置:
-
可缺省,不影响项目启动,为了提高生产环境性能,建议您按需配置
easy-es:keep-alive-millis: 30000 # 心跳策略时间 单位:msconnect-timeout: 5000 # 连接超时时间 单位:mssocket-timeout: 600000 # 通信超时时间 单位:ms request-timeout: 5000 # 请求超时时间 单位:msconnection-request-timeout: 5000 # 连接请求超时时间 单位:msmax-conn-total: 100 # 最大连接数 单位:个max-conn-per-route: 100 # 最大连接路由数 单位:个
全局配置:
-
可缺省,不影响项目启动,若缺省则为默认值
easy-es:enable: true # 是否开启EE自动配置 默认开启,可缺省schema: http # 默认值为http 可缺省 也支持https免ssl方式 配置此值为 https 即可banner: true # 默认为true 打印banner 若您不期望打印banner,可配置为falseglobal-config:i-kun-mode: false # 是否开启小黑子模式,默认关闭, 开启后日志将更有趣,提升编码乐趣,仅供娱乐,切勿用于其它任何用途process-index-mode: manual #索引处理模式,smoothly:平滑模式, not_smoothly:非平滑模式, manual:手动模式,,默认开启此模式print-dsl: true # 开启控制台打印通过本框架生成的DSL语句,默认为开启,测试稳定后的生产环境建议关闭,以提升少量性能distributed: false # 当前项目是否分布式项目,默认为true,在非手动托管索引模式下,若为分布式项目则会获取分布式锁,非分布式项目只需synchronized锁.reindexTimeOutHours: 72 # 重建索引超时时间 单位小时,默认72H 可根据ES中存储的数据量调整async-process-index-blocking: true # 异步处理索引是否阻塞主线程 默认阻塞 数据量过大时调整为非阻塞异步进行 项目启动更快active-release-index-max-retry: 4320 # 分布式环境下,平滑模式,当前客户端激活最新索引最大重试次数,若数据量过大,重建索引数据迁移时间超过4320/60=72H,可调大此参数值,此参数值决定最大重试次数,超出此次数后仍未成功,则终止重试并记录异常日志active-release-index-fixed-delay: 60 # 分布式环境下,平滑模式,当前客户端激活最新索引最大重试次数 分布式环境下,平滑模式,当前客户端激活最新索引重试时间间隔 若您期望最终一致性的时效性更高,可调小此值,但会牺牲一些性能db-config:map-underscore-to-camel-case: false # 是否开启下划线转驼峰 默认为falseindex-prefix: daily_ # 索引前缀,可用于区分环境 默认为空 用法和MP的tablePrefix一样的作用和用法id-type: customize # id生成策略 customize为自定义,id值由用户生成,比如取MySQL中的数据id,如缺省此项配置,则id默认策略为es自动生成field-strategy: not_empty # 字段更新策略 默认为not_nullenable-track-total-hits: true # 默认开启,开启后查询所有匹配数据,若不开启,会导致无法获取数据总条数,其它功能不受影响,若查询数量突破1W条时,需要同步调整@IndexName注解中的maxResultWindow也大于1w,并重建索引后方可在后续查询中生效(不推荐,建议分页查询).refresh-policy: immediate # 数据刷新策略,默认为不刷新,若对数据时效性要求比较高,可以调整为immediate,但性能损耗高,也可以调整为折中的wait_untilbatch-update-threshold: 10000 # 批量更新接口的阈值 默认值为1万,突破此值需要同步调整enable-track-total-hits=true,@IndexName.maxResultWindow > 1w,并重建索引.smartAddKeywordSuffix: true # 是否智能为字段添加.keyword后缀 默认开启,开启后会根据当前字段的索引类型及当前查询类型自动推断本次查询是否需要拼接.keyword后缀
多数据源配置:
-
类似于操作mysql时,可以在同一个代码中,操作多个不同数据源的数据
easy-es:dynamic:datasource:ds1:address: 10.20.64.228:9200 #数据源1的连接地址#username: '若无可去掉此行'#password: '若无可去掉此行'ds2:address: 49.234.28.111:9200 #数据源2的连接地址#username: '若无可去掉此行'#password: '若无可去掉此行'#dsn...
其他配置:
logging:level:tracer: trace # 开启trace级别日志,在开发时可以开启此配置,则控制台可以打印es全部请求信息及DSL语句,为了避免重复,开启此项配置后,可以将EE的print-dsl设置为false.
4.2 基础操作
案例需求,我们将创建一个名为book_info的索引,然后通过接口对该索引进行数据的增删改查的操作。
4.2.1 增加实体类
自定义一个实体类,作为索引中的文档对象,如下:
package com.congge.domain;import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.dromara.easyes.annotation.IndexField;
import org.dromara.easyes.annotation.IndexId;
import org.dromara.easyes.annotation.IndexName;
import org.dromara.easyes.annotation.rely.Analyzer;
import org.dromara.easyes.annotation.rely.FieldType;
import org.dromara.easyes.annotation.rely.IdType;import java.io.Serial;
import java.io.Serializable;
import java.util.Date;//@IndexName(refreshPolicy = RefreshPolicy.IMMEDIATE)
@Data
@IndexName("book_info")
public class Book implements Serializable {@Serialprivate static final long serialVersionUID = 214318775328037145L;@IndexId(type = IdType.CUSTOMIZE)private Long bookId;@IndexField(fieldType = FieldType.TEXT,analyzer = Analyzer.IK_MAX_WORD,searchAnalyzer = Analyzer.IK_MAX_WORD)private String title;@IndexField(fieldType = FieldType.KEYWORD)private String author;@IndexField(fieldType = FieldType.TEXT,analyzer = Analyzer.IK_MAX_WORD,searchAnalyzer = Analyzer.IK_MAX_WORD)private String description;@IndexField(fieldType = FieldType.INTEGER)private Integer status;@JSONField(format = "yyyy-MM-dd HH:mm:ss")@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")@IndexField(fieldType = FieldType.DATE, dateFormat = "yyyy-MM-dd HH:mm:ss")private Date operTime;}
关于实体类对象中的各个属性上面的注解使用,详细的解释可以参考官方的文档说明
4.2.2 添加自定义接口
自定义一个接口,继承 BaseEsMapper,跟我们操作mybatis-plus看起来一样
public interface BookMapper extends BaseEsMapper<Book> {}
4.2.3 增加几个测试接口
为了方便测试看效果,添加几个测试接口
package com.congge.web;import com.congge.domain.Book;
import com.congge.service.IBookService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.*;import java.util.List;@RestController
@RequestMapping("/api/v1/book")
public class BookController {@Resourceprivate IBookService bookService;@GetMapping("/index")public Object index(@RequestParam String type){bookService.index(type);return "索引操作成功";}@PostMapping("/save")public Object saveBook(@RequestBody Book book){bookService.saveBook(book);return "保存成功";}@GetMapping("/detail")public Object detail(@RequestParam Long id){Book book = bookService.getById(id);return book;}@PostMapping("/list")public Object queryBookList(@RequestBody Book book){List<Book> bookList = bookService.queryBookList(book);return bookList;}@DeleteMapping("/delete")public Object deleteById(@RequestParam Long id){bookService.deleteById(id);return "删除成功";}}
4.2.4 接口业务实现
下面是具体的业务实现层逻辑
package com.congge.service.impl;import com.congge.domain.Book;
import com.congge.mapper.es.BookMapper;
import com.congge.service.IBookService;
import jakarta.annotation.Resource;
import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
import org.springframework.stereotype.Service;import java.util.Date;
import java.util.List;
import java.util.Objects;@Service
public class BookServiceImpl implements IBookService {@Resourceprivate BookMapper bookMapper;@Overridepublic void index(String type) {if("create".equals(type)){bookMapper.deleteIndex("book_info");bookMapper.createIndex("book_info");}else if("delete".equals(type)){bookMapper.deleteIndex("book_info");}}@Overridepublic void saveBook(Book book) {//添加数据book.setOperTime(new Date());bookMapper.insert(book);System.out.println(book.getBookId());}@Overridepublic Book getById(Long id) {Book book = bookMapper.selectById(id);return book;}@Overridepublic List<Book> queryBookList(Book book) {LambdaEsQueryWrapper<Book> queryWrapper = new LambdaEsQueryWrapper<>();if(Objects.nonNull(book.getBookId())){queryWrapper.eq("bookId", book.getBookId());}if(Objects.nonNull(book.getTitle())){queryWrapper.like("title", book.getTitle());}if(Objects.nonNull(book.getAuthor())){queryWrapper.eq("author", book.getAuthor());}if(Objects.nonNull(book.getDescription())){queryWrapper.like("description", book.getDescription());}List<Book> books = bookMapper.selectList(queryWrapper);return books;}@Overridepublic void deleteById(Long id) {bookMapper.deleteById(id);}
}
4.2.5 接口扫描
在工程的启动类上面添加对mapper接口所在包目录的扫描配置
4.2.7 接口效果测试
接下来启动工程,分别对上面的接口进行功能验证。项目中集成了swagger,可以方便对接口进行操作。
1)创建索引
通过kibana控制台查看索引是否创建成功
2)添加文档
随机添加几条测试数据
由于开启了输出日志,在后台的控制台中可以看到相关的输出日志,类似于mybatis操作数据库的sql日志,方便开发者看问题
添加完成后,在kibana中可以看到2条新增的记录
3)查询文档数据
分别验证精准匹配和模糊匹配
从后台的输出日志不难看出,走的是模糊匹配,框架自动帮开发者做了封装实现
3)删除文档数据
调用删除接口,根据ID进行删除数据
再次查询,这条记录就查不到了
五、写在文末
本文通过案例操作演示,详细介绍了Easy-Es的详细使用,还有更多好用的功能,有兴趣的同学可以基于此继续深入研究,本篇到此结束,感谢观看。