前言:一张关联表,让你头秃了吗?
你是不是也写过这种需求:
- 查询某个用户及其角色列表
- 查询订单及明细
- 查询项目及其下属的多个阶段和负责人
最后写了一堆复杂 SQL + resultMap,运行一看,要么数据重复、要么数据丢失,最惨还可能触发 N+1 查询……
其实问题不是 MyBatis 不行,而是你用的方法不对。
三种最常见的写法
方式一:联表查询 + 扁平映射(推荐)
这是我最常用也最稳定的一种方式:写一条联表 SQL,把需要的字段都查出来,返回扁平结构,再在 Java 层进行组装。
示例 SQL:
1 2 3 4 5 | SELECT u.id AS userId, u. name AS userName, r.id AS roleId, r. name AS roleName FROM user u LEFT JOIN user_role ur ON u.id = ur.user_id LEFT JOIN role r ON ur.role_id = r.id |
特点:
- 一条 SQL 查全所有数据;
- 性能高,逻辑直观;
- 可与分页插件(如 PageHelper)完美配合。
我的建议:
Java 层用 Map
分组封装,适合在 Service 层统一做聚合逻辑。
方式二:嵌套结果(resultMap + collection)
适合结构较为清晰的“树状结构”,比如订单和订单项、项目和子模块。
示例 XML:
1 |
特点:
- 多表映射层级清晰;
- 比较适合中小型数据量。
踩坑经验:
- 写错列名字段,就会导致内层集合全是 null;
- 出现重复数据时,要手动去重或用 Set;
- 不适合配合分页,容易导致“分页的是主表,但集合是全量”。
方式三:嵌套查询(nested select)
适合逻辑解耦,按模块维护的场景。
示例 XML:
1 |
特点:
- 每条主表数据触发一次副查询;
- 易维护但 极易 N+1;
- 不适合大批量数据查询。
实战建议:
除非你能保证每次最多查询 10 条主记录,否则慎用。性能瓶颈很容易出现在这里!
你可能踩过的坑
问题 | 症状 | 原因 | 建议 |
---|---|---|---|
查询结果为 null | 子集合没数据 | 字段名或别名不匹配 | 显式写 column 属性或使用 @Results 映射 |
一对多重复数据 | 主表字段重复出现 | 联表未分组 / Java 封装没处理 | 手动分组聚合,避免直接用 List |
分页异常 | 只分页主表,子表数据混乱 | 不支持嵌套分页 | 联表 + 扁平查询是最佳方案 |
查询慢到爆炸 | N+1 查询 | 每条主记录触发一次 select | 转成 join 联查或改为批量查 |
实战建议:如何选择这三种方式?
类型 | 性能 | 可维护性 | 适合场景 |
---|---|---|---|
联表扁平查询 | 大批量、一对多、分页场景 | ||
嵌套结果 resultMap | 结构清晰、数据量适中 | ||
子查询 nested | 小数据量、逻辑独立场景 |
我的最佳实践模板(真实项目用的)
- DAO 层只查数据,不做结构组装;
- Service 层按业务模型拼装 DTO;
- 多表结果不要直接给前端,统一封装响应结构;
- DTO + Builder 模式 + Map 分组,灵活好用;
总结一句话:
多表查询选对方式,写代码少掉一半 bug。
不必纠结哪种方式最“完美”,关键是是否适合你当前场景。
记住:能用联查查全的,就别搞子查询;分页场景尽量别用嵌套结构;小数据就图方便,结构优先。
到此这篇关于MyBatis 多表查询的文章就介绍到这了,更多相关MyBatis 多表查询内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!