1. SpringBoot3与MyBatis-Plus 3.5.9整合实战
最近在重构一个老项目时,我决定采用SpringBoot3搭配MyBatis-Plus 3.5.9这套技术栈。这个组合在实际开发中确实能显著提升效率,但升级过程中也踩了不少坑。今天就把完整的整合过程、核心配置要点和实战经验分享给大家。
MyBatis-Plus作为MyBatis的增强工具,在3.5.9版本中提供了更完善的CRUD操作、代码生成器和各种实用插件。而SpringBoot3基于Spring6,对JDK17有更好的支持。两者结合使用时,需要注意版本兼容性和一些新特性的配置方式。
2. 环境准备与基础配置
2.1 依赖管理
首先创建SpringBoot3项目时,我推荐使用最新的Spring Initializr。关键依赖包括:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.9</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.9</version>
<scope>compile</scope>
</dependency>
注意:SpringBoot3默认使用Jakarta EE 9+,而MyBatis-Plus 3.5.9已经适配。如果遇到javax包找不到的问题,检查是否有旧版本依赖冲突。
2.2 数据源配置
在application.yml中配置数据源时,我建议采用以下格式:
yaml复制spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto
3. 核心功能实现
3.1 实体类与Mapper
使用MyBatis-Plus后,实体类只需简单注解:
java复制@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
private String password;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
}
对应的Mapper接口更加简洁:
java复制public interface UserMapper extends BaseMapper<User> {
// 自定义方法
@Select("SELECT * FROM sys_user WHERE username LIKE CONCAT('%',#{username},'%')")
List<User> selectByUsernameLike(String username);
}
3.2 分页查询实现
分页是实际项目中最常用的功能之一。配置分页插件:
java复制@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
使用时分页查询非常简单:
java复制Page<User> page = new Page<>(1, 10);
Page<User> result = userMapper.selectPage(page,
Wrappers.<User>lambdaQuery()
.like(User::getUsername, "admin")
.orderByAsc(User::getId));
4. 代码生成器高级用法
MyBatis-Plus的代码生成器能极大提升开发效率。这是我优化后的生成器配置:
java复制FastAutoGenerator.create("jdbc:mysql://localhost:3306/demo", "root", "123456")
.globalConfig(builder -> {
builder.author("yourname")
.outputDir(System.getProperty("user.dir") + "/src/main/java")
.enableSwagger()
.dateType(DateType.TIME_PACKAGE);
})
.packageConfig(builder -> {
builder.parent("com.example.demo")
.moduleName("system")
.entity("entity")
.service("service")
.serviceImpl("service.impl")
.mapper("mapper")
.xml("mapper.xml")
.controller("controller");
})
.strategyConfig(builder -> {
builder.addInclude("sys_user", "sys_role")
.entityBuilder()
.enableLombok()
.enableChainModel()
.controllerBuilder()
.enableRestStyle();
})
.templateEngine(new FreemarkerTemplateEngine())
.execute();
实战技巧:对于大型项目,建议将生成器配置单独做成一个模块,通过maven插件方式运行,避免污染主项目代码。
5. 高级特性与性能优化
5.1 多租户实现
在SaaS系统中,多租户是常见需求。MyBatis-Plus提供了优雅的实现:
java复制public class MyTenantLineHandler implements TenantLineHandler {
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
@Override
public Expression getTenantId() {
return new LongValue(1L); // 实际应从上下文中获取
}
@Override
public boolean ignoreTable(String tableName) {
return !tableName.startsWith("sys_");
}
}
然后在拦截器中添加:
java复制interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new MyTenantLineHandler()));
5.2 动态表名支持
处理分表场景时,动态表名非常有用:
java复制public class MyDynamicTableNameParser implements TableNameHandler {
@Override
public String dynamicTableName(String sql, String tableName) {
if ("sys_log".equals(tableName)) {
return tableName + "_" + LocalDate.now().getYear();
}
return tableName;
}
}
配置方式:
java复制DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();
dynamicTableNameInnerInterceptor.setTableNameHandler(new MyDynamicTableNameParser());
interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor);
6. 常见问题排查
6.1 类型处理器问题
当遇到Java8时间类型转换问题时,需要检查是否配置了正确的类型处理器:
yaml复制mybatis-plus:
configuration:
default-enum-type-handler: org.apache.ibatis.type.EnumOrdinalTypeHandler
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
6.2 批量插入性能优化
使用Service的saveBatch方法时,默认批次大小是1000,但实际测试发现500左右性能更佳:
java复制// 优化批量插入
userService.saveBatch(userList, 500);
6.3 Lambda表达式缓存问题
在复杂查询场景下,Lambda表达式可能会有性能开销。可以考虑缓存Wrapper实例:
java复制private static final LambdaQueryWrapper<User> QUERY_CACHE = Wrappers.lambdaQuery(User.class)
.select(User::getId, User::getUsername);
public List<User> getSimpleUserList() {
return userMapper.selectList(QUERY_CACHE);
}
7. 安全与监控
7.1 SQL注入防护
虽然MyBatis-Plus已经做了很多防护,但在使用@Select注解写原生SQL时仍需注意:
java复制@Select("SELECT * FROM sys_user WHERE id = #{id}")
User selectById(@Param("id") Long id); // 安全
// 危险示例(绝对不要这样写)
@Select("SELECT * FROM sys_user WHERE id = " + "${id}")
User selectByIdUnsafe(@Param("id") String id);
7.2 监控指标集成
结合SpringBoot Actuator可以监控SQL性能:
yaml复制management:
endpoints:
web:
exposure:
include: health,metrics,prometheus
metrics:
tags:
application: ${spring.application.name}
然后在代码中记录耗时:
java复制@Around("execution(* com.example.demo.mapper.*.*(..))")
public Object logSqlExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
long endTime = System.currentTimeMillis();
log.info("SQL执行耗时: {}ms - {}", (endTime - startTime),
joinPoint.getSignature().toShortString());
return result;
}
在实际项目中,我发现合理使用MyBatis-Plus的特性可以节省约40%的持久层代码量。特别是在处理复杂业务逻辑时,Lambda表达式方式的查询构建器让代码既简洁又类型安全。不过也要注意,过度依赖代码生成器可能导致项目结构僵化,建议对核心业务领域还是手写一些定制化的Mapper和SQL。
