MyBatis-Plus
MyBatis-Plus 是 MyBatis 的增强工具,在不改变 MyBatis 任何特性的基础上,提供了大量开箱即用的功能,是国内最流行的 ORM 框架。
快速开始
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.7</version>
</dependency>java
// 实体类
@Data
@TableName("t_user") // 对应数据库表名
public class User {
@TableId(type = IdType.ASSIGN_ID) // 雪花算法 ID
private Long id;
@TableField("user_name") // 字段名映射
private String name;
private String email;
private Integer age;
@TableField(fill = FieldFill.INSERT) // 自动填充
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableLogic // 逻辑删除
private Integer deleted;
@Version // 乐观锁
private Integer version;
}
// Mapper 接口(继承 BaseMapper,无需写 XML)
@Mapper
public interface UserMapper extends BaseMapper<User> {
// 自动拥有 CRUD 方法
// 复杂查询可以在这里写自定义方法
}
// Service 接口
public interface UserService extends IService<User> {
// IService 提供更多业务方法
}
// Service 实现
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
// 自动注入 baseMapper
}CRUD 操作
java
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>
implements UserService {
// ===== 新增 =====
public void createExamples() {
User user = new User();
user.setName("Alice");
user.setEmail("alice@example.com");
// 单条插入
save(user); // 或 baseMapper.insert(user)
// 批量插入(推荐,内部分批处理)
List<User> users = buildUsers();
saveBatch(users, 500); // 每批 500 条
}
// ===== 查询 =====
public void queryExamples() {
// 按 ID 查询
User user = getById(1L);
// 条件查询(LambdaQueryWrapper 推荐,类型安全)
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class)
.eq(User::getAge, 25)
.like(User::getName, "Alice")
.between(User::getCreateTime,
LocalDateTime.now().minusDays(7), LocalDateTime.now())
.orderByDesc(User::getCreateTime)
.last("LIMIT 10"); // 追加 SQL 片段
List<User> users = list(wrapper);
// 查询单条(多条结果会抛异常)
User one = getOne(Wrappers.lambdaQuery(User.class)
.eq(User::getEmail, "alice@example.com"));
// 查询数量
long count = count(Wrappers.lambdaQuery(User.class)
.eq(User::getAge, 25));
// 只查询部分字段
List<User> partial = list(Wrappers.lambdaQuery(User.class)
.select(User::getId, User::getName, User::getEmail)
.eq(User::getAge, 25));
}
// ===== 分页查询 =====
public IPage<User> pageQuery(int pageNum, int pageSize, String keyword) {
Page<User> page = new Page<>(pageNum, pageSize);
LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class)
.like(StringUtils.isNotBlank(keyword), User::getName, keyword)
.orderByDesc(User::getCreateTime);
return page(page, wrapper);
// 返回 IPage 包含:records(数据列表)、total(总数)、pages(总页数)
}
// ===== 更新 =====
public void updateExamples() {
// 按 ID 更新(null 字段不更新)
User user = new User();
user.setId(1L);
user.setName("Alice Updated");
updateById(user);
// 条件更新
update(Wrappers.lambdaUpdate(User.class)
.set(User::getAge, 26)
.set(User::getUpdateTime, LocalDateTime.now())
.eq(User::getId, 1L));
// 批量更新
List<User> users = buildUsers();
updateBatchById(users);
}
// ===== 删除 =====
public void deleteExamples() {
// 按 ID 删除(有 @TableLogic 则逻辑删除)
removeById(1L);
// 条件删除
remove(Wrappers.lambdaQuery(User.class)
.eq(User::getAge, 0));
// 批量删除
removeByIds(List.of(1L, 2L, 3L));
}
}条件构造器
java
// QueryWrapper(字符串字段名,不推荐)
QueryWrapper<User> qw = new QueryWrapper<>();
qw.eq("age", 25).like("name", "Alice");
// LambdaQueryWrapper(推荐,类型安全,重构友好)
LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
lqw.eq(User::getAge, 25)
.like(User::getName, "Alice")
.in(User::getId, List.of(1L, 2L, 3L))
.isNotNull(User::getEmail)
.nested(w -> w.eq(User::getAge, 18).or().eq(User::getAge, 25))
.orderByAsc(User::getAge)
.orderByDesc(User::getCreateTime);
// 动态条件(condition 为 false 时忽略该条件)
String keyword = "Alice";
Integer minAge = null;
LambdaQueryWrapper<User> dynamic = Wrappers.lambdaQuery(User.class)
.like(StringUtils.isNotBlank(keyword), User::getName, keyword)
.ge(minAge != null, User::getAge, minAge); // minAge 为 null 时不加此条件自动填充
java
@Component
public class MetaObjectHandler implements com.baomidou.mybatisplus.core.handlers.MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
// 从 SecurityContext 获取当前用户
this.strictInsertFill(metaObject, "createBy", Long.class, getCurrentUserId());
}
@Override
public void updateFill(MetaObject metaObject) {
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
this.strictUpdateFill(metaObject, "updateBy", Long.class, getCurrentUserId());
}
}乐观锁
java
// 1. 实体类加 @Version
@Version
private Integer version;
// 2. 注册插件
@Configuration
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
// 3. 使用(必须先查询再更新)
User user = userMapper.selectById(1L);
user.setName("Updated");
// 更新时自动加 WHERE version = ? AND id = ?
// 并将 version + 1
int rows = userMapper.updateById(user);
if (rows == 0) {
throw new OptimisticLockException("数据已被其他人修改,请刷新后重试");
}多租户
java
// 多租户插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new TenantLineInnerInterceptor(
new TenantLineHandler() {
@Override
public Expression getTenantId() {
// 从 ThreadLocal 或 SecurityContext 获取租户 ID
Long tenantId = TenantContext.getCurrentTenantId();
return new LongValue(tenantId);
}
@Override
public String getTenantIdColumn() {
return "tenant_id";
}
@Override
public boolean ignoreTable(String tableName) {
// 不需要租户隔离的表
return "sys_config".equals(tableName);
}
}
));
return interceptor;
}代码生成器
java
// MyBatis-Plus 代码生成器(3.5.x)
FastAutoGenerator.create("jdbc:mysql://localhost:3306/mydb", "root", "password")
.globalConfig(builder -> builder
.author("YourName")
.outputDir(System.getProperty("user.dir") + "/src/main/java")
.disableOpenDir())
.packageConfig(builder -> builder
.parent("com.example")
.moduleName("user")
.entity("model")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.controller("controller"))
.strategyConfig(builder -> builder
.addInclude("t_user", "t_order") // 要生成的表
.entityBuilder()
.enableLombok()
.enableTableFieldAnnotation()
.logicDeleteColumnName("deleted")
.versionColumnName("version")
.controllerBuilder()
.enableRestStyle()
.mapperBuilder()
.enableBaseResultMap())
.execute();MyBatis-Plus 最佳实践
- 使用
LambdaQueryWrapper而非QueryWrapper,避免字段名拼写错误 - 分页查询必须配置
PaginationInnerInterceptor - 批量操作用
saveBatch,不要循环调用save - 逻辑删除配置后,所有查询自动过滤已删除数据
- 生产环境开启 SQL 性能分析插件,防止全表扫描