在 Spring Boot 项目中引入 MyBatis-Plus 之后,最直接的收益就是单表 CRUD 不再需要手写 SQL 和 Mapper 接口。但很多团队并没有用好它自带的代码生成器,仍然手动创建实体类、Service 和 Controller,导致开发效率提升有限。本文从实际项目初始化场景出发,先演示如何通过 AutoGenerator 快速产出符合规范的四层代码,然后深入分析 BaseMapper 和 IService 的封装逻辑,最后给出一套包含逻辑删除、自动填充和分页插件的完整配置,避免在后期业务膨胀时踩坑。

一、代码生成器的核心配置与启动流程
MyBatis-Plus 的代码生成器从 3.x 版本开始使用独立的 mybatis-plus-generator 模块,并支持 FreeMarker 和 Velocity 两种模板引擎。在 pom.xml 中需要同时引入 mybatis-plus-boot-starter 和 mybatis-plus-generator,再根据模板引擎选择 freemarker 或 velocity 依赖。实际开发中 FreeMarker 更常用,因为它的语法严格、模板可读性好。注意 generator 版本要与 starter 版本保持一致,否则会出现类找不到或方法签名不兼容的问题。
生成器的入口类通常单独放在 test 目录或一个独立的 module 中,避免打包进生产包。核心配置对象是 AutoGenerator,它负责组合数据源、全局策略、包名策略、模板策略和注入配置。下面是一段可直接运行的生成器代码,假设数据库中存在 sys_user 表,输出目录为 D:\work\demo\src\main\java:
public class CodeGenerator {
public static void main(String[] args) {
// 数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL);
dataSourceConfig.setUrl("jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai");
dataSourceConfig.setUsername("root");
dataSourceConfig.setPassword("123456");
// 全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setOutputDir("D:\\work\\demo\\src\\main\\java");
globalConfig.setAuthor("admin");
globalConfig.setOpen(false);
globalConfig.setFileOverride(true);
globalConfig.setServiceName("%sService");
globalConfig.setIdType(IdType.AUTO);
// 包名配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.example.demo");
packageConfig.setEntity("entity");
packageConfig.setMapper("mapper");
packageConfig.setService("service");
packageConfig.setServiceImpl("service.impl");
packageConfig.setController("controller");
// 策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setInclude("sys_user");
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
strategyConfig.setEntityLombokModel(true);
strategyConfig.setRestControllerStyle(true);
strategyConfig.setLogicDeleteFieldName("deleted");
// 执行生成
AutoGenerator generator = new AutoGenerator();
generator.setDataSource(dataSourceConfig);
generator.setGlobalConfig(globalConfig);
generator.setPackageInfo(packageConfig);
generator.setStrategy(strategyConfig);
generator.setTemplateEngine(new FreemarkerTemplateEngine());
generator.execute();
}
}
运行该 main 方法后,生成器会扫描 sys_user 表结构,自动创建 SysUser 实体类、SysUserMapper 接口、SysUserService 接口和实现类,以及一个 REST 风格的 SysUserController。默认模板生成的方法已经覆盖了根据 ID 查询、插入、更新、删除和分页查询等常见操作,可以直接用于后台管理系统的基础维护接口。
如果团队希望对生成代码有更细粒度的控制,可以通过 InjectionConfig 注入自定义属性,或者替换 /templates/mapper.xml 等默认模板。修改模板后,生成的 Mapper XML 会带上自定义的 ResultMap 或公共 where 条件,减少后续手改的工作量。
二、CRUD 封装的底层设计与常用操作
MyBatis-Plus 的 CRUD 封装核心是两个接口:BaseMapper<T> 和 IService<T>。BaseMapper 位于 mapper 层,提供 insert、deleteById、updateById、selectById、selectList 等约 30 个方法,开发者只需让自己的 Mapper 接口继承它就能获得全部能力,无需编写 XML 文件。IService 则是对 BaseMapper 的进一步封装,增加了批量插入、批量更新、链式查询和事务性更强的 saveBatch、updateBatchById 等方法,配合 ServiceImpl 基类可以减少 Controller 中的代码量。
条件构造器是 CRUD 封装中最常用的组件。Wrapper 抽象类提供了 QueryWrapper、UpdateWrapper 和 LambdaQueryWrapper、LambdaUpdateWrapper 四种实现。其中 Lambda 版本通过方法引用传递字段名,避免了字符串硬编码,重构时更安全。下面的代码展示了如何用 LambdaQueryWrapper 完成一个多条件组合查询:
@Service
public class UserQueryService {
private final SysUserMapper userMapper;
public UserQueryService(SysUserMapper userMapper) {
this.userMapper = userMapper;
}
public List<SysUser> findActiveAdmins(String keyword) {
LambdaQueryWrapper<SysUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUser::getDeleted, 0)
.like(StringUtils.hasText(keyword), SysUser::getUsername, keyword)
.ge(SysUser::getCreateTime, LocalDateTime.now().minusDays(30))
.orderByDesc(SysUser::getCreateTime);
return userMapper.selectList(wrapper);
}
}
通过 LambdaQueryWrapper 的链式调用,条件之间默认是 AND 关系,如果需要 OR 可以调用 or() 或 and(Consumer) 包裹子条件。这种方式比传统 MyBatis 的动态 SQL 更直观,而且类型安全。但要注意,条件构造器只适用于单表查询,复杂的多表关联还是需要自定义 SQL 或 XML。
分页是后台系统的标配。MyBatis-Plus 提供了 PaginationInnerInterceptor 插件,配置方式非常简单,只需要在 MyBatis 的拦截器链中注册即可:
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor pagination = new PaginationInnerInterceptor(DbType.MYSQL);
pagination.setMaxLimit(500L);
interceptor.addInnerInterceptor(pagination);
return interceptor;
}
}
之后调用 Page<SysUser> page = new Page<>(1, 10); 并传入 userMapper.selectPage(page, wrapper),就能获得包含总记录数和当前页数据的 Page 对象。分页插件底层会自动生成 LIMIT 子句,并在同一连接中执行 count 查询,不会额外增加连接开销。
三、实战中的扩展配置与避坑指南
逻辑删除是很多业务系统的必备功能。MyBatis-Plus 支持通过注解 @TableLogic 标记字段,配置后所有 selectById、selectList 等查询会自动拼上 deleted = 0 条件,删除操作则转换为 update set deleted = 1 where id = ?。需要注意逻辑删除字段不要设置数据库默认值,且不要与其他表做外键关联,否则容易产生脏数据。自动填充功能则通过 MetaObjectHandler 实现,例如创建时间和更新时间字段可以在插入或更新时自动赋值,减少手动 set 代码。
代码生成器默认生成的实体类如果开启了 Lombok,会使用 @Data、@EqualsAndHashCode(callSuper = false) 等注解。团队应统一规定是否使用 Lombok,以及是否在实体类中继承公共父类。StrategyConfig 中的 setSuperEntityClass 可以指定一个包含 id、createTime、updateTime 的基类,这样生成出的每个实体都不需要重复这些字段。另外,数据库中的 tinyint(1) 类型默认映射为 Java 的 Boolean,如果期望生成 Integer,需要在 TypeConvert 中自定义映射规则。
一个常见的坑是生成器与模板引擎版本不匹配。例如 mybatis-plus-generator 3.5.1 使用 freemarker 2.3.31 时会出现 Configuration 类找不到的异常,解决办法是显式声明 freemarker 依赖版本或改用 velocity。另一个坑是生成器默认会覆盖同名文件,如果开发者在生成后手动修改了 Service 或 Controller,再次运行会丢失修改。建议将 FileOverride 设置为 false,并且在正式项目中将生成器作为一次性工具使用,后续代码通过版本管理工具追踪变更。
总之,代码生成器和 CRUD 封装是 MyBatis-Plus 提升开发效率的两大支柱。合理配置生成器可以让团队在几分钟内得到统一规范的数据访问层,而深入理解 BaseMapper 和条件构造器则能避免过度依赖 XML 和手写 SQL。结合逻辑删除、自动填充和分页插件,单表场景下的开发成本会大幅下降,开发者可以把更多时间投入到复杂业务和性能优化上。
MyBatis-PlusSpring Boot代码生成器修改时间:2026-10-01 15:38:18