news 2026/1/12 7:58:58

Spring Boot 复杂查询的代码:解耦动态查询模板与通用工具类

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring Boot 复杂查询的代码:解耦动态查询模板与通用工具类

在 Spring Boot 项目中,处理列表查询时往往难以避免“复杂搜索”这一核心挑战。

典型场景包括:

用户管理:需支持按姓名、手机号、状态、注册时间等多个字段组合筛选。

商品搜索:涉及分类、多选标签、价格区间、关键词匹配及排序等复杂条件。

实际上,复杂搜索的实现本身并不困难,真正的难点在于如何使搜索逻辑保持清晰、可维护且易于扩展。本文将提供一套可在日常项目中直接复用的动态查询模板,其核心流程为:

SearchDTO → Service 层动态 Wrapper → Mapper → SearchVO

一、复杂搜索代码混乱的根源

通常存在以下几类典型问题:

1. DTO 字段设计混乱,所有条件堆积于同一对象

示例:

```java

String name;

String phone;

Integer status;

String keyword;

List<Long> categoryIds;

BigDecimal minPrice;

BigDecimal maxPrice;

String startTime;

String endTime;

```

随着字段不断增加,Service 层中的条件判断也随之膨胀,最终导致代码难以维护。

2. Wrapper 中充斥大量条件判断语句

例如:

```java

if (dto.getName() != null)

wrapper.like(User::getName, dto.getName());

if (dto.getStatus() != null)

wrapper.eq(User::getStatus, dto.getStatus());

if (dto.getStartTime() != null)

wrapper.ge(User::getCreateTime, dto.getStartTime());

// ...

```

数十行条件判断并列呈现,可读性与可维护性均较差。

3. 相同查询逻辑在多处重复实现

相似的“模糊搜索 + 多选 + 区间筛选”逻辑,可能在多个接口中重复编写,缺乏抽象与复用。

4. 条件逻辑难以扩展

若新增一个“标签筛选”条件,需从 Controller 至 SQL 逐层修改,变更成本高。

最终导致的结果是:搜索接口往往成为项目中维护难度最高的部分之一。

二、复杂搜索的核心难点

其本质在于查询条件的动态性与组合不确定性:

1.部分字段为可选条件,传则查询,不传则忽略;

2.部分字段需支持区间查询;

3.部分字段为多选值(IN 查询);

4.部分字段需进行模糊匹配;

5.部分条件需动态组合 AND/OR 逻辑;

6.部分查询涉及关联表筛选。

这些组合可能随时变化,因此代码必须保持结构清晰、易于调整。

解决思路应为:

将所有搜索逻辑通过统一方式进行封装,并嵌入 Wrapper 中,使其具备可配置、可扩展、可复用的特性。

三、SearchDTO:构建结构化的查询入参

设计良好的搜索接口,应从规范的数据传输对象开始。

示例:

```java

@Data

public class UserSearchDTO extends PageRequest {

private String keyword; // 统一关键字匹配(姓名/手机号)

private Integer status; // 精确匹配

private List<Long> roleIds; // 多选条件(IN 查询)

private LocalDateTime beginTime; // 时间区间起点

private LocalDateTime endTime; // 时间区间终点

}

```

设计要点:

1. 关键字统一为 keyword 字段:避免为每个可模糊查询的字段单独定义,提升接口简洁性。

2. 时间区间字段成对出现:采用 `beginTime` 与 `endTime` 的规范命名,便于统一处理。

3. 多选条件使用集合类型:便于直接应用于 `IN` 查询。

4. DTO 仅承担查询条件载体职责:不参与具体业务逻辑。

四、Service 层实现:动态 Wrapper 模板

Service 层应负责组合查询条件,并保持代码清晰。

示例模板:

```java

public PageVO<UserVO> page(UserSearchDTO dto) {

LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

// 1. 关键字多字段搜索

if (StringUtils.isNotBlank(dto.getKeyword())) {

wrapper.and(w >

w.like(User::getName, dto.getKeyword())

.or()

.like(User::getPhone, dto.getKeyword())

);

}

// 2. 状态精确匹配

wrapper.eq(dto.getStatus() != null, User::getStatus, dto.getStatus());

// 3. 多选角色查询

wrapper.in(CollectionUtils.isNotEmpty(dto.getRoleIds()),

User::getRoleId, dto.getRoleIds());

// 4. 时间区间查询

wrapper.ge(dto.getBeginTime() != null, User::getCreateTime, dto.getBeginTime());

wrapper.le(dto.getEndTime() != null, User::getCreateTime, dto.getEndTime());

// 5. 执行分页查询

Page<User> page = userMapper.selectPage(dto.toPage(), wrapper);

return PageVO.of(page, UserVO.class);

}

```

此模板结构清晰,覆盖大部分常见查询场景,并具有良好的扩展性。

五、通用查询工具类封装

为提升代码复用性,可将常用查询条件封装为工具类:

1. 模糊查询封装

```java

public static <T> void like(LambdaQueryWrapper<T> wrapper,

boolean condition,

SFunction<T, ?> column,

String value) {

if (condition && StringUtils.isNotBlank(value)) {

wrapper.like(column, value);

}

}

```

调用方式:

```java

WrapperHelper.like(wrapper, true, User::getName, dto.getName());

```

2. 多字段关键字查询封装

```java

public static <T> void keyword(LambdaQueryWrapper<T> wrapper,

String keyword,

SFunction<T, ?>... columns) {

if (StringUtils.isBlank(keyword)) return;

wrapper.and(w > {

for (int i = 0; i < columns.length; i++) {

if (i == 0) {

w.like(columns[i], keyword);

} else {

w.or().like(columns[i], keyword);

}

}

});

}

调用方式:

```java

WrapperHelper.keyword(wrapper, dto.getKeyword(),

User::getName,

User::getPhone

);

```

3. 区间查询封装

```java

public static <T, R extends Comparable<R>>

void range(LambdaQueryWrapper<T> wrapper,

R begin, R end,

SFunction<T, R> column) {

wrapper.ge(begin != null, column, begin);

wrapper.le(end != null, column, end);

}

```

调用方式:

```java

WrapperHelper.range(wrapper, dto.getBeginTime(), dto.getEndTime(), User::getCreateTime);

```

4. 集合条件查询封装

```java

public static <T> void in(LambdaQueryWrapper<T> wrapper,

List<?> list,

SFunction<T, ?> column) {

wrapper.in(CollectionUtils.isNotEmpty(list), column, list);

}

```

最终效果

Service 层代码将变得极为简洁:

```java

WrapperHelper.keyword(wrapper, dto.getKeyword(), User::getName, User::getPhone);

WrapperHelper.eq(wrapper, dto.getStatus(), User::getStatus);

WrapperHelper.in(wrapper, dto.getRoleIds(), User::getRoleId);

WrapperHelper.range(wrapper, dto.getBeginTime(), dto.getEndTime(), User::getCreateTime);

```

六、典型业务场景示例

场景一:商品多条件搜索(含排序)

SearchDTO:

```java

String keyword;

List<Long> categoryIds;

List<Integer> statusList;

BigDecimal minPrice;

BigDecimal maxPrice;

String sortField;

String sortOrder;

```

Service 实现:

```java

WrapperHelper.keyword(wrapper, dto.getKeyword(), Goods::getName, Goods::getDesc);

WrapperHelper.in(wrapper, dto.getCategoryIds(), Goods::getCategoryId);

WrapperHelper.in(wrapper, dto.getStatusList(), Goods::getStatus);

WrapperHelper.range(wrapper, dto.getMinPrice(), dto.getMaxPrice(), Goods::getPrice);

SortHelper.applySort(wrapper, dto.getSortField(), dto.getSortOrder());

```

场景二:用户复合条件筛选(OR 逻辑)

```java

wrapper.and(w > {

w.eq(dto.getGender() != null, User::getGender, dto.getGender());

w.or(dto.getIsVip() != null).eq(User::getIsVip, dto.getIsVip());

});

```

场景三:多表关联筛选(如标签过滤)

```java

wrapper.inSql(User::getId,

"SELECT user_id FROM user_tag WHERE tag_id IN "

+ convertToSql(dto.getTagIds())

);

```

七、动态查询模板的收益

采用统一模板后,代码结构将呈现以下优势:

1.Controller 层保持简洁:仅负责参数传递与结果返回。

2.Service 层逻辑统一、可读性强、易于维护:通过工具类方法组合条件,避免重复代码。

3.DTO 设计规范清晰:采用 `keyword` + `List` + `range` 的标准化结构。

查询逻辑高度复用、可灵活组合、易于扩展:新增条件或调整逻辑时,只需在工具类或 DTO 中集中修改。

自此,您将彻底告别:

满屏的 if/else 条件判断;

搜索逻辑在不同接口中的重复实现;

因单个字段变更引发的连锁式修改。

通过统一的动态查询模板,任何复杂搜索场景均可从容应对。

总结

复杂搜索的难点并非技术实现,而在于:

1.查询条件不断累积导致字段膨胀;

2.业务逻辑分散且重复,难以维护;

3.每次开发均从零开始,效率低下;

4.代码逐渐演变为难以管理的条件判断堆砌。

通过本文介绍的动态查询模板,您可将搜索逻辑标准化、模块化,从而显著提升代码质量与开发效率。

来源:小程序app开发|ui设计|软件外包|IT技术服务公司-木风未来科技-成都木风未来科技有限公司

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/2 9:40:29

No points are provided; please add points first

No points are provided; please add points first解决方法&#xff1a;sam2 跟踪没有输入条件&#xff0c;box 或者points都没有&#xff0c;解决方法&#xff0c;添加输入条件即可。

作者头像 李华
网站建设 2026/1/3 12:54:57

python 推送视频流

2025年&#xff0c;底层也是c https://gitee.com/lipku/python_rtmpstream

作者头像 李华
网站建设 2026/1/10 10:00:34

实时 数字人 DH_live 半身

DH_live https://blog.csdn.net/selifecn/article/details/144023214 视频效果&#xff0c;嘴巴不是特别 https://www.bilibili.com/video/BV1aQDGYWEvE/?spm_id_from888.80997.embed_other.whitelist https://github.com/kleinlee/DH_live https://github.com/hpc203/livepo…

作者头像 李华
网站建设 2026/1/2 1:08:26

live2d 数字人

目录 git地址&#xff1a; 安装docker&#xff1a; 软件下载&#xff1a; https://docs.live2d.com/zh-CHS/cubism-editor-tutorials/template/ git地址&#xff1a; https://github.com/wan-h/awesome-digital-human-live2d https://gitcode.com/GitHub_Trending/aw/aweso…

作者头像 李华
网站建设 2026/1/3 8:40:59

电商视觉时代:如何用Dreamshop重构“人-货-场”?

上传一张平铺图&#xff0c;三分钟后&#xff0c;一件大衣已穿在五位不同国籍的模特身上&#xff0c;背景从纽约街头切换到东京雪景——这背后是一场正在发生的供应链革命。深夜十一点&#xff0c;杭州一家跨境电商公司的运营负责人林薇&#xff0c;刚刚在AI模特工作台生成了最…

作者头像 李华