如何在MyBatis-Plus中优雅实现复杂查询:结合JSON_CONTAINS
与动态条件构建
在现代企业级开发中,复杂的查询需求是不可避免的。尤其是在涉及多表关联、模糊匹配以及JSON字段查询时,如何高效地实现这些功能并保持代码的可读性和扩展性,是一个值得深入探讨的话题。本文将通过一个实际案例,展示如何使用 MyBatis-Plus 结合 JSON_CONTAINS
和动态条件构建来实现复杂查询,并提供完整的代码解析和优化建议。
问题背景
假设我们正在开发一个项目管理系统,其中包含以下需求:
- 根据项目名称(
projectName
)进行模糊匹配查询。 - 查询结果需要匹配
project_member
字段中的 JSON 数据,判断是否包含特定用户 ID。 - 查询逻辑需要支持动态条件拼接,确保查询性能和灵活性。
为了满足这些需求,我们需要结合 MyBatis-Plus 的动态条件构建器(Wrapper
)以及 MySQL 的 JSON_CONTAINS
函数,来实现一个高效的解决方案。
代码解析
以下是实现上述需求的核心代码:
if (StringUtils.isNotBlank(bo.getProjectName())) {
// 查询用户列表
List<SysUser> sysUserList = iSysUserService.selectSimpleLikeNickName(bo.getProjectName());
List<String> memberIds = sysUserList.stream()
.map(SysUser::getUserId)
.collect(Collectors.toList());
// 构建查询条件
lqw.and(wrapper -> {
// 添加 like 条件
wrapper.like(ItProjectManage::getProjectName, bo.getProjectName());
// 添加 JSON_CONTAINS 条件
if (ObjectUtil.isNotEmpty(memberIds)) {
wrapper.or(orWrapper -> {
for (String memberId : memberIds) {
orWrapper.or(w -> w.apply("JSON_CONTAINS(project_member, CONCAT('\"', {0}, '\"'))", memberId));
}
});
}
});
}
这段代码看似简单,但包含了多个关键点,下面我们逐一分析。
1. 用户列表查询
List<SysUser> sysUserList = iSysUserService.selectSimpleLikeNickName(bo.getProjectName());
首先,通过 selectSimpleLikeNickName
方法,根据项目名称模糊匹配查询出相关的用户列表。这一步的目的是获取所有可能与该项目相关的用户 ID 列表。
- 注意点:
selectSimpleLikeNickName
是一个自定义方法,通常会在服务层实现。- 查询结果会返回一个
SysUser
对象列表,后续通过流式操作提取userId
。
2. 动态条件构建
lqw.and(wrapper -> {
wrapper.like(ItProjectManage::getProjectName, bo.getProjectName());
...
});
在这部分代码中,我们使用了 MyBatis-Plus 的 Wrapper
动态条件构建器,逐步拼接查询条件。
-
like
条件:wrapper.like(ItProjectManage::getProjectName, bo.getProjectName());
这里实现了对
projectName
字段的模糊匹配查询。 -
or
条件与JSON_CONTAINS
:wrapper.or(orWrapper -> { for (String memberId : memberIds) { orWrapper.or(w -> w.apply("JSON_CONTAINS(project_member, CONCAT('\"', {0}, '\"'))", memberId)); } });
- 使用
JSON_CONTAINS
函数判断project_member
字段是否包含某个用户 ID。 apply
方法允许我们直接嵌入原生 SQL 表达式,灵活性非常高。- 通过
orWrapper.or
循环拼接多个JSON_CONTAINS
条件,确保所有匹配的用户 ID 都被包含在查询中。
- 使用
3. JSON字段的处理
MySQL 提供了强大的 JSON 函数支持,例如 JSON_CONTAINS
,可以用来判断 JSON 字段中是否包含特定值。
-
示例数据:
假设project_member
字段存储的是一个 JSON 数组:["user1", "user2", "user3"]
-
SQL表达式:
JSON_CONTAINS(project_member, CONCAT('\"', 'user1', '\"'))
这条语句会检查
project_member
是否包含"user1"
。
通过这种方式,我们可以轻松实现基于 JSON 字段的精确匹配查询。
4. 动态条件的优势
MyBatis-Plus 的 Wrapper
提供了非常灵活的动态条件拼接能力,能够根据业务需求动态生成 SQL 查询语句。相比手动拼接 SQL,这种方式不仅更加安全(防止 SQL 注入),而且代码更具可读性和维护性。
完整代码与优化建议
以下是完整的代码实现:
if (StringUtils.isNotBlank(bo.getProjectName())) {
// 查询用户列表
List<SysUser> sysUserList = iSysUserService.selectSimpleLikeNickName(bo.getProjectName());
List<String> memberIds = sysUserList.stream()
.map(SysUser::getUserId)
.collect(Collectors.toList());
// 构建查询条件
lqw.and(wrapper -> {
wrapper.like(ItProjectManage::getProjectName, bo.getProjectName());
if (ObjectUtil.isNotEmpty(memberIds)) {
wrapper.or(orWrapper -> {
for (String memberId : memberIds) {
orWrapper.or(w -> w.apply("JSON_CONTAINS(project_member, CONCAT('\"', {0}, '\"'))", memberId));
}
});
}
});
}
优化建议:
- 索引优化:为
project_member
字段创建适当的索引,提升JSON_CONTAINS
的查询性能。 - 批量处理:如果
memberIds
列表较大,可以考虑批量查询或分页处理,避免单次查询过重。 - 日志记录:在生产环境中,建议记录生成的 SQL 语句,便于调试和优化。
总结
本文通过一个实际案例,展示了如何利用 MyBatis-Plus 的动态条件构建器和 MySQL 的 JSON_CONTAINS
函数,实现复杂的查询需求。这种方案不仅具有良好的扩展性和灵活性,还能有效提升开发效率。
如果你在项目中遇到类似的复杂查询需求,不妨尝试这种方法!同时也欢迎在评论区分享你的经验和见解。让我们一起探索更高效的开发方式!