【MPJBaseMapper】更优雅写SQL的开源工具

利用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.2MyBatis-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 上)
    selectJoinListselectJoinPageselectJoinOneselectJoinMapselectJoinMapsselectJoinMapsPageselectJoinCount(不同版本命名略有差异,但 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. 进阶功能与亮点

  1. 子查询 / 自定义 FROM/JOIN 数据集:1.5.x 起增强明显,支持在 selectFunc 传参/子查询、join/from 使用自定义数据集(如子查询或临时表),便于应对复杂报表。GitHub

  2. 一对一 / 一对多映射(Deep)MPJBaseMapper 继承了 MPJDeepMapper,可将连表结果映射为更复杂的对象结构(例如嵌套)。适合“主对象 + 集合/子对象”场景。JavaDoc

  3. 逻辑删除、TypeHandler、别名:与 MP 配合良好,常见企业规范(逻辑删、类型转换、列枚举/别名)能无缝使用。MyBatis-Plus-Join

  4. 与 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. 踩坑与排雷

  1. 列名歧义(Column … is ambiguous)

    • 连表后 id/name 这类公共列易歧义。解决

      • selectAll(主表) + 明确选择从表列;

      • 为从表列用 selectAs 指定别名并映射到 DTO。GitHub

  2. 分页性能

    • selectJoinPage 会执行统计总数,复杂 JOIN 统计可能较慢。建议:只选必要字段、加好连接键索引、必要时改造为“先查主键分页,再二次查询详情”的模式(通用 SQL 优化思路)。

  3. 与自定义 SQL 注入器冲突

    • 如果你项目里自定义了 SqlInjector,再引入 MPJ 也会注入一个,可能出现 Bean 冲突解决:给你自己的注入器 Bean 加 @Primary 或合并配置,避免二选一报错。优快云博客

  4. 依赖版本

    • 牢记 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<>()

  • JOINleftJoin / innerJoin / rightJoin

  • 选择列selectAll / select / selectAs
    (方法命名与用法以官方文档/JavaDoc 为准,1.5.x 与 1.2.x 的核心方法名保持一致。)GitHubJavaDoc


10. 什么时候该用 MPJ,什么时候别用?

  • 该用:业务表设计规范、连表键有索引、查询列有限、需要快速开发且维护成本低。

  • 慎用:超复杂报表(几十列+多层嵌套聚合)——此类往往配专门 SQL/物化视图更稳。

  • 折中:常规查询走 MPJ;极少数“怪 SQL”保留 XML/自定义 @Select,二者并存。


11. 一页总结

  • 引入依赖 → Mapper 继承 MPJBaseMapper<T> → 用 MPJLambdaWrapperleftJoin/innerJoinselectJoinList/One/Page 输出到 DTO(用 selectAs 起别名)。

  • 优势:少写 XML、类型安全、对 MP 友好、功能覆盖够用。

  • 注意:列名歧义要用别名;分页尽量少选列;自定义 SqlInjector 记得处理 Bean 冲突;版本匹配看文档。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值