1. MyBatis-Plus高效开发全攻略
作为Java开发者,你是否还在为重复的CRUD代码头疼?MyBatis-Plus(简称MP)这个MyBatis的增强工具,用过的都说真香。我在三个大型项目中深度使用MP后,总结出这套覆盖基础到高阶的全方位开发指南,包含动态租户隔离等最新实战技巧。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心功能解析与选型优势
2.1 为什么选择MyBatis-Plus
相比原生MyBatis,MP最直观的优势是自动生成CRUD代码。但它的价值远不止于此:
- 内置通用Mapper:BaseMapper接口提供20+常用方法
- 条件构造器:QueryWrapper/LambdaQueryWrapper实现动态SQL
- 代码生成器:可生成entity/mapper/service全套代码
- 插件体系:分页/性能分析/乐观锁等开箱即用
实际项目中,MP通常能减少50%以上的DAO层代码量。我们电商系统用户模块的接口开发时间从3天缩短到半天。
2.2 版本选择建议
当前主流版本是3.5.x系列,建议使用最新3.5.3.1版本。特别注意:
- 3.4.x版本存在SQL注入风险(CVE-2022-25517)
- 与SpringBoot版本存在兼容矩阵:
SpringBoot MP版本 2.7.x 3.5.3.1 2.5.x 3.4.3.4 2.4.x 3.3.2
3. 高效开发实战指南
3.1 代码生成器深度配置
标准的代码生成器配置大家都会,分享几个实用技巧:
java复制// 自定义模板引擎
TemplateConfig templateConfig = new TemplateConfig()
.setController("/templates/controller.java") // 自定义Controller模板
.setEntityKt("/templates/entity.kt"); // 支持Kotlin
// 字段类型映射
StrategyConfig strategy = new StrategyConfig()
.setColumnNaming(NamingStrategy.underline_to_camel)
.setEntityBooleanColumnRemoveIsPrefix(true) // 去除is前缀
.setRestControllerStyle(true);
3.2 动态SQL最佳实践
Lambda表达式写法更安全:
java复制// 避免硬编码字段名
queryWrapper.lambda()
.eq(User::getName, "老王")
.between(User::getCreateTime, startDate, endDate)
.nested(w -> w.eq(User::getStatus,1).or().eq(User::getType,2));
复杂查询推荐使用XML映射文件+MP条件构造器组合方案。
4. 高阶特性实战
4.1 动态租户隔离方案
多租户系统常需要动态切换隔离策略:
java复制// 自定义租户处理器
public class DynamicTenantHandler implements TenantLineHandler {
@Override
public String getTenantId() {
return TenantContext.getCurrentTenant(); // 动态获取
}
@Override
public boolean ignoreTable(String tableName) {
return "sys_config".equals(tableName); // 系统表不隔离
}
}
通过AOP实现方法级租户控制:
java复制@Around("@annotation(tenantSwitch)")
public Object switchTenant(ProceedingJoinPoint pjp) {
try {
String tenantId = // 解析租户ID
TenantContext.setTenant(tenantId);
return pjp.proceed();
} finally {
TenantContext.clear();
}
}
4.2 多数据源事务方案
结合dynamic-datasource实现:
yaml复制spring:
datasource:
dynamic:
primary: master
datasource:
master: # 主库配置
tenant1: # 租户1库
tenant2: # 租户2库
使用@DSTransactional注解实现跨库事务:
java复制@DSTransactional
public void crossDatabaseOperation() {
// 操作主库
masterMapper.insert(...);
// 操作租户库
tenantMapper.update(...);
}
5. 性能优化与监控
5.1 SQL执行分析
启用性能分析插件:
java复制@Bean
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor interceptor = new PerformanceInterceptor();
interceptor.setMaxTime(1000); // 超过1秒记录警告
interceptor.setFormat(true); // 格式化SQL
return interceptor;
}
配合Logback输出慢SQL日志:
xml复制<logger name="com.baomidou.mybatisplus" level="DEBUG"/>
<appender-ref ref="SQL_APPENDER"/>
5.2 二级缓存优化
Redis缓存配置示例:
java复制@Bean
public MybatisRedisCacheFactory redisCacheFactory() {
return new MybatisRedisCacheFactory(redisTemplate) {
@Override
public Cache createCache(String id) {
return new CustomRedisCache(id, redisTemplate, 30, TimeUnit.MINUTES);
}
};
}
缓存雪崩防护建议:
- 设置随机过期时间(基础时间±随机值)
- 使用@CacheEvict时配置beforeInvocation=true
- 热点数据永不过期,后台异步更新
6. 常见问题排查
6.1 字段映射异常
典型问题场景:
- 数据库字段为user_name,实体类用userName但未开启驼峰转换
- 布尔类型字段命名不规范(建议使用status而非is_deleted)
解决方案:
yaml复制mybatis-plus:
configuration:
map-underscore-to-camel-case: true
6.2 分页查询失效
必须同时满足:
- 配置分页插件
- 使用MP的Page对象作为参数
- Service层返回IPage接口
正确示例:
java复制@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
// Service实现
public IPage<User> pageUsers(Page<User> page) {
return userMapper.selectPage(page, null);
}
7. 安全防护建议
7.1 SQL注入防护
禁止直接拼接SQL:
java复制// 错误示范
queryWrapper.apply("date_format(create_time,'%Y-%m') = '"+month+"'");
// 正确做法
queryWrapper.apply("date_format(create_time,'%Y-%m') = {0}", month);
7.2 批量操作优化
使用executeBatch提升性能:
java复制sqlSessionFactory.openSession(ExecutorType.BATCH).getMapper(UserMapper.class)
.insertBatchSomeColumn(userList);
批处理建议:
- 每1000条提交一次
- 失败时记录已处理条数
- 使用并行流时注意线程安全
8. 扩展开发技巧
8.1 自定义全局操作
实现自动填充创建人:
java复制@Bean
public MetaObjectHandler metaObjectHandler() {
return new MetaObjectHandler() {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createBy", String.class, getCurrentUser());
}
};
}
8.2 逻辑删除优化
建议方案:
java复制@TableLogic(delval = "id", value = "0")
private Long deleted; // 删除时设置为记录ID
// 查询时自动过滤已删除
@SqlParser(filter = true)
public interface UserMapper extends BaseMapper<User> {}
9. 测试方案设计
9.1 单元测试技巧
内存数据库测试配置:
java复制@SpringBootTest
@Testcontainers
class UserMapperTest {
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0");
@DynamicPropertySource
static void setDatasourceProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", mysql::getJdbcUrl);
}
}
9.2 性能压测要点
重点关注指标:
- 单条插入/批量插入吞吐量
- 复杂查询响应时间P99值
- 连接池利用率监控
JMeter测试建议:
- 模拟不同租户的混合请求
- 测试分页查询的极限偏移量
- 验证二级缓存命中率
10. 升级迁移策略
从MyBatis迁移步骤:
- 保留原有XML映射文件
- 逐步替换通用CRUD为BaseMapper
- 使用MP代码生成器生成新模块
- 最终移除冗余XML文件
版本升级检查清单:
- 重命名API检查(如ActiveRecord→ServiceImpl)
- 插件初始化方式变更(3.4→3.5)
- 分页对象构造方法调整
这套方案在我们金融项目中平稳完成了从3.2.0到3.5.3的升级,零故障过渡。关键是要在测试环境充分验证边界条件,特别是多租户场景下的数据隔离表现。
