利用lambda表达式规范表字段 避免拼写错误,优雅的写sql。
1.带分页的查多条数据 selectList
startPage();
List<App> list = appBusinesslogMapper.
selectList(Wrappers.lambdaQuery(App.class)
.select(App::getBusinessContent, App::getBusinessType, App::getBusinessStatus, App::getBusinessDate, App::getCertId)
.eq(App::getUserId, getUserId())
.orderByDesc(App::getBusinessId));
return getDataTable(list);
2.查单条数据 selectOne
App association = appAssociationMapper.selectOne(Wrappers.lambdaQuery(App.class)
.eq(App::getUserId, userId)
.eq(App::getEnterpriseId, enterpriseId));
3.修改操作
appSealMapper.update(Wrappers.lambdaUpdate(App.class)
.set(App::getStatus, 0)
.eq(App::getEnterpriseId, enterprise.getEnterpriseId())
.eq(App::getStatus, 1));
1. MPJBaseMapper 是什么?能做什么?
MPJBaseMapper 来自第三方开源库 MyBatis-Plus-Join(简称 MPJ)。它在 MyBatis-Plus 的基础上增强了“连表查询”能力,让你用类似 QueryWrapper 的链式语法完成 LEFT/INNER/RIGHT JOIN,还支持 DTO 投影、别名、分页、逻辑删除、一对一/一对多映射 等常见需求。其核心就是配合 MPJLambdaWrapper / MPJQueryWrapper 构建 join 条件,然后通过 selectJoinList / selectJoinPage / selectJoinOne … 等方法执行。GitHubMyBatis-Plus-Join
一句话:不改你的 CRUD,用更优雅的方式写多表查询(“只做增强不做改变”是官方定位)。MyBatis-Plus-Join
2. 安装与版本要求
(我项目中使用的mybatis-plus 3.58和yulichang 1.51)
Maven 最新依赖:
<dependency> <groupId>com.github.yulichang</groupId> <artifactId>mybatis-plus-join-boot-starter</artifactId> <version>1.5.4</version> </dependency>
截至 2025-08-14,最新稳定版为 1.5.4,要求 MyBatis-Plus ≥ 3.1.2。MyBatis-Plus-Join
分页插件(用于 selectJoinPage):跟 MyBatis-Plus 一样配置分页拦截器即可。
3. 快速上手(照抄就能跑)
3.1 定义实体(示例)
@Data @TableName("user") public class User { @TableId private Long id; private String name; private Integer status; } @Data @TableName("user_address") public class UserAddress { @TableId private Long id; private Long userId; private String tel; private Long areaId; } @Data @TableName("area") public class Area { @TableId private Long id; private String province; private String city; }
3.2 定义返回 DTO(只装你要的字段)
@Data public class UserDTO { private Long id; private String name; private String tel; private String userAddress; // 通过 selectAs 映射别名 private String province; private String city; }
3.3 定义 Mapper:继承 MPJBaseMapper
@Mapper public interface UserMapper extends MPJBaseMapper<User> {}
继承 MPJBaseMapper 后即可获得
selectJoinList / selectJoinPage / selectJoinOne …等连表方法。JavaDoc
3.4 写第一条连表查询(Lambda 版,最常用)
@Resource private UserMapper userMapper; public List<UserDTO> demo() { MPJLambdaWrapper<User> wrapper = JoinWrappers.lambda(User.class) .selectAll(User.class) // 主表全选 .select(UserAddress::getTel) // 选取从表字段 .selectAs(UserAddress::getAddress, UserDTO::getUserAddress) // 别名映射到 DTO .select(Area::getProvince, Area::getCity) .leftJoin(UserAddress.class, UserAddress::getUserId, User::getId) .leftJoin(Area.class, Area::getId, UserAddress::getAreaId) .eq(User::getStatus, 1) .like(UserAddress::getTel, "138"); // 多条记录 return userMapper.selectJoinList(UserDTO.class, wrapper); }
分页查询:
IPage<UserDTO> page = userMapper.selectJoinPage(new Page<>(1, 10), UserDTO.class, wrapper);
上面这套写法与官方 README 中的“Lambda 形式用法”一致,
leftJoin / selectAs / selectJoinList / selectJoinPage等是最常用的 API。GitHub
4. 常用 API 速查
-
查询类方法(在
MPJBaseMapper上)
selectJoinList、selectJoinPage、selectJoinOne、selectJoinMap、selectJoinMaps、selectJoinMapsPage、selectJoinCount(不同版本命名略有差异,但 1.5.x 仍保持兼容与延续)。JavaDoc -
构造条件(Wrapper)
-
MPJLambdaWrapper<T>:最常用,类型安全(SFunction)。 -
MPJQueryWrapper<T>:字符串方式,更灵活(复杂 SQL/函数等)。 -
链式:
leftJoin / innerJoin / rightJoin / on / eq / ne / gt / like / in / groupBy / orderBy / having …。GitHub
-
-
投影与别名
-
selectAll(clazz)、select(FromTable::getField)、selectAs(FromTable::getField, Dto::setField)。GitHub
-
5. 进阶功能与亮点
-
子查询 / 自定义 FROM/JOIN 数据集:1.5.x 起增强明显,支持在
selectFunc传参/子查询、join/from使用自定义数据集(如子查询或临时表),便于应对复杂报表。GitHub -
一对一 / 一对多映射(Deep):
MPJBaseMapper继承了MPJDeepMapper,可将连表结果映射为更复杂的对象结构(例如嵌套)。适合“主对象 + 集合/子对象”场景。JavaDoc -
逻辑删除、TypeHandler、别名:与 MP 配合良好,常见企业规范(逻辑删、类型转换、列枚举/别名)能无缝使用。MyBatis-Plus-Join
-
与 MP 生态一致:拦截器、分页、自动填充等依旧按 MP 的用法来,几乎零迁移成本。MyBatis-Plus-Join
6. 实战模板:常见查询场景
6.1 条件可选 + 只选所需字段(性能友好)
public IPage<UserDTO> queryUsers(String telLike, List<Long> areaIds, int pn, int size) {
MPJLambdaWrapper<User> w = JoinWrappers.lambda(User.class)
.select(User::getId, User::getName) // 主表只选必要列
.selectAs(UserAddress::getAddress, UserDTO::getUserAddress)
.select(Area::getProvince, Area::getCity)
.leftJoin(UserAddress.class, UserAddress::getUserId, User::getId)
.leftJoin(Area.class, Area::getId, UserAddress::getAreaId)
.eq(User::getStatus, 1)
.like(telLike != null, UserAddress::getTel, telLike) // 条件可选
.in(areaIds != null && !areaIds.isEmpty(), Area::getId, areaIds)
.orderByDesc(User::getId);
return userMapper.selectJoinPage(new Page<>(pn, size), UserDTO.class, w);
}
6.2 仅在有条件时才 JOIN(动态 JOIN)
MPJLambdaWrapper<User> w = JoinWrappers.lambda(User.class).selectAll(User.class);
if (needAddress) {
w.leftJoin(UserAddress.class, UserAddress::getUserId, User::getId)
.select(UserAddress::getTel);
}
List<UserDTO> list = userMapper.selectJoinList(UserDTO.class, w);
6.3 使用字符串 Wrapper 处理函数/复杂表达式
MPJQueryWrapper<User> w = new MPJQueryWrapper<User>()
.select("t.id, t.name, addr.tel, CONCAT(area.province,'-',area.city) AS region")
.leftJoin("user_address addr ON addr.user_id = t.id")
.leftJoin("area ON area.id = addr.area_id")
.like("addr.tel", "138");
List<Map<String, Object>> rows = userMapper.selectJoinMaps(w);
7. 与“写 XML 连表”相比的优势
-
类型安全 + 更少模板代码:
Lambda指定列与实体属性强绑定,避免手写列名出错。 -
DTO 投影天然支持:
selectAs直达目标字段,减少二次拷贝。 -
统一风格:依旧是 MP 的链式风格,团队学习成本低。
-
功能覆盖常见企业场景:分页、逻辑删除、复杂 where/group/having、子查询等。GitHubMyBatis-Plus-Join
8. 踩坑与排雷
-
列名歧义(Column … is ambiguous)
-
连表后
id/name这类公共列易歧义。解决:-
用
selectAll(主表)+ 明确选择从表列; -
为从表列用
selectAs指定别名并映射到 DTO。GitHub
-
-
-
分页性能
-
selectJoinPage会执行统计总数,复杂 JOIN 统计可能较慢。建议:只选必要字段、加好连接键索引、必要时改造为“先查主键分页,再二次查询详情”的模式(通用 SQL 优化思路)。
-
-
与自定义 SQL 注入器冲突
-
如果你项目里自定义了
SqlInjector,再引入 MPJ 也会注入一个,可能出现 Bean 冲突。解决:给你自己的注入器 Bean 加@Primary或合并配置,避免二选一报错。优快云博客
-
-
依赖版本
-
牢记 MPJ 与 MyBatis-Plus 的版本匹配(MPJ 1.5.4 需 MP 3.1.2+),升级注意查看 Release Notes。MyBatis-Plus-JoinGitHub
-
9. 常用方法清单(速记)
-
多条:
selectJoinList(ResultType.class, wrapper) -
单条:
selectJoinOne(ResultType.class, wrapper) -
分页:
selectJoinPage(page, ResultType.class, wrapper) -
Map 结果:
selectJoinMap(wrapper)/selectJoinMaps(wrapper)/selectJoinMapsPage(page, wrapper) -
计数:
selectJoinCount(wrapper) -
Wrapper 入口:
JoinWrappers.lambda(Entity.class)、new MPJLambdaWrapper<>()、new MPJQueryWrapper<>() -
JOIN:
leftJoin / innerJoin / rightJoin -
选择列:
selectAll / select / selectAs
(方法命名与用法以官方文档/JavaDoc 为准,1.5.x 与 1.2.x 的核心方法名保持一致。)GitHubJavaDoc
10. 什么时候该用 MPJ,什么时候别用?
-
该用:业务表设计规范、连表键有索引、查询列有限、需要快速开发且维护成本低。
-
慎用:超复杂报表(几十列+多层嵌套聚合)——此类往往配专门 SQL/物化视图更稳。
-
折中:常规查询走 MPJ;极少数“怪 SQL”保留 XML/自定义
@Select,二者并存。
11. 一页总结
-
引入依赖 → Mapper 继承
MPJBaseMapper<T>→ 用MPJLambdaWrapper写leftJoin/innerJoin→selectJoinList/One/Page输出到 DTO(用selectAs起别名)。 -
优势:少写 XML、类型安全、对 MP 友好、功能覆盖够用。
-
注意:列名歧义要用别名;分页尽量少选列;自定义
SqlInjector记得处理 Bean 冲突;版本匹配看文档。
23万+

被折叠的 条评论
为什么被折叠?



