1. 为什么选择MyBatis-Plus与Spring Boot组合
在Java企业级开发领域,持久层框架的选择往往决定了项目的开发效率和维护成本。MyBatis-Plus作为MyBatis的增强工具,与Spring Boot的自动配置特性结合后,能够显著降低数据库操作的复杂度。我经历过从原生JDBC到Hibernate再到MyBatis的技术演进,最终在2018年的大型电商项目中全面采用MyBatis-Plus,单就CRUD操作而言,开发效率提升了约40%。
MyBatis-Plus的核心优势在于其"只做增强不做改变"的设计理念。它保留了MyBatis的所有特性,同时通过内置通用Mapper和Service,减少了约70%的样板代码。特别是在处理多表关联查询时,其Wrapper条件构造器比传统XML配置方式更直观。去年在为某金融机构重构系统时,我们通过Lambda表达式构建动态查询条件,使复杂查询的代码量减少了50%以上。
Spring Boot的自动配置机制与MyBatis-Plus的默契配合体现在几个关键点:
- 自动识别数据源配置
- 智能注册MyBatis组件
- 无缝集成事务管理
- 简化分页插件配置
这种组合特别适合需要快速迭代的中大型项目。我曾用两周时间完成了一个传统需要一个月开发的供应链管理系统原型,其中MyBatis-Plus的ActiveRecord模式对快速验证业务逻辑起到了关键作用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 项目初始化与环境准备
2.1 创建Spring Boot项目
建议使用Spring Initializr(start.spring.io)生成项目骨架。关键依赖选择:
- Spring Web(可选,根据项目需求)
- Lombok(强烈推荐,减少样板代码)
- MySQL Driver(或其他数据库驱动)
- MyBatis-Plus Starter
对于Maven项目,pom.xml中需要显式添加的依赖:
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>
注意:MyBatis-Plus版本需要与Spring Boot版本匹配。Spring Boot 2.7.x建议使用3.5.x系列,而Spring Boot 3.x需要MyBatis-Plus 3.5.3+
2.2 数据库配置
application.yml中的典型配置:
yaml复制spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: 123456
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启SQL日志
global-config:
db-config:
id-type: auto # 主键策略
logic-delete-field: deleted # 逻辑删除字段
logic-delete-value: 1 # 删除值
logic-not-delete-value: 0 # 未删除值
在实际项目中,我推荐使用Druid连接池而非默认的HikariCP,特别是在高并发场景下。Druid的监控功能对性能调优非常有帮助:
xml复制<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.16</version>
</dependency>
对应的配置调整:
yaml复制spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
initial-size: 5
min-idle: 5
max-active: 20
test-on-borrow: true
validation-query: SELECT 1
3. 核心配置详解
3.1 MyBatis-Plus配置类
创建MybatisPlusConfig.java配置类:
java复制@Configuration
@MapperScan("com.example.mapper")
public class MybatisPlusConfig {
/**
* 分页插件配置
* 实测在百万级数据分页时性能优化明显
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
/**
* 元对象字段填充控制器
* 自动填充createTime/updateTime等字段
*/
@Bean
public MetaObjectHandler metaObjectHandler() {
return new MetaObjectHandler() {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
};
}
}
在电商项目中,我们通过自定义SqlInjector实现了逻辑删除与唯一键冲突的自动处理:
java复制@Bean
public MyLogicSqlInjector myLogicSqlInjector() {
return new MyLogicSqlInjector();
}
public class MyLogicSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new InsertBatchSomeColumn());
methodList.add(new MysqlInsertOrUpdate());
return methodList;
}
}
3.2 实体类与Mapper配置
基础实体类示例:
java复制@Data
@TableName("sys_user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String username;
@TableField(condition = SqlCondition.LIKE)
private String nickname;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@Version
private Integer version;
@TableLogic
private Integer deleted;
}
Mapper接口只需继承BaseMapper即可获得CRUD能力:
java复制public interface UserMapper extends BaseMapper<User> {
// 自定义SQL示例
@Select("SELECT * FROM sys_user WHERE username = #{username}")
User selectByUsername(@Param("username") String username);
// 注解方式动态SQL
@SelectProvider(type = UserSqlProvider.class, method = "selectByCondition")
List<User> selectByCondition(@Param("condition") Map<String, Object> condition);
}
在金融项目中,我们通过TypeHandler实现了敏感字段的自动加解密:
java复制@TableField(typeHandler = EncryptTypeHandler.class)
private String idCard;
4. 高级特性与实战技巧
4.1 条件构造器的深度使用
MyBatis-Plus的Wrapper体系是它的核心优势。Lambda表达式方式更安全:
java复制// 复杂查询示例
List<User> users = userMapper.selectList(new LambdaQueryWrapper<User>()
.eq(User::getDeleted, 0)
.and(wrapper -> wrapper
.like(User::getUsername, "admin")
.or()
.like(User::getNickname, "管理员")
)
.between(User::getCreateTime, startDate, endDate)
.orderByDesc(User::getCreateTime));
在物流系统中,我们通过动态条件构造实现了运单的多维度筛选:
java复制public Page<Waybill> queryWaybill(WaybillQuery query, Page<Waybill> page) {
return waybillMapper.selectPage(page, new LambdaQueryWrapper<Waybill>()
.eq(query.getStatus() != null, Waybill::getStatus, query.getStatus())
.eq(query.getCustomerId() != null, Waybill::getCustomerId, query.getCustomerId())
.between(query.getStartTime() != null && query.getEndTime() != null,
Waybill::getCreateTime,
query.getStartTime(),
query.getEndTime())
.like(StringUtils.isNotBlank(query.getWaybillNo()),
Waybill::getWaybillNo,
query.getWaybillNo()));
}
4.2 批量操作性能优化
MyBatis-Plus 3.5.0+版本提供了增强的批量操作方法:
java复制// 批量插入(MySQL的rewriteBatchedStatements=true时性能最佳)
List<User> userList = new ArrayList<>();
// 填充数据...
userService.saveBatch(userList, 1000); // 每批1000条
// 批量更新(根据ID)
userService.updateBatchById(userList);
在数据迁移项目中,我们通过自定义批量插入实现了每秒2万条以上的写入速度:
java复制@Transactional(rollbackFor = Exception.class)
public void batchInsert(List<Log> logs) {
SqlSessionFactory sqlSessionFactory =
SpringUtil.getBean(SqlSessionFactory.class);
try (SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
LogMapper mapper = sqlSession.getMapper(LogMapper.class);
for (int i = 0; i < logs.size(); i++) {
mapper.insert(logs.get(i));
if (i % 1000 == 0 || i == logs.size() - 1) {
sqlSession.flushStatements();
}
}
}
}
4.3 多租户实现方案
基于MyBatis-Plus的多租户插件:
java复制@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 多租户插件
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(new TenantLineHandler() {
@Override
public Expression getTenantId() {
return new StringValue(TenantContext.getCurrentTenant());
}
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
@Override
public boolean ignoreTable(String tableName) {
return !Arrays.asList("user", "order").contains(tableName);
}
}));
return interceptor;
}
在SAAS项目中,我们结合ThreadLocal实现了租户ID的自动传递:
java复制public class TenantContext {
private static final ThreadLocal<String> CURRENT_TENANT = new ThreadLocal<>();
public static void setCurrentTenant(String tenantId) {
CURRENT_TENANT.set(tenantId);
}
public static String getCurrentTenant() {
return CURRENT_TENANT.get();
}
public static void clear() {
CURRENT_TENANT.remove();
}
}
5. 常见问题排查与性能调优
5.1 SQL性能监控
启用MyBatis-Plus的SQL分析插件:
java复制@Bean
@Profile({"dev", "test"}) // 只在开发测试环境启用
public PerformanceInterceptor performanceInterceptor() {
PerformanceInterceptor interceptor = new PerformanceInterceptor();
interceptor.setMaxTime(1000); // SQL执行最大时长(ms)
interceptor.setFormat(true); // 格式化SQL
return interceptor;
}
结合Druid的监控功能,可以获取更全面的SQL性能数据:
yaml复制spring:
datasource:
druid:
filter:
stat:
enabled: true
slow-sql-millis: 1000
log-slow-sql: true
web-stat-filter:
enabled: true
stat-view-servlet:
enabled: true
url-pattern: /druid/*
5.2 典型问题解决方案
问题1:分页查询结果不正确
解决方案:
- 检查是否配置了分页插件
- 确保没有多个MybatisPlusInterceptor实例
- 分页参数必须在第一个Interceptor位置
问题2:自动填充字段不生效
排查步骤:
- 检查字段是否有@TableField(fill = FieldFill.INSERT/UPDATE)
- 确认MetaObjectHandler已正确配置为Spring Bean
- 确保没有手动设置这些字段值
问题3:乐观锁更新失败
最佳实践:
java复制User user = userService.getById(id);
user.setName(newName);
boolean success = userService.updateById(user);
if (!success) {
// 重试或提示
user = userService.getById(id);
user.setName(newName);
success = userService.updateById(user);
}
5.3 生产环境建议配置
- 关闭mybatis-plus.configuration.log-impl或设置为文件输出
- 添加二级缓存配置(需考虑分布式环境一致性)
- 配置SQL注入器白名单
- 启用Druid的防火墙功能
完整生产配置示例:
yaml复制mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
type-aliases-package: com.example.entity
configuration:
cache-enabled: true
default-statement-timeout: 30
map-underscore-to-camel-case: true
global-config:
banner: false
db-config:
id-type: auto
logic-delete-field: deleted
logic-delete-value: 1
logic-not-delete-value: 0
spring:
datasource:
druid:
filter:
wall:
config:
multi-statement-allow: true
connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
在大型电商系统的高峰期,这些配置帮助我们维持了数据库的稳定运行,平均响应时间控制在200ms以内。
