ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

IDEA集成Mybatis-Plus:从配置到实战,提升Java开发效率

2026/8/15 21:52:40 拓冰建站 浏览量
IDEA集成Mybatis-Plus:从配置到实战,提升Java开发效率

1. 项目概述:为什么Mybatis-Plus是IDEA开发者的效率倍增器

如果你是一个用IntelliJ IDEA做Java后端开发的程序员,并且还在手写Mybatis的XML映射文件,或者为每一个实体类重复编写基础的增删改查方法,那今天这个内容可能会彻底改变你的工作流。Mybatis-Plus(简称MP)不是一个新概念,但很多开发者,尤其是刚从学校出来或者在小团队里单打独斗的朋友,对它的认知可能还停留在“一个Mybatis的增强工具”上,配置起来总觉得有点麻烦,不如直接用原生的Mybatis来得“踏实”。但我想说,这种“踏实”背后,是大量重复、低效且容易出错的体力劳动。

我经历过那个阶段,一个简单的用户管理模块,UserMapper.xml文件里光是基础的CRUD SQL就占了几十行,每次加个字段,改个查询条件,都得小心翼翼地同步好几个地方。直到后来在一个项目里被强制要求使用Mybatis-Plus,上手配置完后,那种“真香”的感觉至今难忘。它本质上是一个对Mybatis的“无侵入”增强,你原有的Mybatis代码可以完全保留,它只是在上面套了一层非常便捷的“糖衣”。核心价值就两点:第一,通过少量配置,自动生成并注入通用CRUD方法,让你几乎不用写任何SQL就能完成单表操作;第二,提供了强大的条件构造器(Wrapper),让你用Java Lambda表达式就能安全、灵活地构建复杂查询,彻底告别在XML里拼接WHERE 1=1<if test>标签的繁琐与风险。

在IDEA这个以智能和高效著称的IDE里配置Mybatis-Plus,更像是一次强强联合。IDEA强大的项目管理和自动提示能力,能让MP的代码生成和条件构造变得行云流水。接下来,我不会只给你一个干巴巴的配置步骤列表,而是会结合我这些年趟过的坑,带你从零开始,在IDEA里搭建一个既能享受MP的便捷,又能保持架构清晰、易于维护的Spring Boot项目。我们会涵盖从项目创建、依赖引入、代码生成器深度定制,到实际业务中高频使用的查询、分页、乐观锁等场景,最后还会聊聊那些官方文档里不会写的,关于性能监控和团队协作的实战经验。

2. 环境准备与项目骨架搭建:超越“New Project”的细节

很多人觉得在IDEA里创建Spring Boot项目就是点几下“Next”的事,但针对Mybatis-Plus的集成,有一些细节从项目诞生之初就值得关注,这能避免后续很多莫名其妙的依赖冲突和配置问题。

2.1 利用Spring Initializr创建项目时的关键选择

打开IDEA,选择File -> New -> Project,左侧选择Spring Initializr。这里第一个坑是服务URL,默认的https://start.spring.io在国内访问有时不稳定,导致依赖列表加载慢或失败。你可以将其替换为阿里云的镜像地址https://start.aliyun.com,速度会快很多,并且它提供的依赖版本通常也更适合国内环境。

在依赖选择页面,除了必选的Spring WebSpring Boot DevTools(热部署,开发必备),我们需要重点关注数据库和Mybatis-Plus相关的依赖:

  1. MySQL Driver: 根据你的数据库版本选择。注意,如果你用的是MySQL 8.0+,务必选择对应版本,这会影响到连接驱动类和后续的时区、SSL等配置。
  2. MyBatis Framework这个不要选!这是原生Mybatis的Starter。我们的主角是下一个。
  3. MyBatis-Plus: 在搜索框输入“Mybatis-Plus”,你会看到MyBatis-PlusMyBatis-Plus Generator(代码生成器)。这里我建议:首次创建时,只勾选MyBatis-Plus。原因是,代码生成器依赖是一个相对独立的工具,它的版本和配置方式我们可能需要更精细的控制,稍后通过Maven手动引入会更清晰。一股脑全勾上,容易让初学者混淆核心框架和辅助工具。

项目创建完成后,打开pom.xml文件。IDEA会自动解析依赖,但我们需要手动检查和添加一些关键依赖。下面是一个我常用的基础依赖配置(版本号请根据当时最新稳定版调整):

<dependencies> <!-- Spring Boot 基础 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <!-- 数据库相关 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.20</version> <!-- 推荐使用Druid连接池 --> </dependency> <!-- Mybatis-Plus 核心 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.6</version> <!-- 示例版本,请查最新 --> </dependency> <!-- 代码生成器(单独引入,便于管理) --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.6</version> <scope>test</scope> <!-- 建议放test范围,仅生成代码时使用 --> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.3</version> <scope>test</scope> </dependency> <!-- Lombok(强烈推荐,减少Getter/Setter等模板代码) --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 测试 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>

为什么这么选?

  • Druid连接池: Spring Boot默认使用HikariCP,它很好,但Druid提供了更强大的监控功能(SQL监控、防火墙等),对于后期排查慢SQL、分析数据库访问模式非常有帮助。这是一个来自生产环境的经验之选。
  • Lombok: Mybatis-Plus的实体类通常需要标准的Java Bean格式(私有字段、公共Getter/Setter)。手动写这些方法极其枯燥且容易出错。Lombok通过注解在编译时自动生成这些方法,让实体类代码保持极度简洁。在IDEA中需要安装Lombok插件才能正常识别注解。
  • 生成器依赖放test范围: 代码生成器通常只在开发初期或表结构变更时使用,不应该打包到生产环境中。将其作用域设为test是一种最佳实践。

2.2 配置文件application.yml的“魔鬼细节”

src/main/resources下创建application.yml(我个人更喜欢YAML格式,结构清晰)。基础的数据库和Mybatis-Plus配置如下:

spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/your_database?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai username: root password: your_password type: com.alibaba.druid.pool.DruidDataSource # 指定使用Druid druid: initial-size: 5 min-idle: 5 max-active: 20 test-on-borrow: true validation-query: SELECT 1 mybatis-plus: configuration: # 控制台打印完整带参数SQL(开发环境强烈建议开启) log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启驼峰命名自动映射。数据库字段 user_name 会自动映射到实体属性 userName map-underscore-to-camel-case: true # 配置默认执行器。REUSE是重用预处理语句,性能较好 default-executor-type: reuse global-config: db-config: # 全局主键类型。AUTO表示数据库自增, INPUT表示手动输入, ASSIGN_ID是MP的雪花算法 id-type: ASSIGN_ID # 逻辑删除字段名(非物理删除) logic-delete-field: is_deleted # 逻辑删除值(删除后该字段的值) logic-delete-value: 1 # 逻辑未删除值 logic-not-delete-value: 0 mapper-locations: classpath*:/mapper/**/*.xml # XML映射文件位置,如果你有自定义SQL

关键配置解读与避坑点:

  1. serverTimezone=Asia/Shanghai: 这是MySQL 8.0驱动必须的配置,否则可能遇到“服务器时区”错误。很多教程会写成UTC,但Asia/Shanghai对我们更直观。
  2. useSSL=false: 本地开发环境如果没有配置MySQL SSL证书,必须加上,否则连接失败。生产环境应设置为true并提供证书。
  3. log-impl: StdOutImpl: 这是开发调试的神器。开启后,控制台会打印出Mybatis-Plus执行的所有SQL及其参数。切记,在生产环境的配置文件中一定要关闭它,或者通过@Profile("dev")注解限定在开发环境使用,否则日志量巨大且暴露敏感信息。
  4. id-type: ASSIGN_ID: 我推荐使用MP内置的分布式ID生成器(雪花算法)。相比于数据库自增(AUTO),它在分库分表、数据迁移等场景下优势明显,且能避免自增ID带来的业务逻辑猜测风险。实体类中的id字段类型应为Long
  5. 逻辑删除配置: 这是一个“软删除”的最佳实践。配置后,调用mapper.deleteById()方法实际上执行的是UPDATE table SET is_deleted = 1 WHERE id = ?。所有MP的查询方法(如selectList)会自动附加条件WHERE is_deleted = 0。这需要你的数据库表中有对应的字段(如is_deletedtinyint)。

3. 代码生成器的深度定制:从“能用”到“好用”

Mybatis-Plus的代码生成器(MybatisPlusGenerator)是快速启动项目的利器,但默认配置生成的代码往往不符合项目的实际规范和架构。我们需要深度定制它。

3.1 创建独立的生成器配置类

我习惯在src/test/java下创建一个专门的包(如generator),里面放一个CodeGenerator类。这样既不影响主代码,也方便随时运行。以下是高度定制化的配置示例:

import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.OutputFile; import com.baomidou.mybatisplus.generator.config.rules.DateType; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; import java.util.Collections; public class CodeGenerator { public static void main(String[] args) { // 数据库配置 String url = "jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=Asia/Shanghai"; String username = "root"; String password = "your_password"; // 项目路径配置(请根据你的实际项目路径修改) String projectPath = System.getProperty("user.dir"); String javaOutputDir = projectPath + "/src/main/java"; String xmlOutputDir = projectPath + "/src/main/resources/mapper"; FastAutoGenerator.create(url, username, password) .globalConfig(builder -> { builder.author("YourName") // 作者名 .outputDir(javaOutputDir) // 输出Java文件目录 .dateType(DateType.ONLY_DATE) // 实体类日期类型用 java.util.Date .commentDate("yyyy-MM-dd") // 注释日期格式 .disableOpenDir() // 生成后不打开文件夹 .fileOverride(); // 覆盖已生成文件(谨慎使用,建议第一次后注释掉) }) .packageConfig(builder -> { builder.parent("com.yourcompany.yourproject") // 父包名 .moduleName("system") // 模块名,会在父包下生成 system 子包 .entity("entity") // 实体类包名 .mapper("mapper") .service("service") .serviceImpl("service.impl") .controller("controller") .pathInfo(Collections.singletonMap(OutputFile.xml, xmlOutputDir)); // XML文件输出目录 }) .strategyConfig(builder -> { builder.addInclude("user", "role", "menu") // 要生成的表名,多个用逗号隔开 .addTablePrefix("t_", "sys_") // 忽略表前缀,生成的实体类名会去掉这些前缀 .entityBuilder() .enableLombok() // 启用Lombok .enableChainModel() // 链式模型,实体类.setId(1L).setName(“张三”) .enableTableFieldAnnotation() // 开启字段注解 @TableField .logicDeleteColumnName("is_deleted") // 逻辑删除字段名(与全局配置对应) .versionColumnName("version") // 乐观锁字段名(可选) .naming(NamingStrategy.underline_to_camel) // 数据库下划线转驼峰 .columnNaming(NamingStrategy.underline_to_camel) .addSuperEntityColumns("id", "create_time", "update_time") // 通用父类字段 .superClass(BaseEntity.class) // 设置实体类父类(需自定义) .mapperBuilder() .enableMapperAnnotation() // 在Mapper接口上添加 @Mapper 注解 .serviceBuilder() .formatServiceFileName("%sService") // Service接口文件名格式 .formatServiceImplFileName("%sServiceImpl") .controllerBuilder() .enableRestStyle(); // 生成 @RestController 控制器 }) .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎 .execute(); } }

3.2 定制化核心:策略配置详解

这个配置的威力在于strategyConfig部分,它决定了生成代码的“长相”和“行为”。

  • addTablePrefix(“t_”, “sys_”): 如果你的数据库表有统一前缀(如t_user,sys_log),这个配置会让生成的实体类名自动去掉前缀,变成User,Log,更符合Java的类命名规范。
  • enableLombok(): 必须开启。生成的实体类将包含@Data,@Accessors(chain = true)等注解,无需手动编写Getter/Setter。
  • enableChainModel(): 开启链式模型,可以让你的实体类赋值操作更流畅:new User().setName(“Tom”).setAge(20)
  • logicDeleteColumnNameversionColumnName: 如果你在数据库表设计中采用了逻辑删除和乐观锁(一个version字段,每次更新+1),在这里配置后,生成的实体类对应字段会自动加上@TableLogic@Version注解,MP会自动处理相关逻辑。
  • superClass(BaseEntity.class)这是高级玩法,强烈推荐。大多数表都有一些通用字段,如id,create_time,update_time,create_by,update_by。与其在每个实体类里重复定义,不如创建一个抽象的BaseEntity父类。生成器配置指向这个父类后,生成的实体类会自动继承它,代码更加简洁、统一。你需要先手动创建这个BaseEntity类。

运行这个main方法,IDEA会在控制台看到生成日志,并在指定包下生成完整的Entity,Mapper,Service,Controller层代码。生成后,务必花几分钟检查生成的代码,特别是实体类的字段类型、注解是否与数据库设计意图一致。

4. 核心功能实战:CRUD、条件查询与分页

代码生成后,我们就拥有了对应表的Mapper接口(继承了MP的BaseMapper)和Service实现类(继承了MP的ServiceImpl)。现在来看看如何用它们高效工作。

4.1 无需XML的基础CRUD

假设我们生成了User实体和UserMapper

// 注入Mapper @Autowired private UserMapper userMapper; // 1. 插入 (返回插入成功条数) User user = new User(); user.setName(“张三”).setAge(25).setEmail(“zhangsan@example.com”); int rows = userMapper.insert(user); // 插入后,user对象的id会被自动回填(如果使用ASSIGN_ID) // 2. 根据ID删除 (物理删除,如果配置了逻辑删除则是逻辑删除) int deleteRows = userMapper.deleteById(1L); // 3. 根据ID更新 (注意:会更新所有字段,即使你只set了部分字段,其他字段会被更新为null!) User updateUser = new User(); updateUser.setId(1L).setName(“李四”); userMapper.updateById(updateUser); // 危险!会把age, email等字段更新为null // 正确的更新方式:先查询,再修改,最后更新。或者使用UpdateWrapper。 User dbUser = userMapper.selectById(1L); if (dbUser != null) { dbUser.setName(“李四”); userMapper.updateById(dbUser); }

这里有一个巨坑updateById(T entity)方法默认是全字段更新。如果你new一个对象,只设置了ID和要改的字段,其他字段会被认为是null,执行SQL时会用null去覆盖数据库里的原有值。所以,要么先查后改,要么使用UpdateWrapper进行动态更新

4.2 使用QueryWrapper构建灵活查询

这是Mybatis-Plus最强大的功能之一,让你用Java代码安全地构建动态SQL。

// 注入Service(推荐使用Service层,它封装了更多便捷方法) @Autowired private UserService userService; // 1. 基础条件查询:查询年龄大于20且姓名包含“张”的用户列表 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.gt(“age”, 20) // age > 20 .like(“name”, “张”); // name like ‘%张%’ List<User> userList = userService.list(queryWrapper); // 2. 选择特定字段,排序,最后修改时间倒序 queryWrapper.clear(); // 清空之前的条件 queryWrapper.select(“id”, “name”, “email”) // 只查询这三个字段 .orderByDesc(“update_time”); List<Map<String, Object>> maps = userService.listMaps(queryWrapper); // 返回Map列表,节省实体对象开销 // 3. 使用Lambda表达式,避免魔法值(推荐!) LambdaQueryWrapper<User> lambdaQuery = new LambdaQueryWrapper<>(); lambdaQuery.gt(User::getAge, 20) .like(User::getName, “张”) .orderByDesc(User::getCreateTime); List<User> lambdaUserList = userService.list(lambdaQuery);

LambdaQueryWrapper的优势:它通过方法引用(User::getName)来指定字段,是类型安全的。如果你在User实体中重命名了name属性,编译器会直接报错,而字符串形式的“name”则会在运行时才可能出错。

4.3 分页查询的正确姿势

Mybatis-Plus的分页需要一点额外配置。首先,定义一个配置类,注册分页插件:

@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 添加分页插件 PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); paginationInnerInterceptor.setMaxLimit(1000L); // 设置单页最大记录数 paginationInnerInterceptor.setOverflow(true); // 超过最大页数后,是否返回第一页数据 interceptor.addInnerInterceptor(paginationInnerInterceptor); // 还可以添加其他插件,如乐观锁插件 // interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }

然后,在Service中使用就非常简单了:

// 分页查询:查询第2页,每页10条,条件为年龄大于18 Page<User> page = new Page<>(2, 10); QueryWrapper<User> wrapper = new QueryWrapper<User>().gt(“age”, 18); Page<User> resultPage = userService.page(page, wrapper); System.out.println(“总记录数:” + resultPage.getTotal()); System.out.println(“总页数:” + resultPage.getPages()); System.out.println(“当前页数据:” + resultPage.getRecords());

注意:MP的分页插件原理是拦截Executor,在执行查询SQL前自动计算并拼接LIMIT语句,同时执行一条COUNT(*)查询。这意味着,如果你的查询本身非常复杂或者关联了多张表,这个COUNT查询可能会成为性能瓶颈。对于极端复杂的统计分页,有时需要手写SQL并优化。

5. 高级特性与生产环境考量

当基础功能玩转后,就需要关注一些能提升项目健壮性和开发效率的高级特性。

5.1 乐观锁:解决并发更新冲突

在高并发场景下,同时更新同一条记录可能导致数据覆盖。乐观锁通过一个version字段来解决。配置步骤如下:

  1. 在数据库表中增加一个version字段(整数类型)。
  2. 在实体类的对应字段上添加@Version注解。
  3. 在之前的MybatisPlusConfig配置类中,添加乐观锁插件(见上面配置类的注释部分)。 配置好后,更新操作会变成这样:UPDATE user SET name=?, version=version+1 WHERE id=? AND version=?。如果更新时发现version值与数据库中不一致(说明期间被其他线程修改过),更新行数会为0,你可以在业务逻辑中据此判断并重试或抛出异常。

5.2 自动填充:优雅处理创建/更新时间

我们通常需要记录数据的创建时间和最后更新时间。Mybatis-Plus的MetaObjectHandler接口可以自动填充这些字段。

  1. 在实体类字段上添加注解:
    @TableField(fill = FieldFill.INSERT) // 插入时填充 private LocalDateTime createTime; @TableField(fill = FieldFill.INSERT_UPDATE) // 插入和更新时填充 private LocalDateTime updateTime;
  2. 创建一个处理器类实现MetaObjectHandler
    @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()); } }

这样,在执行insertupdate方法时,这些字段会自动被填充上当前时间,无需在业务代码中手动设置。

5.3 多数据源与读写分离

对于稍大一点的项目,数据库读写分离是常见需求。Mybatis-Plus本身不直接提供多数据源支持,但它可以很好地与第三方多数据源组件(如dynamic-datasource-spring-boot-starter)协同工作。大致步骤是:

  1. 引入dynamic-datasource依赖。
  2. 在配置文件中配置主库(master)和从库(slave)的连接信息。
  3. 使用@DS(“master”)@DS(“slave”)注解在ServiceMapper方法上指定数据源。关键点:事务管理会变得复杂。声明式事务@Transactional默认只对主库生效,如果涉及跨数据源的写操作,需要引入分布式事务解决方案(如Seata),或者从架构上避免这种情况。

5.4 性能监控与SQL分析

这是很多教程里不提,但对线上系统至关重要的部分。我们之前配置了Druid连接池,现在可以利用它的监控功能。 在application.yml中补充Druid监控配置:

spring: datasource: druid: stat-view-servlet: enabled: true # 启用StatViewServlet login-username: admin # 监控页面登录用户名 login-password: admin # 监控页面登录密码 allow: 127.0.0.1 # 允许访问的IP,生产环境务必设置 filter: stat: enabled: true # 开启SQL统计 log-slow-sql: true # 记录慢SQL slow-sql-millis: 2000 # 慢SQL阈值(2秒) wall: enabled: true # 开启SQL防火墙

启动应用后,访问http://localhost:8080/druid,输入配置的用户名密码,就可以看到一个强大的监控面板。这里可以看到数据源状态、SQL执行统计、慢SQL记录、Web请求统计等信息。定期查看慢SQL列表,是优化数据库性能的第一步。

6. 常见问题排查与团队协作规范

即使配置得当,开发中还是会遇到各种问题。这里分享几个我高频遇到的坑及其解决方案。

6.1 “Invalid bound statement (not found)”错误

这是最经典的错误,意思是Mybatis找不到对应的SQL映射。可能的原因和排查顺序:

  1. XML文件位置不对: 检查mybatis-plus.mapper-locations配置的路径是否与实际XML文件存放路径一致。IDEA有时不会自动将resources/mapper目录标记为资源目录,需要手动在Project Structure -> Modules里设置。
  2. Mapper接口未扫描: 确保你的Spring Boot主应用类上有@MapperScan(“com.yourcompany.yourproject.mapper”)注解,或者每个Mapper接口上加了@Mapper注解。
  3. 方法名冲突: 如果你在自定义的Mapper接口中定义了一个方法,又在对应的XML里定义了同名的SQL,或者继承了BaseMapper中的同名方法但意图不同,会导致冲突。检查接口方法名。
  4. IDEA缓存问题: 尝试File -> Invalidate Caches and Restart

6.2 分页插件失效,查询结果不是分页数据

如果调用page方法返回的Page对象里records包含了所有数据,total也是总条数,但SQL日志里没有LIMIT语句,说明分页插件没有生效。

  • 检查配置类是否被加载: 确保MybatisPlusConfig类上有@Configuration注解,并且位于能被主类扫描到的包路径下。
  • 检查是否有多数据源配置冲突: 如果使用了多数据源,分页插件需要在每个数据源的配置中单独注册,或者注册到全局。
  • 手动指定分页参数类型: 在极少数情况下,可能需要确保Page对象的泛型与Mapper方法返回的实体类型一致。

6.3 字段映射失败(值为null)

实体类属性名是userName,数据库字段是user_name,但查询出来userName为null。

  • 确认驼峰映射已开启: 检查配置map-underscore-to-camel-case: true
  • 检查@TableField注解: 如果属性名与字段名不完全符合驼峰规则(例如isDeleted对应is_deleted),可能需要使用@TableField(value = “is_deleted”)显式指定。
  • 检查数据库连接和查询结果: 直接在数据库客户端执行生成的SQL,看是否真的能查到该字段的数据。

6.4 团队协作规范建议

当项目有多人开发时,统一的Mybatis-Plus使用规范能减少很多沟通成本:

  1. 实体类规范: 强制使用Lombok,统一继承BaseEntity。布尔类型字段命名以is开头,对应的数据库字段去掉is_前缀(如isDeleted对应deleted,配合@TableField(“is_deleted”))。
  2. 查询规范: 强制要求使用LambdaQueryWrapperLambdaUpdateWrapper,禁止在业务代码中出现SQL字符串拼接。复杂查询(如多表关联、子查询)必须写在XML中,并附上SQL注释。
  3. Service层规范: 生成的Service接口和实现类已经提供了大部分方法。对于特别复杂的业务逻辑,应在ServiceImpl中编写,而不是自己另起炉灶。公共的查询条件可以封装成Wrapper的静态方法。
  4. 代码生成器使用: 将定制好的CodeGenerator类放入项目test目录,并写入项目Wiki。规定每次表结构变更后,必须重新生成对应代码,并仔细核对差异,避免手动修改被覆盖。

配置和使用Mybatis-Plus的过程,是一个从“手动劳动”到“声明式编程”的思维转变。初期可能会觉得配置繁琐,不如直接写SQL“痛快”。但一旦这套基础设施搭建完毕,后续的开发效率提升是线性的,代码的规范性和可维护性也会大大提高。尤其是在IDEA智能提示的加持下,通过Lambda表达式构建查询条件,那种行云流水的感觉,会让你再也回不去手写XML的时代。最后记住,任何工具都有其边界,Mybatis-Plus最适合的是单表CRUD和中等复杂的动态查询,对于极其复杂、需要高度优化的SQL,不要犹豫,直接使用XML或注解方式编写,这才是Mybatis生态灵活性的体现。