当前位置: 首页 > news >正文

JeecgBoot 全文检索实战:从数据库 LIKE 到 Elasticsearch 集成的完整迁移

JeecgBoot 全文检索实战:从数据库 LIKE 到 Elasticsearch 集成的完整迁移

【免费下载链接】jeecg-boot【低代码迈入v2.0时代,一句话即可生成整个系统】企业级AI低代码平台,一键生成前后端代码甚至整个系统。 AI Skills 一句话画流程、设计表单、生成报表、大屏。内置 AI应用平台涵盖:AI聊天、知识库、流程编排、MCP插件等,兼容主流大模型。引领AI低代码「Skills 生成 → 在线配置 → 代码生成 → 手工合并->AI修改」开发模式,解决 Java 项目 90% 重复工作,提高效率又不失灵活。项目地址: https://gitcode.com/GitHub_Trending/je/jeecg-boot

如果你的系统里有一张两百万行的用户表,前端搜索框每敲一个字就触发一次LIKE '%关键字%'查询,你会发现接口耗时从几十毫秒一路涨到几秒,数据库 CPU 也随之报警。这是很多管理后台的真实处境,也是我们决定把 JeecgBoot 的搜索能力迁移到 Elasticsearch 的直接原因。这篇文章就围绕 JeecgBoot Elasticsearch 集成的完整过程展开,记录我们从零配置、写代码到踩坑排错的全流程,读完你可以照着在自己的 JeecgBoot 项目里复现一遍。

为什么非换不可:先看 LIKE 查询的三个硬伤

在动手之前,我们先说清楚"为什么要迁移",否则你会觉得这是没事找事。

第一个硬伤是扫描成本。LIKE '%关键词%'无法利用 B+ 树索引,本质是全表扫描,数据量过百万后性能断崖式下跌。

第二个硬伤是命中率。用户搜"张伟",数据库只认字面完全一致的匹配;想支持"错别字容忍""同义词扩展""拼音前缀"这类体验,SQL 基本无能为力。

第三个硬伤是排序。相关性打分(比如标题命中比正文命中权重更高)在数据库里要靠手写复杂 CASE WHEN,维护成本极高。

Elasticsearch 解决的正是这三件事:倒排索引、分词与打分。它不替代数据库做主存储,而是作为"检索副本"存在——数据先落库,再同步到 ES。

前置条件:三条清单,一项都不能少

JeecgBoot 对 ES 的封装基于 REST API 实现,不依赖重量级客户端依赖,这一点对新手非常友好。动手前确认三件事:

  1. Elasticsearch 服务:7.x 及以上版本均可,8.x 也兼容(代码里针对 7.x 做了显式兼容处理)。
  2. JeecgBoot 工程:任意 3.x 版本即可,ES 相关类位于jeecg-boot-base-core模块,无需额外引入 starter。
  3. Java + Maven 环境:能正常编译运行 JeecgBoot 即可。

这里有个值得注意的设计:模板类的注册是有条件的。查看源码会发现JeecgElasticsearchTemplate上标了@ConditionalOnProperty(prefix = "jeecg.elasticsearch", name = "cluster-nodes"),意思是只有当你配置了 ES 地址,这个 Bean 才会被创建。没配 ES 的项目不会因为类加载而报错,这也是它能作为可选功能安静存在的原因。

核心配置类源码:jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/vo/Elasticsearch.java

三步完成 ES 连接配置

配置集中在application.yml,总共就两个键,加上注释也不到十行:

jeecg: elasticsearch: cluster-nodes: 127.0.0.1:9200 # ES 服务地址,多个节点用逗号分隔 check-enabled: true # 启动时是否校验连接,建议开发期打开

这段配置的作用很直白:cluster-nodes是模板类拼接请求 URL 的基础地址,check-enabled控制项目启动时是否主动探测一次 ES 版本号。

check-enabled: true时,启动日志里会出现两行关键信息:ElasticSearch 服务连接成功ElasticSearch version: 7.x.x。如果 ES 没起来,日志会给出明确的失败警告,并且后续所有操作都会被拒——这是官方故意设计的"熔断",避免你在服务不可用时做无意义的写入。

核心实操:索引与增删改查

ES 相关代码集中在模板类里,先注入它:

配置文件中的映射关系由JeecgBaseConfig统一管理,相关源码见:jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/config/JeecgBaseConfig.java

@Resource private JeecgElasticsearchTemplate esTemplate;

1. 创建与检查索引

// 返回 true 表示索引创建成功;若索引已存在会打印警告并返回 false boolean created = esTemplate.createIndex("sys_user_index"); // 判断索引是否存在,常用于启动时的幂等初始化 boolean exists = esTemplate.indexExists("sys_user_index");

这段代码在做什么:createIndex底层发的是PUT /{indexName},靠返回体的acknowledged字段判断成败;已存在的索引不会抛异常,只会打一条警告,方便你重复执行初始化逻辑。

2. 写入与更新数据

JSONObject user = new JSONObject(); user.put("id", "u_1001"); user.put("username", "张三"); user.put("deptName", "研发部"); // typeName 只是分类标识,不是数据库表名,随便起但要统一 esTemplate.saveOrUpdate("sys_user_index", "docs", "u_1001", user);

这段代码在做什么:saveOrUpdate发的是PUT /{indexName}/{typeName}/{dataId}?refresh=wait_forwait_for表示写入后等待刷新完成再返回,保证你立刻能查到刚写入的数据,代价是写入吞吐略降,适合低频同步场景。

3. 按 ID 查询与删除

// 查不到时返回 null,而不是空对象 JSONObject doc = esTemplate.getDataById("sys_user_index", "docs", "u_1001"); // 删除成功返回 true,文档不存在返回 false boolean deleted = esTemplate.delete("sys_user_index", "docs", "u_1001");

这段代码在做什么:这两个方法分别对应GETDELETE请求,且都对 404 做了兜底处理,不会因为文档不存在而抛出异常打断你的业务逻辑。

4. 批量写入

JSONArray list = new JSONArray(); // 注意:数组里每个元素必须带 id 字段,模板会用它作为文档 _id list.add(userJson1); list.add(userJson2); esTemplate.saveBatch("sys_user_index", "docs", list);

这段代码在做什么:saveBatch走的是 ES 的_bulk批量接口,把所有数据拼成一条请求发送,网络往返从 N 次降到 1 次,是全量初始化索引时最该用的方法。

查询实操:从关键词到组合条件

查询是 ES 集成的重头戏,模板类给了一套"搭积木"式的构造方法。

全文检索,一段代码搞定

// 在 username、deptName 两个字段里搜"研发" JSONObject query = esTemplate.buildQueryString("username OR deptName", "研发"); // 组装完整请求:只返回指定字段,从第 0 条开始取 10 条 JSONObject body = esTemplate.buildQuery(null, query, 0, 10); JSONObject result = esTemplate.search("sys_user_index", "docs", body);

这段代码在做什么:buildQueryString生成query_string查询 DSL,支持ANDORNOT*通配符;buildQuery负责把查询条件、分页参数和字段过滤拼成完整请求体。

组合查询:过滤 + 范围 + 关键字

JSONArray must = new JSONArray(); must.add(esTemplate.buildQueryString("status", "1")); // 状态必须为 1 must.add(esTemplate.buildRangeQuery("createTime", "2024-01-01", "2024-12-31", true, true)); JSONObject query = esTemplate.buildBoolQuery(must, null, null); // bool 查询,must 条件全中

这段代码在做什么:buildBoolQuery对应 DSL 里的bool查询,三个参数分别是 must(必须满足)、mustNot(必须排除)、should(满足加分);buildRangeQuery生成范围条件,gte/lte是否包含边界由最后两个布尔参数控制。

如果查询条件更复杂,模板还提供了QueryStringBuilder这个链式工具,支持.and(...).or(...).not(...)连续拼接,适合在代码里动态组装复杂的检索表达式。

避坑指南:四个高频问题

问题一:启动日志提示连接失败,但 ES 明明在跑。

先确认cluster-nodes里没写http://前缀——模板类会自己拼http://,写了反而拼出http://http://。再看 ES 是否绑定了127.0.0.1,跨机器访问记得放开监听地址。

问题二:写入报failed to parse field ... of type [text]

这是模板类里最典型的一个坑。看saveOrUpdate源码会发现,它写入前会主动剔除两类字段:空值字段,以及包含[{前缀的上传控件字段(比如富文本、图片路径)。原因很简单:这类值在 ES 里无法解析成合法的 text。你自己封装写入方法时,也要记得过滤这两种字段,否则报错会非常隐蔽。

问题三:一次查询只能拿到 10000 条?

这是 ES 的分页硬上限,模板类里定义了ES_MAX_SIZE = 10000。业务上如果需要深分页,应该改用 search_after 或 scroll 方案,而不是盲目加大 size。

问题四:getIndexMapping返回 null。

模板对 ES 7.x 做了include_type_name=true的兼容处理,但如果你用的是 8.x 且未显式指定 type 名称,映射结构可能对不上。遇到这种情况,先手动调一次GET /{index}/{type}/_mapping对比返回结构,再决定是否需要自定义解析。

性能与维护:三句可落地的话

  • 全量同步用saveBatch,增量同步用saveOrUpdate,别反过来——逐条写 ES 的性能和批量写入差一个数量级。
  • 业务数据变更处加同步钩子,比如在 Service 层保存方法成功后调用 ES 更新,同时用定时任务兜底对账,保证 ES 与数据库最终一致。
  • 上线前用getIndices列一次索引清单,配合getIndexMappingFormat检查字段类型是否符合预期,避免"索引建了但字段全是 text 没法做精确过滤"这类隐形问题。

收尾:先跑通最小闭环

回到开头的用户表场景——两百万行数据、几秒的 LIKE 查询,在我们接入 ES 后变成了几十毫秒的关键词检索,命中率也因为分词和通配支持明显提升。但请记住,ES 不是银弹:它解决的是"检索"问题,事务、强一致、复杂关联查询仍然要交给数据库。

下一步行动很明确:先在测试环境搭一个最小 ES 实例,按上文完成配置、跑通"建索引 → 写入 → 查询"三步闭环,再逐步把真实业务表同步进来。整个过程不需要引入额外依赖,改的是配置和几段 Service 代码,风险完全可控。

【免费下载链接】jeecg-boot【低代码迈入v2.0时代,一句话即可生成整个系统】企业级AI低代码平台,一键生成前后端代码甚至整个系统。 AI Skills 一句话画流程、设计表单、生成报表、大屏。内置 AI应用平台涵盖:AI聊天、知识库、流程编排、MCP插件等,兼容主流大模型。引领AI低代码「Skills 生成 → 在线配置 → 代码生成 → 手工合并->AI修改」开发模式,解决 Java 项目 90% 重复工作,提高效率又不失灵活。项目地址: https://gitcode.com/GitHub_Trending/je/jeecg-boot

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.jsqmd.com/news/1392999/

相关文章:

  • Moonlight 主题安装配置实战:5 分钟把 VS Code 换成月光下的泡泡糖配色
  • openEuler_编译64K内核包(spec文件构建)
  • Ruby 4.0 通用 RCE 反序列化利用链:突破版本限制,深入底层构建
  • Rai Pal高级技巧:离线模组加载、本地数据库管理与Wine前缀优化
  • GEO排名查询平台助力AI搜索曝光评估,解锁AI时代品牌营销方式
  • Milvus RAG 灰度上线,先核对新旧召回差异
  • 3分钟搞定3MF导入导出:Blender3mfFormat插件完整上手指南
  • 2026 河南树瘤板材源头厂家选型指南:环保等级、花色丰富度与直供模式解析 - 中国华商产业观察网
  • 婴童润护洗发沐浴露哪家好:【蜜妙诗】冲净无残留感 - 17728181569
  • 从零到第一张图只要5分钟:这份ComfyUI中文工作流合集,是AI绘画新手的“作弊码“
  • Paperclip_Agent编排平台
  • 零基础安装pakku.js弹幕过滤插件:4步装好,5分钟告别刷屏弹幕
  • Raft 组件选型:先验证日志、快照和成员变更
  • Remix平板电脑评测:Android、Windows傻傻分不清楚
  • 一个链接把整个账号视频搬回家:DouK-Downloader 抖音数据采集实战指南
  • 短链接生成原理深度解析:durl如何用Beego+Xorm打造高效URL转换引擎
  • C语言函数递归:从“自己调用自己“到“大事化小“的完整复盘
  • 快手与星竞威武达成战略合作,持续完善游戏直播与电竞内容生态
  • 2026年8月白银屋顶漏水维修哪家好?正规防水修缮科普指南 - 聪居到家
  • 在 windows 上部署免安装版本的 PostgreSQL 16.15
  • 【ORC】如何将 Parquet 文件高效转换为 ORC 格式?有哪些工具或方法?
  • 婴童润护洗发沐浴露哪家好:【蜜妙诗】舒缓浴后紧绷 - 18102756859
  • scroll-world零基础实战:用AI把任何品牌变成无缝3D滚动页面的完整攻略
  • 如何让文心生成word文档?AI导出鸭苹果版将文心一言输出的富文本标记实时转为标准docx,保留公式、表格与流程图。
  • Flowable工作流引擎
  • 分布式存储 RPC 评审:超时、重试和幂等要连着看
  • Stellarium星空文化终极指南:一键切换千年文明,看清同一片天
  • 一台旧iPhone的自救:applera1n激活锁绕过工具全解析
  • RPG Maker MV解密神器上手指南:不用装软件,浏览器一键解锁加密资源
  • 婴童蛋黄油精华霜哪家好:【蜜妙诗】抚平起皮粗糙 - 17328623207