1. SpringBoot与MyBatisPlus整合实战指南
在Java后端开发领域,SpringBoot和MyBatisPlus的组合已经成为快速构建数据持久层的黄金搭档。最近我在一个学生管理系统中采用了SpringBoot 3.5.8 + MyBatisPlus 3.5.8的技术栈,整个过程既有顺利的整合体验,也遇到了一些版本兼容性的"坑"。下面我就把这次实战经验完整分享出来,特别是如何处理SpringBoot 3.x与MyBatisPlus的版本冲突问题。
2. 环境准备与项目搭建
2.1 基础环境配置
我选择的开发环境是:
- JDK 17(LTS长期支持版本)
- SpringBoot 3.5.8(目前最新的稳定版)
- MyBatisPlus 3.5.8(与SpringBoot版本保持一致)
这里特别提醒:SpringBoot 3.x系列必须使用JDK 17及以上版本,这是Spring团队明确的要求。如果还在用JDK 8或11,要么降级到SpringBoot 2.7.x,要么升级JDK。
2.2 Maven依赖配置
pom.xml的关键配置如下:
xml复制<dependencies>
<!-- SpringBoot Web基础依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok简化代码 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- MyBatisPlus核心依赖(排除冲突的mybatis-spring) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.8</version>
<exclusions>
<exclusion>
<artifactId>mybatis-spring</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 显式指定mybatis-spring版本 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.5</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
重要提示:MyBatisPlus 3.5.8默认依赖的mybatis-spring版本(2.x)与SpringBoot 3.x不兼容,必须排除后显式指定3.0.x版本,否则会报"Invalid value type for attribute 'factoryBeanObjectType'"错误。
3. 核心配置详解
3.1 数据库连接配置
application.yml中的关键配置:
yaml复制spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/studb?useSSL=false&serverTimezone=UTC
username: root
password: root
mybatis-plus:
type-aliases-package: com.wdh.po # 实体类所在包
mapper-locations: classpath:/mapper/**/*.xml # XML映射文件位置
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 控制台SQL日志
map-underscore-to-camel-case: true # 自动驼峰转换
建议添加连接池配置(如HikariCP):
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 10
minimum-idle: 5
connection-timeout: 30000
3.2 MyBatisPlus配置类
创建配置类启用分页插件:
java复制@Configuration
@MapperScan("com.wdh.mapper") // 指定Mapper接口扫描路径
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 可添加其他插件,如乐观锁
return interceptor;
}
}
4. 业务层实现
4.1 实体类定义
使用Lombok简化代码:
java复制@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Serializable {
@TableId(type = IdType.AUTO) // 主键自增
private Long id;
private String name;
private Integer age;
@TableField(fill = FieldFill.INSERT) // 自动填充
private LocalDateTime createTime;
}
4.2 Mapper接口
继承BaseMapper获得CRUD能力:
java复制public interface StudentMapper extends BaseMapper<Student> {
// 自定义查询方法
@Select("SELECT * FROM student WHERE age > #{age}")
List<Student> selectByAge(@Param("age") Integer age);
}
4.3 Service层实现
java复制public interface StudentServiceI extends IService<Student> {
// 自定义业务方法
List<Student> findTopStudents(Integer limit);
}
@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student>
implements StudentServiceI {
@Override
public List<Student> findTopStudents(Integer limit) {
QueryWrapper<Student> wrapper = new QueryWrapper<>();
wrapper.orderByDesc("score").last("LIMIT " + limit);
return baseMapper.selectList(wrapper);
}
}
5. 测试验证
5.1 基础CRUD测试
java复制@SpringBootTest
class StudentMapperTest {
@Autowired
private StudentMapper studentMapper;
@Test
void testInsert() {
Student student = new Student(null, "张三", 20, LocalDateTime.now());
int result = studentMapper.insert(student);
assertEquals(1, result);
}
@Test
void testSelect() {
List<Student> students = studentMapper.selectList(null);
students.forEach(System.out::println);
}
}
5.2 分页查询测试
java复制@SpringBootTest
class StudentServiceImplTest {
@Autowired
private StudentServiceImpl studentService;
@Test
void testPageQuery() {
Page<Student> page = new Page<>(1, 5); // 第1页,每页5条
Page<Student> result = studentService.page(page);
System.out.println("总记录数:" + result.getTotal());
result.getRecords().forEach(System.out::println);
}
}
6. 常见问题解决方案
6.1 版本冲突问题
问题现象:
code复制Invalid value type for attribute 'factoryBeanObjectType': java.lang.String
原因分析:
SpringBoot 3.x需要mybatis-spring 3.0.x版本,而MyBatisPlus默认引入的是2.x版本
解决方案:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.8</version>
<exclusions>
<exclusion>
<artifactId>mybatis-spring</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
</exclusions>
</dependency>
<!-- 显式指定3.0.x版本 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.5</version>
</dependency>
6.2 日期时间处理
问题:Java 8的LocalDateTime在MyBatisPlus中需要特殊处理
解决方案:
- 添加jsr310依赖:
xml复制<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
- 在application.yml中配置:
yaml复制spring:
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
6.3 逻辑删除实现
- 在实体类中添加标记字段:
java复制@TableLogic
private Integer deleted; // 1-删除,0-未删除
- 全局配置:
yaml复制mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除字段
logic-delete-value: 1 # 删除值
logic-not-delete-value: 0 # 未删除值
7. 高级特性应用
7.1 自动填充功能
- 实现MetaObjectHandler接口:
java复制@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
}
- 实体类注解:
java复制@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
7.2 多数据源配置
- 添加多数据源依赖:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
- 配置多数据源:
yaml复制spring:
datasource:
dynamic:
primary: master
datasource:
master:
url: jdbc:mysql://localhost:3306/master
username: root
password: root
slave:
url: jdbc:mysql://localhost:3306/slave
username: root
password: root
- 使用注解切换数据源:
java复制@DS("slave") // 指定使用从库
public List<Student> findAllFromSlave() {
return studentMapper.selectList(null);
}
8. 性能优化建议
- 批量操作:
java复制// 批量插入
boolean success = studentService.saveBatch(studentList, 1000); // 每批1000条
// 批量更新
studentService.updateBatchById(studentList);
- SQL优化:
java复制// 只查询需要的字段
QueryWrapper<Student> wrapper = new QueryWrapper<>();
wrapper.select("id", "name").eq("age", 20);
- 二级缓存:
yaml复制mybatis-plus:
configuration:
cache-enabled: true
- 逻辑删除优化:
java复制// 不需要逻辑删除的查询
studentMapper.selectList(Wrappers.<Student>lambdaQuery()
.apply("1=1")
.notInSql("id", "select id from student where deleted=1"));
9. 最佳实践总结
- 版本管理:
- 保持SpringBoot、MyBatisPlus和mybatis-spring的版本兼容
- 使用dependencyManagement统一管理版本号
- 代码规范:
- 实体类实现Serializable接口
- 使用包装类型(Integer/Long)而非基本类型(int/long)
- 为所有Mapper方法添加@Param注解
- 安全建议:
- 生产环境关闭SQL日志(mybatis-plus.configuration.log-impl=no_op)
- 使用预处理语句防止SQL注入
- 监控方案:
- 集成P6Spy打印完整SQL:
xml复制<dependency>
<groupId>p6spy</groupId>
<artifactId>p6spy</artifactId>
<version>3.9.1</version>
</dependency>
配置application.yml:
yaml复制spring:
datasource:
driver-class-name: com.p6spy.engine.spy.P6SpyDriver
url: jdbc:p6spy:mysql://localhost:3306/studb
在实际项目中,这套技术栈已经稳定运行了半年多,处理了日均10万+的读写请求。特别是在处理复杂查询和分页需求时,MyBatisPlus提供的Wrapper和Page工具大大提高了开发效率。对于刚从MyBatis转向MyBatisPlus的团队,建议先从基础CRUD开始,逐步尝试条件构造器、分页插件等高级功能,最终实现全项目的平滑迁移。