Skip to content

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 性能分析插件,防止全表扫描

系统学习 Java 生态,深入底层架构