1. 项目背景与核心价值
Spring Boot 3.3.X作为当前Java生态中最主流的应用开发框架,其与MyBatis-Plus的组合堪称企业级开发的黄金搭档。最近在重构一个老项目时,我再次验证了这套技术栈的威力——原本需要200行的JDBC模板代码,用MyBatis-Plus只需要10行注解就能搞定。这种开发效率的提升对于应对互联网时代快速迭代的需求至关重要。
MyBatis-Plus 3.5.3+版本针对Spring Boot 3.x的自动配置做了深度优化,特别是在动态数据源、多租户等企业级场景下表现出色。本文将基于最新稳定版演示如何实现二者的无缝整合,并分享我在实际项目中总结的五个关键配置技巧。
2. 环境准备与工程搭建
2.1 依赖配置要点
创建Spring Boot项目时,建议使用最新的3.3.0版本。在pom.xml中需要特别注意以下依赖项:
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <!-- 必须添加的配套依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.3.1</version> <scope>compile</scope> </dependency>重要提示:Spring Boot 3.x默认使用Jakarta EE 9+,需要确保所有相关依赖(如JDBC驱动)也兼容Jakarta命名空间。常见的坑是引入老版本的druid等连接池会导致类加载冲突。
2.2 配置文件关键参数
在application.yml中,以下配置项直接影响MyBatis-Plus的行为:
mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开发环境建议开启 map-underscore-to-camel-case: true # 必须开启的驼峰转换 global-config: db-config: logic-delete-field: deleted # 逻辑删除字段名 logic-not-delete-value: 0 logic-delete-value: 13. 核心功能实现详解
3.1 实体类与Mapper配置
实体类定义时需要特别注意Lombok注解与MyBatis-Plus注解的配合:
@Data @TableName(value = "sys_user", autoResultMap = true) public class User { @TableId(type = IdType.AUTO) private Long id; @TableField(value = "user_name", condition = SqlCondition.LIKE) private String username; @TableLogic private Integer deleted; }Mapper接口只需要继承BaseMapper即可获得CRUD能力:
@Mapper public interface UserMapper extends BaseMapper<User> { // 自定义方法示例 @Select("SELECT * FROM sys_user WHERE age > #{age}") List<User> selectByAge(@Param("age") Integer age); }3.2 分页插件最佳实践
分页配置需要特别注意高并发场景下的优化:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 分页插件(必须放在第一位) interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL){ @Override public void optimizeCount(boolean optimizeCount) { // 大数据量时关闭count优化 if(tableSize > 1000000) { super.optimizeCount(false); } } }); // 乐观锁插件 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; } }4. 高级特性实战
4.1 动态表名处理
在SAAS多租户系统中,动态表名是常见需求。通过实现TableNameHandler接口:
public class TenantTableNameHandler implements TableNameHandler { private final ThreadLocal<String> tenantId = new ThreadLocal<>(); @Override public String dynamicTableName(String sql, String tableName) { return Optional.ofNullable(tenantId.get()) .map(id -> id + "_" + tableName) .orElse(tableName); } }然后在配置类中注册:
@Bean public DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() { DynamicTableNameInnerInterceptor interceptor = new DynamicTableNameInnerInterceptor(); interceptor.setTableNameHandler(new TenantTableNameHandler()); return interceptor; }4.2 多数据源配置
结合Spring Boot的AbstractRoutingDataSource实现:
@Configuration @MapperScan(basePackages = "com.example.mapper") public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DruidDataSourceBuilder.create().build(); } @Bean public DataSource dynamicDataSource() { Map<Object, Object> targetDataSources = new HashMap<>(); targetDataSources.put("master", masterDataSource()); targetDataSources.put("slave", slaveDataSource()); return new DynamicDataSource(masterDataSource(), targetDataSources); } }5. 性能优化与问题排查
5.1 SQL注入防护
MyBatis-Plus虽然提供了Wrapper构建安全查询,但仍需注意:
// 错误示例(存在注入风险) QueryWrapper<User> wrapper = new QueryWrapper<>(); wrapper.apply("date_format(create_time,'%Y-%m-%d') = '" + date + "'"); // 正确写法 wrapper.apply("date_format(create_time,'%Y-%m-%d') = {0}", date);5.2 批量操作优化
使用ExecuteBatch批量执行器可以显著提升性能:
@Test void testBatch() { String sqlStatement = "com.example.mapper.UserMapper.updateById"; SqlSessionFactory sqlSessionFactory = ...; try (SqlSession batchSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) { UserMapper mapper = batchSession.getMapper(UserMapper.class); for (int i = 0; i < 1000; i++) { User user = new User(); // ...set properties mapper.updateById(user); } batchSession.commit(); } }6. 常见问题解决方案
6.1 类型处理器问题
处理JSON字段时推荐使用JacksonTypeHandler:
@TableField(typeHandler = JacksonTypeHandler.class) private Map<String, Object> attributes;需要在配置类中注册:
@Bean public MybatisPlusSqlInjector mybatisPlusSqlInjector() { return new MybatisPlusSqlInjector() { @Override public List<AbstractJsonTypeHandler<?>> getTypeHandlers() { return Arrays.asList( new JacksonTypeHandler(), new FastjsonTypeHandler() ); } }; }6.2 事务失效场景
多数据源环境下需要明确指定事务管理器:
@Transactional(transactionManager = "masterTransactionManager") public void businessMethod() { // 操作master数据源 masterMapper.insert(...); // 操作slave数据源 slaveMapper.select(...); }在项目实际落地过程中,我发现合理使用MyBatis-Plus的Lambda表达式可以大幅提升代码可读性。特别是在复杂查询场景下,相比原生MyBatis的XML配置方式,LambdaQueryWrapper能让代码逻辑更集中、更类型安全。比如处理多表关联查询时,可以这样构建查询条件:
List<User> users = userMapper.selectList(new LambdaQueryWrapper<User>() .eq(User::getStatus, 1) .inSql(User::getDeptId, "SELECT id FROM sys_dept WHERE status = 1") .orderByDesc(User::getCreateTime) .last("LIMIT 10"));这种链式调用的写法不仅直观,还能利用IDE的代码提示和编译时类型检查,有效减少运行时错误。对于需要频繁变动的查询条件,建议封装成Builder模式,进一步提高代码的可维护性。