三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

别再写满屏的if-else了!用Mybatis-Plus的QueryWrapper和UpdateWrapper重构你的业务代码(附实战案例)

别再写满屏的if-else了!用Mybatis-Plus的QueryWrapper和UpdateWrapper重构你的业务代码(附实战案例)

告别if-else地狱:Mybatis-Plus动态条件构造器的工程实践

在电商订单管理后台,我们经常看到这样的代码:

public List<Order> queryOrders(OrderQueryDTO dto) { if (dto.getOrderNo() != null) { if (dto.getStatus() != null) { if (dto.getStartTime() != null) { // 更多嵌套if... } } } // 实际SQL执行 }

这种代码不仅难以维护,更会在业务复杂时变成"代码沼泽"。Mybatis-Plus的QueryWrapper和UpdateWrapper正是为解决这类问题而生,它们像瑞士军刀一样,让动态SQL构建变得优雅而高效。

1. 条件构造器核心价值解析

1.1 传统条件处理的三大痛点

  • 可读性灾难:多层嵌套的if-else语句让代码逻辑支离破碎
  • 维护成本高:新增查询条件需要修改原有代码结构
  • 类型不安全:字符串硬编码字段名容易拼写错误且IDE无法提示

1.2 Wrapper设计哲学对比

特性传统方式Wrapper方式
条件组合硬编码if判断链式方法调用
字段引用字符串字面量Lambda表达式
逻辑关系显式控制流声明式构建
类型安全无编译期检查强类型检查
// 传统方式 if (user.getAge() > 18) { query.where("age > 18"); } // Wrapper方式 wrapper.gt(User::getAge, 18);

2. QueryWrapper深度应用指南

2.1 基础查询构建模式

构建电商订单查询时,典型场景如下:

public Page<Order> queryOrders(OrderQueryVO vo) { return new LambdaQueryChainWrapper<>(orderMapper) .eq(vo.getOrderNo() != null, Order::getOrderNo, vo.getOrderNo()) .ge(vo.getStartTime() != null, Order::getCreateTime, vo.getStartTime()) .le(vo.getEndTime() != null, Order::getCreateTime, vo.getEndTime()) .in(CollectionUtils.isNotEmpty(vo.getStatusList()), Order::getStatus, vo.getStatusList()) .page(vo.toPage()); }

提示:LambdaQueryChainWrapper可直接返回Mapper查询结果,减少样板代码

2.2 动态条件组合技巧

处理多条件筛选时,可采用条件谓词:

wrapper.nested(w -> w .like(StringUtils.isNotBlank(keyword), Product::getName, keyword) .or() .like(StringUtils.isNotBlank(keyword), Product::getCode, keyword) ).eq(Product::getDeleted, 0);

对应生成的SQL:

WHERE (name LIKE '%手机%' OR code LIKE '%phone%') AND deleted = 0

3. UpdateWrapper实战精要

3.1 智能更新策略

CMS内容状态批量更新示例:

public void batchUpdateStatus(List<Long> ids, Integer status) { new LambdaUpdateWrapper<>(Article.class) .in(Article::getId, ids) .set(Article::getStatus, status) .set(status == PUBLISHED, Article::getPublishTime, LocalDateTime.now()) .update(); }

注意:set方法的第一个参数支持条件判断,实现条件更新

3.2 增量更新方案

避免全字段更新的优雅实现:

public void updateProductStock(Long id, int delta) { new UpdateWrapper<>(Product.class) .setSql("stock = stock + " + delta) .eq("id", id) .update(); }

4. 复杂业务场景解决方案

4.1 多表关联查询优化

使用Wrapper实现安全join:

public List<OrderDetailDTO> queryOrderDetails(Long orderId) { return orderMapper.selectJoinList(new LambdaQueryWrapper<Order>() .select(Order::getOrderNo, Order::getCreateTime) .select(OrderItem::getProductName, OrderItem::getQuantity) .eq(Order::getId, orderId) .leftJoin(OrderItem.class, OrderItem::getOrderId, Order::getId)); }

4.2 动态排序方案

前端传参动态排序实现:

wrapper.orderBy(StringUtils.isNotBlank(sortField), "asc".equals(sortOrder), sortField);

5. 工程化最佳实践

5.1 性能优化要点

  • 避免N+1查询:使用selectMaps替代selectList减少对象转换开销
  • 索引命中:确保Wrapper条件顺序与联合索引顺序一致
  • SQL打印:开发环境开启sql打印校验生成语句
# application.yml mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

5.2 安全防护策略

风险类型防护措施示例代码
SQL注入始终使用参数化构造wrapper.eq("name", param)
全表更新强制要求where条件全局拦截器检查
字段暴露使用select指定返回字段wrapper.select("id,name")

在大型电商系统中,我们通过Wrapper重构后,订单查询模块的代码量减少40%,而可维护性显著提升。特别是在促销活动期间,动态条件组合让应对各种查询需求变得游刃有余。

← 返回列表