十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MyBatis开发效率革命:Mybatis-Plus与MybatisX组合实战指南

MyBatis开发效率革命:Mybatis-Plus与MybatisX组合实战指南 1. 项目概述为什么我们需要MybatisX和Mybatis-Plus如果你和我一样常年泡在Java后端开发里和数据库打交道是家常便饭那你肯定对MyBatis又爱又恨。爱它的灵活和强大SQL完全掌握在自己手里恨它的繁琐和重复一个简单的CRUD增删改查也得写接口、写XML、写实体类项目稍微大点Mapper文件多得能让人看花眼。这种“体力活”不仅消耗时间更容易因为手误引入低级Bug。所以当团队里新来的小伙伴还在吭哧吭哧手写resultMap的时候我通常会直接甩给他两个名字Mybatis-Plus和MybatisX。这俩不是什么高深的新框架而是能实实在在把你从重复劳动中解放出来的“效率神器”。今天我就结合自己多年的踩坑和实战经验来深扒一下这两个工具告诉你它们到底怎么用以及如何组合起来让你的开发效率飞起来。简单来说Mybatis-Plus是一个MyBatis的增强工具在MyBatis的基础上只做增强不做改变内置了通用的Mapper、Service你只需简单配置即可实现单表大部分CRUD操作连XML都可以不写。而MybatisX则是一款主要面向IntelliJ IDEA的插件它提供了强大的代码提示、代码生成、跳转和重构功能是连接你的Java实体、Mapper接口和XML文件的智能桥梁。它们一个在“运行时”帮你简化操作一个在“开发时”帮你提升编码体验双剑合璧堪称MyBatis生态里的“黄金搭档”。2. Mybatis-Plus核心功能与实战配置2.1 核心设计思想约定大于配置Mybatis-Plus简称MP的成功很大程度上得益于其“约定大于配置”的理念。它预设了一套合理的默认规则比如默认将数据库表名映射到同名的实体类将表的字段名映射到实体类中驼峰命名的属性。只要你遵守这些约定就可以用极少的配置完成绝大部分工作。这避免了我们在每个Mapper里写大量重复的SQL片段把精力集中在真正的业务逻辑上。2.2 基础集成与配置详解首先在你的Spring Boot项目中引入依赖。这里以最新的稳定版为例dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.6/version !-- 请使用最新稳定版本 -- /dependency接下来是核心配置。在application.yml中基础的配置和原生MyBatis差不多但MP增加了一些特有的配置项spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/your_db?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password mybatis-plus: configuration: # 控制台打印MP自带的SQL日志非原生MyBatis日志调试时非常有用 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启下划线转驼峰这是默认true的通常保持开启 map-underscore-to-camel-case: true global-config: db-config: # 全局主键类型。AUTO为数据库自增INPUT为手动输入ASSIGN_ID为MP雪花算法ASSIGN_UUID为UUID id-type: ASSIGN_ID # 表名前缀如果所有表都有共同前缀如t_可以在这里配置实体类名就不需要带前缀了 # table-prefix: t_ mapper-locations: classpath*:/mapper/**/*.xml # XML文件位置MP的通用方法不需要XML但自定义SQL仍需注意log-impl配置为StdOutImpl会在控制台输出MP执行的具体SQL语句及参数这在开发阶段排查问题至关重要。但在生产环境建议关闭或切换到性能更好的日志实现。2.3 通用CRUD告别简单SQL编写这是MP最爽的功能。假设你有一个User实体类对应user表。Data // 使用Lombok简化代码 TableName(user) // 如果表名和实体类名不一致需要用此注解指定 public class User { TableId(type IdType.ASSIGN_ID) // 指定主键及生成策略 private Long id; private String name; private Integer age; private String email; }对应的Mapper接口只需要继承MP提供的BaseMapper即拥有了全套单表CRUD方法Repository // Spring的注解可省略但建议加上 public interface UserMapper extends BaseMapperUser { // 无需定义任何方法就已经拥有了selectById, insert, updateById, deleteById, selectList等方法 }现在你可以在Service中直接使用Service public class UserServiceImpl extends ServiceImplUserMapper, User implements UserService { // 继承了ServiceImpl也拥有了很多便捷的Service层方法 public void someBusiness() { // 1. 查询所有年龄大于18的用户 ListUser userList this.lambdaQuery() .gt(User::getAge, 18) .list(); // 2. 根据ID更新 User user new User(); user.setId(1L); user.setName(UpdatedName); this.updateById(user); // 3. 根据条件删除 this.remove(new QueryWrapperUser().eq(email, testexample.com)); } }这里用到了lambdaQuery()这是MP提供的Lambda表达式查询方式通过User::getAge这种方式引用字段是类型安全的避免了手写字段名字符串导致的硬编码错误在重构时IDE也能自动识别强烈推荐使用。2.4 条件构造器与分页插件复杂查询离不开条件构造器。MP提供了QueryWrapper和LambdaQueryWrapper。// 使用LambdaQueryWrapper类型安全 LambdaQueryWrapperUser lqw new LambdaQueryWrapper(); lqw.like(User::getName, 张) // 名字包含“张” .between(User::getAge, 20, 30) // 年龄在20到30之间 .orderByDesc(User::getId); // 按ID倒序 ListUser list userMapper.selectList(lqw); // 使用QueryWrapper需要写字段名字符串 QueryWrapperUser qw new QueryWrapper(); qw.select(id, name, age) // 只查询特定字段 .eq(status, 1) .or(w - w.gt(age, 60).lt(age, 18));对于分页需要先配置分页插件一个拦截器Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); // 添加分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }使用起来非常简单PageUser page new Page(1, 10); // 查询第1页每页10条 LambdaQueryWrapperUser lqw new LambdaQueryWrapper(); lqw.gt(User::getAge, 18); PageUser resultPage userMapper.selectPage(page, lqw); System.out.println(总记录数 resultPage.getTotal()); System.out.println(当前页数据 resultPage.getRecords());3. MybatisX插件IDEA中的开发加速器如果说Mybatis-Plus解决了运行时的问题那么MybatisX就是解决开发时痛点的利器。它是一款专为MyBatis定制的IDE插件支持IntelliJ IDEA和基于IntelliJ的社区版如Apache NetBeans的某些版本可能不支持主要支持IDEA。3.1 安装与基础功能识别在IDEA中打开Settings - Plugins搜索“MybatisX”找到由“MyBatisX”发布的插件进行安装并重启IDE。安装成功后你会获得以下肉眼可见的提升XML与Java代码的智能跳转在Mapper接口的方法名上如selectById按住CtrlMac是Cmd点击可以直接跳转到XML中对应的select标签反之亦然。再也不用在文件海里手动搜索id了。小鸟图标在Mapper接口和XML文件的侧边栏会出现一只蓝色或红色的小鸟图标。点击小鸟可以在接口方法和XML语句之间快速跳转一目了然。SQL语句补全与提示在XML中编写SQL时能获得数据库表名、字段名的代码补全就像写Java代码一样流畅。3.2 核心神器一键代码生成这是MybatisX插件最核心、最节省时间的功能。它可以根据数据库表一键生成实体类(Entity)、Mapper接口、Mapper XML文件、Service接口及实现类、Controller控制器。操作步骤在IDEA右侧找到「Database」数据库工具窗口连接上你的数据库。找到你要生成代码的表右键点击。在右键菜单中选择「MybatisX-Generator」。会弹出一个配置窗口这是关键步骤。生成配置详解与技巧# 以下是在配置窗口中需要关注的核心选项我通常会这样设置 模块路径: src/main/java # 生成代码的根目录 基础包名: com.yourcompany.project # 生成的类所在的包前缀 相对包路径: entity: entity # 实体类包如 com.yourcompany.project.entity mapper: mapper # Mapper接口包 service: service # Service接口包 serviceImpl: service.impl # Service实现类包 controller: controller # Controller包 # 表配置 表名: user # 你选择的表 类名: User # 生成的实体类名默认是表名转驼峰可以手动修改 字段前缀: # 例如如果表字段都叫user_name, user_age可以填写user_生成实体时会自动去掉此前缀 # 生成选项根据团队规范勾选 ☑ 生成注解如 TableName, TableField # 必选MP依赖这些注解 ☑ 使用Lombok生成 Data, Builder 等 # 强烈推荐极大简化实体类 ☑ 生成Swagger注解ApiModel, ApiModelProperty # 如果项目用Swagger做API文档可以选 ☑ 生成Mybatis-Plus注解TableId, TableLogic等 # 如果使用MP必选 ☑ 覆盖已存在文件 # 谨慎使用避免误覆盖手写的逻辑 ☑ 生成Service层 # 按需 ☑ 生成Controller层 # 按需对于纯后端服务可能不需要配置好后点击「生成」一个完整的、符合MP规范的CRUD代码层就瞬间创建好了。这至少节省了半小时手写和核对的时间而且风格统一不易出错。实操心得对于字段很多的表生成后务必快速浏览一遍实体类。检查字段类型映射是否正确特别是数据库的datetime、decimal对应Java的LocalDateTime、BigDecimal检查是否有字段需要特殊注解如TableField(fill FieldFill.INSERT)用于自动填充创建时间。3.3 其他实用功能JPA风格提示在Mapper接口中如果你输入findBy插件会给出类似findByNameAndAge这样的提示虽然MP本身不支持这种命名自动生成SQL但这个提示能帮你快速定义方法名然后你可以配合Select注解或在XML中实现。XML标签补全输入会自动提示select,insert,resultMap等标签并且自动闭合。字段名快速输入在XML的SQL里输入#{}时插件能提示当前实体类的属性名避免拼写错误。4. Mybatis-Plus与MybatisX的协同工作流理解了各自的功能我们来看看在实际项目中如何将两者无缝结合形成高效的开发流水线。4.1 标准开发流程设计数据库表在数据库中创建好表结构。使用MybatisX生成基础代码通过插件一键生成Entity, Mapper, Service, Controller等所有层的基础CRUD代码。此时生成的Mapper已经继承了BaseMapperService继承了ServiceImpl实体类也加好了MP的注解。补充业务逻辑在生成的Service实现类中添加具体的业务逻辑。对于简单的单表操作直接使用父类提供的lambdaQuery(),save(),updateById()等方法。对于复杂的多表关联查询则在Mapper接口中定义新的方法并在对应的XML文件中编写自定义SQL。享受MybatisX的编码支持在编写自定义SQL的XML文件时利用插件的跳转、补全、提示功能高效编码。通过“小鸟图标”快速在Java方法和XML标签间导航。运行与调试得益于MP大部分简单接口无需编写SQL即可运行。利用MP的SQL日志功能在控制台查看实际执行的SQL方便调试。4.2 应对复杂场景自定义SQL与MP的融合MP虽然强大但不可能覆盖所有场景复杂的多表关联、动态SQL仍需手写。这时MP和自定义SQL可以和谐共存。例如我们需要一个查询用户及其订单总数的复杂SQL首先在UserMapper.java接口中定义方法public interface UserMapper extends BaseMapperUser { // 自定义方法 ListUserOrderStats selectUserWithOrderCount(Param(minAge) Integer minAge); }然后在UserMapper.xml中编写SQL。MybatisX的跳转功能让你从selectUserWithOrderCount方法上能一键到达这里select idselectUserWithOrderCount resultTypecom.xxx.vo.UserOrderStats SELECT u.id, u.name, u.age, COUNT(o.id) as order_count FROM user u LEFT JOIN order o ON u.id o.user_id where if testminAge ! null AND u.age #{minAge} /if /where GROUP BY u.id /select最后你依然可以在Service中混合使用MP的通用方法和这个自定义方法public ListUserOrderStats getActiveUserStats(Integer minAge) { // 1. 使用自定义的复杂查询 ListUserOrderStats stats userMapper.selectUserWithOrderCount(minAge); // 2. 同时可以使用MP的简单查询做其他事 ListUser youngUsers this.lambdaQuery().lt(User::getAge, 18).list(); return stats; }5. 常见问题、避坑指南与性能优化5.1 常见问题排查表问题现象可能原因解决方案启动报错Invalid bound statement (not found)1. Mapper接口与XML的namespace不对应。2. XML中的id与方法名不对应。3. XML文件未被扫描到mapper-locations配置错误。1. 使用MybatisX的跳转功能检查对应关系。2. 检查application.yml中mybatis-plus.mapper-locations路径是否正确是否包含了自定义XML所在目录。MP通用方法执行报错提示字段不存在1. 实体类字段名与数据库列名映射失败未开启驼峰或命名不符。2. 使用了QueryWrapper但字段名字符串写错。1. 确认map-underscore-to-camel-case: true或在字段上用TableField(db_column_name)指定。2.改用LambdaQueryWrapper利用方法引用杜绝拼写错误。分页查询不生效返回了所有数据分页插件PaginationInnerInterceptor没有配置或未添加到拦截器链。确保在配置类中正确添加了分页插件见2.4节代码。插入数据时主键ID为null主键生成策略配置错误。数据库自增但实体类TableId类型设为ASSIGN_ID雪花算法。根据数据库实际情况设置id-type。自增数据库用AUTO或实体类注解用TableId(type IdType.AUTO)。MybatisX插件的小鸟图标不显示1. 插件未安装或未启用。2. 项目结构未被正确识别为Maven/Gradle项目。3. Mapper接口或XML文件格式不符合插件识别规范。1. 检查Plugins设置。2. 尝试重新导入项目File - Reload Project from Disk。3. 确保接口和XML文件的基本结构正确。5.2 高级特性与性能考量逻辑删除MP支持优雅的逻辑删除。在表中增加一个deleted字段默认0未删除1已删除在实体类字段上加TableLogic注解。之后调用remove或delete方法MP会自动将其改为更新deleted字段。查询时也会自动加上deleted0的条件。这能很好地保护数据。TableLogic private Integer deleted;在配置中可全局指定逻辑删除的字段名和值mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0字段自动填充对于create_time,update_time这种每次操作都需要设置的字段可以用MP的元对象处理器。Component public class MyMetaObjectHandler implements MetaObjectHandler { Override public void insertFill(MetaObject metaObject) { this.strictInsertFill(metaObject, createTime, LocalDateTime.class, LocalDateTime.now()); this.strictInsertFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } Override public void updateFill(MetaObject metaObject) { this.strictUpdateFill(metaObject, updateTime, LocalDateTime.class, LocalDateTime.now()); } }在实体类字段上添加注解TableField(fill FieldFill.INSERT) private LocalDateTime createTime; TableField(fill FieldFill.INSERT_UPDATE) private LocalDateTime updateTime;性能注意MP的selectList(new QueryWrapper())会查询所有字段。对于宽表字段非常多如果只需要其中几列务必使用wrapper.select(“col1”, “col2”)来指定字段避免不必要的网络传输和内存消耗。对于超大规模数据的分页深度翻页limit 1000000, 10性能极差需要考虑其他方案如基于游标的分页或业务上避免深度翻页。5.3 我踩过的坑与最佳实践实体类字段与数据库字段的映射是首要大事务必在项目初期统一命名规范如驼峰vs下划线并正确配置map-underscore-to-camel-case。每个实体类生成后花1分钟核对字段类型和名称能避免后续80%的映射错误。LambdaQueryWrapper是王道无脑用LambdaQueryWrapper代替QueryWrapper。虽然多写几个字母但它带来的类型安全和重构友好性是字符串无法比拟的这是防止低级错误和提升代码健壮性的关键一步。代码生成不是一劳永逸MybatisX生成的是“骨架”。对于复杂的业务表生成后需要手动调整添加必要的枚举类型、数据字典转换逻辑、字段的默认值或校验注解如NotNull。把生成的代码当作一个高级起点而不是最终成品。XML文件的管理即使大量使用MP通用方法也难免有复杂SQL需要写XML。建议将XML文件按模块或实体分类存放并在mapper-locations中使用通配符如classpath*:/mapper/**/*.xml确保都能被扫描到。MybatisX的跳转功能让管理这些文件不再痛苦。团队统一在团队内推广这套组合拳并统一代码生成模板、MP和MybatisX的配置版本。这能极大减少沟通成本让代码风格保持一致新人上手也更快速。可以考虑将一份优化过的代码生成配置包含公司统一的Lombok、Swagger、MP注解选项保存下来作为团队标准。
返回列表