第一次接触 MyBatis-Plus 是在三年前的一个紧急项目里,当时需要在两周内完成一个包含 20 张表 CRUD 操作的后台系统。当我看到同事只用几行代码就实现了原本需要大量重复工作的功能时,这个"MyBatis 增强工具"立刻引起了我的注意。经过这几年的实战应用,我可以负责任地说:MyBatis-Plus 是 Java 后端开发者的效率倍增器。
简单来说,MyBatis-Plus(简称 MP)是一个 MyBatis 的增强工具,在保留 MyBatis 所有特性的基础上,提供了大量开箱即用的功能。最直观的感受是——它让单表操作变得极其简单,通常只需要定义好实体类,甚至不需要写任何 XML 或接口方法就能完成常规的增删改查。根据我的经验统计,使用 MP 后,基础数据操作代码量平均减少 60% 以上。
在新建 Spring Boot 项目时,我推荐直接使用官方提供的 starter:
xml复制<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
这里有个重要细节:不要同时引入 mybatis 和 mybatis-plus 的依赖,这会导致类冲突。我曾在项目中遇到过因为同时引入 mybatis-spring-boot-starter 导致的启动报错,排查了半天才发现是依赖冲突。
在 application.yml 中建议配置以下内容:
yaml复制mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 开启SQL日志
global-config:
db-config:
id-type: auto # 主键自增策略
logic-delete-field: deleted # 逻辑删除字段
logic-not-delete-value: 0
logic-delete-value: 1
提示:生产环境记得关闭 SQL 日志或改用文件日志,避免控制台输出影响性能
定义一个用户实体类:
java复制@Data
@TableName("sys_user") // 指定表名
public class User {
@TableId(type = IdType.AUTO) // 主键策略
private Long id;
@TableField("username") // 字段映射
private String name;
private Integer age;
@TableField(fill = FieldFill.INSERT) // 自动填充
private LocalDateTime createTime;
@TableLogic // 逻辑删除标记
private Integer deleted;
}
对应的 Mapper 接口只需要简单继承:
java复制public interface UserMapper extends BaseMapper<User> {
// 无需任何方法即可获得完整CRUD能力
}
查询操作:
java复制// 按ID查询
User user = userMapper.selectById(1L);
// 条件构造器查询
List<User> users = userMapper.selectList(
new QueryWrapper<User>()
.lambda()
.ge(User::getAge, 18) // age >= 18
.like(User::getName, "张") // name like '%张%'
.orderByDesc(User::getCreateTime)
);
更新操作:
java复制// 按ID更新
userMapper.updateById(new User().setId(1L).setAge(25));
// 条件更新
userMapper.update(
new User().setAge(30),
new UpdateWrapper<User>()
.lambda()
.lt(User::getAge, 18) // age < 18
);
实现 MetaObjectHandler 来处理自动填充:
java复制@Component
public class MyMetaObjectHandler implements 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());
}
}
添加分页插件配置类:
java复制@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
使用分页查询:
java复制Page<User> page = new Page<>(1, 10); // 第1页,每页10条
Page<User> result = userMapper.selectPage(page,
new QueryWrapper<User>().orderByDesc("create_time"));
问题1:字段值为null不更新
java复制// 方案1:全局配置
mybatis-plus.global-config.db-config.update-strategy=not_null
// 方案2:字段注解
@TableField(updateStrategy = FieldStrategy.IGNORED)
private String remark;
问题2:逻辑删除不生效
通过 TenantLineInnerInterceptor 实现:
java复制interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(
new TenantLineHandler() {
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
@Override
public Expression getTenantId() {
return new LongValue(1L); // 实际应从上下文中获取
}
@Override
public boolean ignoreTable(String tableName) {
return !"sys_user".equals(tableName); // 指定需要过滤的表
}
}
));
实现动态表名处理器:
java复制public class MyTableNameHandler implements TableNameHandler {
@Override
public String dynamicTableName(String sql, String tableName) {
return tableName + "_" + LocalDate.now().getYear();
}
}
// 配置插件
interceptor.addInnerInterceptor(new DynamicTableNameInnerInterceptor(new MyTableNameHandler()));
在实际项目中,我发现 MyBatis-Plus 最适合用于管理后台、快速原型开发等需要大量基础 CRUD 的场景。但对于复杂关联查询、存储过程调用等需求,还是应该回归原生 MyBatis 的方案。掌握好这个平衡点,就能让 MP 真正成为提升开发效率的利器。