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

⚡SimpleDAO 企业实战教程(06) mergeParams 多组条件合并

⚡ SQL‑First 范式 · Java 版

🎬 SimpleDAO 企业实战教程 · 第 06 集 · mergeParams 多组条件合并

相关开源地址

  1. 核心框架源码:https://gitee.com/gao_zhenzhong/simple-dao
  2. 系统底座:https://gitee.com/gao_zhenzhong/simple-dao-starter
  3. 代码生成器:https://gitee.com/gao_zhenzhong/simple-dao-coder
  4. 实战案例(本集源码):https://gitee.com/gao_zhenzhong/simple-dao-demo

📋 前置知识

✅ 你只需要会❌ 你完全不需要会的
Java 基础(类、接口、注解、泛型)MyBatis / Hibernate 任何知识
基础 SQL(SELECT、JOIN、WHERE、子查询)XML 配置、动态标签、插件开发
Spring Boot 基础配置(数据源配置、启动类)Spring Boot 自动配置原理、条件注解
会用 IDE 运行 Maven 项目Maven 高级配置、父子工程

📚 全套教程总览(1 小时从零到生产落地)

集数 · 标题本集目录时长
01 · 单表 CRUD + 审计 + 逻辑删除实体注解 · 空 DAO · 保存审计 · ID查询 · 分页 · 逻辑删除约 6 min
02 · 联表查询 + 分页联表 SQL · VO定义 · 条件类 · page调用 · 高性能COUNT约 4 min
03 · 条件进阶:IN + 子查询IN自动展开 · 子查询拼接 · add vs and · 三种动态边界约 6 min
04 · 多表联查 + 复杂条件行锁 · updateNull · 重复性校验 · 三表联查透传 · 时间范围约 6 min
05 · 报表聚合:GROUP BY + 聚合函数三表JOIN+聚合 · 条件类复用 · 独立判空 · 日志控制到方法约 6 min
06 · mergeParams 多组条件合并(本集)多条件类定义 · SQL多位置嵌入 · mergeParams合并 · 条件跨位置复用约 5 min
07 · 多租户 + 数据权限 · AOP 破局传统痛点 · 构造器 add 租户ID · 数据权限 · 对比 MyBatis 插件约 7 min
08 · 脱敏 + 审计扩展 · 框架不设限字段脱敏(VO getter)· 审计重写 · 逻辑删除调整约 7 min

🚀 项目快速上手

本集案例依旧内置 H2 内存库,无需安装任何外部数据库,克隆项目直接启动即可运行。本集重点展示多组条件合并—— 当报表需要同时接收时间范围、业务筛选、数据权限等多组条件,且这些条件需要嵌入 SQL 的不同位置时,SimpleDAO 如何做到清晰又灵活。

完整项目层级结构

demo06_mergeParams/ ├── pom.xml └── src/main/ ├── java/example/ │ ├── DemoApplication.java // 启动类,内置全套测试逻辑 │ └── report/ │ ├── TimeCond.java // 时间条件类 │ ├── BizCond.java // 业务筛选条件类 │ ├── ValidCond.java // 有效数据条件类 │ ├── ReportDao.java // 报表 DAO(核心:mergeParams) │ └── ReportVo.java // 报表结果 VO └── resources/ ├── application.yml // 极简数据源配置 └── schema.sql // 三表建表 + 测试数据

1. Maven 依赖pom.xml

说明:仅依赖 Spring JDBC + SimpleDAO 核心包,依赖体积缩减至 1/3。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>com.simple</groupId><artifactId>simple-dao</artifactId></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>

2. 配置文件application.yml

说明:无框架专属复杂配置,仅标准 Spring 数据源。simple-dao.show-sql: false全局关闭 SQL 日志。

spring:datasource:url:jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1driver-class-name:org.h2.Driverusername:sapassword:sql:init:schema-locations:classpath:schema.sqlmode:alwayssimple-dao.show-sql:false

3. 建表脚本 + 测试数据schema.sql

说明:包含用户表sys_user、订单表bus_order、商品表bus_goods,一套典型的一对多对多关联。测试数据已预置,开箱即用。

-- 1. 用户表(3条)DROPTABLEIFEXISTSsys_user;CREATETABLEsys_user(idBIGINTPRIMARYKEY,nameVARCHAR(20)NOTNULL,ageINTNOTNULL,emailVARCHAR(50),drTINYINTDEFAULT0COMMENT'删除标记 0-未删 1-已删');INSERTINTOsys_user(id,name,age,email)VALUES(1,'张三',25,'zhangsan@test.com'),(2,'李四',30,'lisi@test.com'),(3,'王五',28,'wangwu@test.com');-- 2. 订单表(4条)DROPTABLEIFEXISTSbus_order;CREATETABLEbus_order(idBIGINTPRIMARYKEY,user_idBIGINTNOTNULLCOMMENT'关联sys_user.id',order_noVARCHAR(30)NOTNULLCOMMENT'订单号',create_timeDATETIMENOTNULLCOMMENT'创建时间',create_byBIGINTNOTNULLCOMMENT'创建人',update_timeDATETIMENOTNULLCOMMENT'修改时间',update_byBIGINTNOTNULLCOMMENT'修改人',drTINYINTDEFAULT0COMMENT'删除标记 0-未删 1-已删',FOREIGNKEY(user_id)REFERENCESsys_user(id));INSERTINTObus_order(id,user_id,order_no,create_time,create_by,update_time,update_by)VALUES(1,1,'ORD2026001','2026-02-01 10:00:00',1,'2026-02-01 10:00:00',1),(2,1,'ORD2026002','2026-02-02 14:30:00',1,'2026-02-02 14:30:00',1),(3,2,'ORD2026003','2026-02-03 09:15:00',2,'2026-02-03 09:15:00',2),(4,3,'ORD2026004','2026-02-04 16:20:00',3,'2026-02-04 16:20:00',3);-- 3. 商品表(3条)DROPTABLEIFEXISTSbus_goods;CREATETABLEbus_goods(idBIGINTPRIMARYKEY,order_idBIGINTNOTNULLCOMMENT'关联bus_order.id',goods_nameVARCHAR(50)NOTNULLCOMMENT'商品名',priceDECIMAL(10,2)NOTNULLCOMMENT'商品价格',drTINYINTDEFAULT0COMMENT'删除标记 0-未删 1-已删',FOREIGNKEY(order_id)REFERENCESbus_order(id));INSERTINTObus_goods(id,order_id,goods_name,price)VALUES(1,1,'手机',1999.00),(2,2,'耳机',199.00),(3,3,'键盘',299.00);

🔧 核心业务代码演示

第一层:实体类Goods.java

说明@Table绑定表名,@Id标记主键。本集重点在多组条件合并,实体类快速过。

@Setter@Getter@Table("bus_goods")publicclassGoods{@IdprivateLongid;privateLongorderId;privateStringgoodsName;privateBigDecimalprice;privateBytedr;}

第二层:报表 VOReportVo.java

说明:按商品分组统计的结果,包含商品名、订单数、商品数、总金额、均价。

@DatapublicclassReportVo{privateStringgoodsName;// 商品名privateIntegerorderCount;// 订单数privateIntegergoodsCount;// 商品数privateBigDecimaltotalAmount;// 总金额privateBigDecimalavgPrice;// 均价}

第三层:条件类(本集核心:多条件类各司其职)

说明:我们定义了三个独立的条件类,各司其职、互不干扰。每个条件类只关心自己那部分规则,通过继承BaseCondition并在addCondition()中定义自己的条件逻辑。

TimeCond.java—— 时间范围条件
@Setter@GetterpublicclassTimeCondextendsBaseCondition{privateLocalDateTimeorderTimeStart;// 开始时间privateLocalDateTimeorderTimeEnd;// 结束时间@OverrideprotectedvoidaddCondition(){add("AND create_time >= ?",orderTimeStart);add("AND create_time <= ?",orderTimeEnd);}}
BizCond.java—— 业务筛选条件
@Setter@GetterpublicclassBizCondextendsBaseCondition{privateStringgoodsName;// 商品名(模糊)privateDoublepriceMin;// 最低价格privateDoublepriceMax;// 最高价格@OverrideprotectedvoidaddCondition(){add("AND t.price >= ?",priceMin);add("AND t.price <= ?",priceMax);add("AND t.goods_name LIKE ?",goodsName,3);}}
ValidCond.java—— 有效数据条件
@Setter@GetterpublicclassValidCondextendsBaseCondition{privateStringuserName;// 用户名(模糊)@OverrideprotectedvoidaddCondition(){add("AND name LIKE ?",userName,3);add("AND dr = 0");// 固定过滤}}

第四层:DAO 层ReportDao.java(核心:mergeParams)

说明:需求是「按商品分组统计,但筛选条件分别来自三张表,且要嵌入 SQL 的不同位置」:

  • 时间条件→ 嵌入订单子查询(timeCond.and()
  • 业务条件→ 作用于主查询 WHERE(bizCond.and()
  • 有效用户条件→ 嵌入用户子查询(validCond.where()

三个条件片段被精确地放置在了它们该去的地方。最后通过mergeParams()按顺序合并参数列表。

@RepositorypublicclassReportDaoextendsBaseDao<Goods>{/** * 多条件合并报表统计 * 条件分别嵌入 SQL 的 3 个不同位置 */publicList<ReportVo>reportGoodsByMerge(TimeCondtimeCond,BizCondbizCond,ValidCondvalidCond){Stringsql="SELECT t.goods_name, COUNT(o.id) order_count, COUNT(t.id) goods_count, "+"SUM(t.price) total_amount, AVG(t.price) avg_price FROM bus_goods t "+"JOIN (SELECT id, user_id FROM bus_order WHERE dr=0 "+timeCond.and()+") o ON t.order_id = o.id "+"WHERE t.dr = 0 "+bizCond.and()+"AND o.user_id IN (SELECT id FROM sys_user "+validCond.where()+") GROUP BY t.goods_name";// 按顺序合并三个条件类的参数数组returnlist(sql,ReportVo.class,BaseCondition.mergeParams(timeCond,bizCond,validCond));}}
📌mergeParams两大核心作用
作用说明示例
合并参数按传入顺序把多个条件类的参数列表合并成一个完整数组,永不乱序mergeParams(timeCond, bizCond, validCond)
条件复用同一个条件类可在 SQL 不同位置多次出现,参数同样可以重复传入下文的 UNION ALL 示例
📌 条件复用典型场景:UNION ALL

当你在 UNION ALL 中需要对多个独立查询应用完全相同的时间条件时,mergeParams配合条件复用可以避免重复构造:

-- 纯 SQL:两段查询共用同一个时间范围SELECT'收入'AStype,SUM(amount)FROMt_incomeWHEREcreate_time>=?ANDcreate_time<=?UNIONALLSELECT'退费'AStype,SUM(amount)FROMt_refundWHEREcreate_time>=?ANDcreate_time<=?
// Java 代码:同一个 dateCond 在 SQL 中出现两次,mergeParams 也传两次Stringsql="SELECT '收入' AS type, SUM(amount) FROM t_income WHERE 1=1 "+dateCond.and()+" UNION ALL "+"SELECT '退费' AS type, SUM(amount) FROM t_refund WHERE 1=1 "+dateCond.and();returnlist(sql,IncomeVO.class,BaseCondition.mergeParams(dateCond,dateCond));

要点:条件类没有被绑定在某个固定的 SQL 片段上,它是一个完全独立的、可携带的参数单元。你可以在任何需要它的地方调用.and().where(),然后由mergeParams按指定顺序统一调度这些单元产生的参数。将来要拿掉或新增某个条件,只需修改一处,其他位置完全不用动。


📝 业务调用与运行日志

场景一:全条件组合

调用代码:传入时间范围 + 业务筛选(商品名模糊 + 价格下限)+ 有效用户(用户名模糊)。

LocalDateTimestart=LocalDateTime.of(2026,2,1,0,0,0);LocalDateTimeend=LocalDateTime.of(2026,2,4,23,59,59);TimeCondtimeCond=TimeCond.builder().orderTimeStart(start).orderTimeEnd(end).build();BizCondbizCond=BizCond.builder().goodsName("手").priceMin(1000.0).build();ValidCondvalidCond=ValidCond.builder().userName("张").build();reportDao.reportGoodsByMerge(timeCond,bizCond,validCond).forEach(i->log.info("结果:{}",i));

运行日志

[INFO] SELECT t.goods_name, COUNT(o.id) order_count, COUNT(t.id) goods_count, SUM(t.price) total_amount, AVG(t.price) avg_price FROM bus_goods t JOIN (SELECT id, user_id FROM bus_order WHERE dr=0 AND create_time >= '2026-02-01 00:00:00' AND create_time <= '2026-02-04 23:59:59') o ON t.order_id = o.id WHERE t.dr = 0 AND t.price >= 1000.0 AND t.goods_name LIKE '%手%' AND o.user_id IN (SELECT id FROM sys_user WHERE name LIKE '%张%' AND dr=0) GROUP BY t.goods_name [INFO] 结果:ReportVo(goodsName=手机, orderCount=1, goodsCount=1, totalAmount=1999.00, avgPrice=1999.00)

场景二:缺时间条件

调用代码:传入空的TimeCond,时间条件被自动忽略。

reportDao.reportGoodsByMerge(newTimeCond(),bizCond,validCond).forEach(i->log.info("结果:{}",i));

运行日志

[INFO] SELECT t.goods_name, COUNT(o.id) order_count, COUNT(t.id) goods_count, SUM(t.price) total_amount, AVG(t.price) avg_price FROM bus_goods t JOIN (SELECT id, user_id FROM bus_order WHERE dr=0) o ON t.order_id = o.id WHERE t.dr = 0 AND t.price >= 1000.0 AND t.goods_name LIKE '%手%' AND o.user_id IN (SELECT id FROM sys_user WHERE name LIKE '%张%' AND dr=0) GROUP BY t.goods_name [INFO] 结果:ReportVo(goodsName=手机, orderCount=1, goodsCount=1, totalAmount=1999.00, avgPrice=1999.00)

关键观察:日志中的 SQL完全没有时间范围条件—— 因为TimeCond为空对象,add()方法检测到值为null自动忽略。你不需要写任何if (timeCond != null)判断。


📌 本集核心总结

  1. 多条件类各司其职:按业务维度拆分条件类(时间、业务、权限),每个类只管理自己的规则,职责清晰,互不干扰。

  2. SQL 多位置嵌入:同一个查询中,不同条件可以嵌入 SQL 的不同位置(主查询 WHERE、子查询、JOIN 子句等),通过.where().and()精准控制。

  3. mergeParams合并参数:静态方法BaseCondition.mergeParams(cond1, cond2, ...)按顺序合并多个条件类的参数数组,顺序完全由你控制,永不乱序。

  4. 条件跨位置复用:同一个条件类可在 SQL 不同位置多次出现,mergeParams同样支持重复传入,特别适合 UNION ALL 等场景。

  5. 零 XML,SQL 即所见:全程无 XML、无标签、无 OGNL。你手写的 SQL 片段 + 条件类生成的片段 = 最终执行的 SQL,完全透明。

  6. 条件类 = 可组合的积木:条件类没有被绑定在某个固定的 SQL 片段上,它是完全独立的、可携带的参数单元。将来要拿掉或新增某个条件,只需修改一处,其他位置完全不用动。

下一集,我们将进入企业级开发的另一个硬核战场 ——多租户 + 数据权限 · AOP 破局,看 SimpleDAO 如何用 Spring 原生 AOP 优雅解决传统 MyBatis 拦截器的诸多痛点,敬请期待!

http://www.cnnetsun.cn/news/3052207.html

相关文章:

  • 卫星合成孔径雷达技术解析 穿透云雨雾霾实现全天时对地探测
  • DRV10964评估板实战:BLDC电机驱动硬件拆解、配置与调优指南
  • 靠《堡垒之夜》游戏录像训练AI,General Intuition获3.2亿美元融资!
  • Conda 环境一键搬家:用 conda-pack 打包带走,连网都不用
  • 现在学习SEO还来得及吗?
  • 重要的桥接Python库
  • ChatGPT Plus 支付失败后,为什么不建议连续重试?
  • 告别论文熬夜卡文!Okbiye 毕业论文 AI 写作工作台全拆解,一站式适配全学段学术创作
  • 企业级 AI 工具选购指南:ChatGPT Team vs Claude Team vs Gemini Business
  • 我来发一个做股票从没亏过的指标成功率
  • GPT-4.1 Nano 轻量化智能应用落地指南
  • 低代码平台,让企业应用开发不再难
  • ESP32S3 AP+MQTT Broker
  • LinkLifeVerse OS:让数据价值留在县域
  • 3个实际场景告诉你,为什么你需要Winhance中文版优化Windows系统
  • QKeyMapper:5分钟解决你的Windows按键映射烦恼,手柄玩PC游戏不是梦!
  • awesome-ratatui:Rust 终端 UI 生态全景收录
  • ngx_http_index_handler
  • cu-cockpit:轻量级Linux单节点运维管理平台入门指南
  • Python驱动Aspose.Words:精准提取Word文档结构化数据的实战指南
  • SAP二维码尺寸与布局的实战调优
  • 模型初始化常用参数设置
  • 大数据本科生不考研,靠项目能进优质企业吗?
  • 老旧安卓电视直播优化终极指南:如何用MyTV-Android让低端设备流畅播放
  • 非结构化数据服务模型训练的处理方式
  • 【Springboot毕设全套源码+文档】基于springboot智能垃圾分类系统的设计与实现(丰富项目+远程调试+讲解+定制)
  • Lua学习笔记:库函数
  • 2026闭眼入!5款AI论文工具亲测,摆脱无效加班,初稿质量效率翻倍
  • Adobe GenP 3.0完整教程:免费解锁Adobe CC全系列软件的终极指南
  • 免费音乐解锁工具:3分钟掌握跨平台音乐解密完整指南