MyBatisPlus 15个高级用法详解

 更新时间:2026年03月07日 14:42:14   作者:程序员大华  
MyBatis-Plus(简称MP)是Java后端开发中非常流行的ORM框架,它能够简化CRUD操作,下面给大家分享MyBatisPlus 15个高级用法,感兴趣的朋友跟随小编一起看看吧

大家好,我是大华!相信Java后端开发的朋友们对MyBatis-Plus(简称MP)肯定不陌生,它可以让CRUD的操作瞬间起飞。

今天就给大家分享15个使用技巧。

1. Service 和 Mapper?

IService:内置了海量现成方法,save, update, list, page 等,适用于绝大多数单表 CRUD。 BaseMapper:当需要进行复杂联表查询、或者要写自定义 SQL 时,它就是你的主战场。

// 简单查询,用 Service 的 lambdaQuery
List<User> users = userService.lambdaQuery()
                .eq(User::getStatus, 1)
                .like(User::getName, "张")
                .list();
// 复杂查询,用 Mapper + XML 或注解!
// 在 UserMapper.java 中
@Select("SELECT u.*, d.dept_name FROM user u LEFT JOIN dept d ON u.dept_id = d.id WHERE u.id = #{userId}")
UserVO selectUserDetail(@Param("userId") Long userId);

别在 Service 里硬塞复杂 SQL,该写 XML 的时候还是不能偷懒。

2. Lambda 表达式

MP 最棒的特性之一就是 Lambda 查询,编译期就能发现字段名错误,告别运行时才发现拼错的尴尬。

// 【错误姿势】字段名是字符串,容易拼错,编译器不报错
userService.lambdaQuery().eq("naem", "张三"); // 运行才报错,哭死!
// 【正确姿势】使用方法引用,安全又优雅
userService.lambdaQuery().eq(User::getName, "张三"); 
// 编译不通过,立马改正!

3. 分页查询,不只是PageHelper的替代品

MP 的分页功能非常强大,而且与自身条件构造器无缝集成。

// 创建分页参数,并指定排序
Page<User> page = new Page<>(1, 20); // 查第1页,每页20条
page.addOrder(OrderItem.desc("create_time")); // 按创建时间倒序
// 执行分页查询
Page<User> userPage = userService.page(page,
        Wrappers.<User>lambdaQuery()
                .eq(User::getDeptId, 2)
);
// 直接转换为 VO 分页对象,一步到位
Page<UserVO> voPage = userPage.convert(user -> {
    UserVO vo = new UserVO();
    BeanUtils.copyProperties(user, vo); // 使用 Spring 的工具类
    // 或者用 MapStruct 等更专业的工具
    return vo;
});

4. 批量操作,性能提升的关键

大批量数据插入/更新时,一条条处理会让数据库哭泣。

// 批量插入,分批提交
List<User> hugeUserList = ... // 一个巨大的列表
userService.saveBatch(hugeUserList, 1000); // 每1000条批量提交一次
// 批量更新(自己控制事务)
@Transactional(rollbackFor = Exception.class)
public void batchUpdateStatus(List<Long> ids, Integer status) {
    List<User> updateList = ids.stream().map(id -> {
        User user = new User();
        user.setId(id);
        user.setStatus(status);
        return user;
    }).collect(Collectors.toList());
    userService.updateBatchById(updateList);
}

5. 条件构造器,让你的逻辑更清晰

QueryWrapperLambdaQueryWrapper可以构建非常复杂的查询逻辑。

// 复杂的 AND-OR 组合查询
List<User> users = userService.lambdaQuery()
    .eq(User::getStatus, 1)
    .and(wrapper -> wrapper // 这是一个 AND 嵌套
        .like(User::getName, "张")
        .or()
        .like(User::getEmail, "zhang") // name LIKE '%张%' OR email LIKE '%zhang%'
    )
    .between(User::getCreateTime, startTime, endTime)
    .list();
// 【性能技巧】只查需要的字段,避免 SELECT *
List<User> userList = userService.lambdaQuery()
    .select(User::getId, User::getName) // 只查询 ID 和 Name 字段
    .eq(User::getStatus, 1)
    .list();

6. 自动填充,告别手动 set 创建时间

create_time, update_time 这种字段,就别再手动 set 了。

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        // 插入时自动填充
        this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        this.strictInsertFill(metaObject, "createBy", String.class, getCurrentUser());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        // 更新时自动填充
        this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}
// 实体类字段上需要加注解
public class User {
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

7. 逻辑删除,数据不是真的删除

千万别用 delete from 硬删数据了!

# 在 application.yml 中配置
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted  # 全局逻辑删除实体字段名
      logic-delete-value: 1        # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0    # 逻辑未删除值(默认为 0)

配置后,调用 userService.removeById(1),MP 实际执行的是: UPDATE user SET deleted = 1 WHERE id = 1 AND deleted = 0。 所有查询也会自动带上 AND deleted = 0 条件。

8. 枚举处理器,告别数据库存数字的迷惑行为

数据库存 status = 1,代码里还要猜 1 是啥意思?用枚举!

@Getter
public enum UserStatus {
    ENABLED(1, "启用"),
    DISABLED(0, "禁用");
    private final int code;
    private final String desc;
    UserStatus(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}
// 实体类中直接使用枚举类型
public class User {
    private UserStatus status;
}
// 配置枚举处理器(Spring Boot 基本不用配了,开箱即用)

这样,数据库存的是数字 1,但代码里操作的一直是 UserStatus.ENABLED,清晰明了!

9. 多租户数据隔离,SAAS 系统必备

SAAS 应用中,不同租户的数据必须严格隔离。MP 的租户插件可以自动在每次查询时加上租户 ID。

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 租户插件
        TenantLineInnerInterceptor tenantInterceptor = new TenantLineInnerInterceptor();
        tenantInterceptor.setTenantLineHandler(new TenantLineHandler() {
            @Override
            public Expression getTenantId() {
                // 从当前上下文中获取租户ID,比如从 JWT Token 中
                return new StringValue(TenantContext.getCurrentTenantId());
            }
            @Override
            public String getTenantIdColumn() {
                return "tenant_id"; // 数据库中的租户ID列名
            }
            @Override
            public boolean ignoreTable(String tableName) {
                // 忽略不需要租户隔离的表,如全局配置表
                return "system_config".equals(tableName);
            }
        });
        interceptor.addInnerInterceptor(tenantInterceptor);
        return interceptor;
    }
}

10. 代码生成器,效率翻倍利器

别再手撸 Entity, Mapper, Service, Controller 了!

public class CodeGenerator {
    public static void main(String[] args) {
        AutoGenerator generator = new AutoGenerator();
        // 数据源配置
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/test");
        dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
        dataSourceConfig.setUsername("root");
        dataSourceConfig.setPassword("123456");
        generator.setDataSource(dataSourceConfig);
        // 全局配置
        GlobalConfig globalConfig = new GlobalConfig();
        globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
        globalConfig.setAuthor("大华");
        globalConfig.setOpen(false);
        globalConfig.setSwagger2(true); // 实体属性 Swagger2 注解
        generator.setGlobalConfig(globalConfig);
        // 包配置
        PackageConfig packageConfig = new PackageConfig();
        packageConfig.setParent("com.laomao.demo");
        packageConfig.setEntity("domain.entity");
        packageConfig.setMapper("dao.mapper");
        packageConfig.setService("service");
        packageConfig.setServiceImpl("service.impl");
        generator.setPackageInfo(packageConfig);
        generator.execute(); // 执行生成
    }
}

运行一下,全套代码瞬间生成!

11. 自定义全局拦截器,统一处理逻辑

可以用来做数据权限控制、SQL 性能监控、字段加解密等。

@Component
@Slf4j
public class SqlLogInterceptor implements InnerInterceptor {
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, BoundSql boundSql) {
        long start = System.currentTimeMillis();
        // 将开始时间存入当前线程上下文
    }
    @Override
    public void afterQuery(Executor executor, MappedStatement ms, Object parameter, BoundSql boundSql, List<Object> result) {
        long end = System.currentTimeMillis();
        long cost = end - start;
        if (cost > 1000) { // 超过1秒算慢SQL
            log.warn("慢SQL警告: {}, 执行耗时: {}ms", boundSql.getSql(), cost);
            // 可以接入告警系统,通知开发人员
        }
    }
}

12. 分布式主键 ID,告别数据库自增

在分布式系统中,数据库自增 ID 是瓶颈。推荐使用雪花算法。

public class User {
    // 指定主键类型为 ASSIGN_ID(雪花算法)
    @TableId(type = IdType.ASSIGN_ID)
    private Long id; // 注意是 Long,不是 Integer
    // ...
}

13. 乐观锁,防止并发更新覆盖

高并发下,防止后提交的数据覆盖先提交的数据。

// 实体类中增加版本号字段
public class User {
    @Version
    private Integer version;
}
// 更新时,MP会自动带上版本号条件
User user = userService.getById(1L);
user.setName("新名字");
userService.updateById(user); // SQL: UPDATE user SET name=?, version=? WHERE id=? AND version=?

14. 结果映射,自动处理一对一、一对多

MP 可以和 MyBatis 的 @Result 注解完美结合。

// 在 Mapper 方法上使用复杂结果映射
@Select("SELECT u.*, d.name as dept_name FROM user u LEFT JOIN department d ON u.dept_id = d.id WHERE u.id = #{id}")
@Results({
    @Result(column = "id", property = "id"),
    @Result(column = "dept_name", property = "deptName"),
    @Result(column = "id", property = "roles", 
            many = @Many(select = "com.laomao.mapper.RoleMapper.findByUserId"))
})
UserVO findUserWithDept(Long id);

15. 事务管理,保证数据一致性

这是最后一道防线,也是最关键的一道。

@Service
public class UserService {
    @Transactional(rollbackFor = Exception.class) // 注意:默认只回滚 RuntimeException
    public void createUserWithInitData(User user) {
        // 1. 保存用户基本信息
        userService.save(user);
        // 2. 初始化用户账户
        Account account = new Account();
        account.setUserId(user.getId());
        accountService.save(account);
        // 3. 发送欢迎消息(如果消息发送失败,希望用户创建也回滚)
        messageService.sendWelcomeMessage(user.getId());
        // 任何一个步骤出错,所有操作都会回滚
    }
}

总结

MyBatis-Plus 的强大远不止于此,但掌握以上 15个核心技巧,足以让你在日常开发中游刃有余,写出既高效又优雅的代码。

到此这篇关于MyBatisPlus 高级用法详解的文章就介绍到这了,更多相关MyBatisPlus用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 基于Spring Data的AuditorAware审计功能的示例代码

    基于Spring Data的AuditorAware审计功能的示例代码

    这篇文章主要介绍了基于Spring Data的AuditorAware审计功能的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-03-03
  • SpringBoot MongoDB与MongoDB GridFS基本使用

    SpringBoot MongoDB与MongoDB GridFS基本使用

    这篇文章主要为大家介绍了SpringBoot MongoDB与MongoDB GridFS基本使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • MyBatis-plus批量插入的通用方法使用

    MyBatis-plus批量插入的通用方法使用

    mybatis-plus的IService接口默认提供saveBatch批量插入,也是唯一一个默认批量插入,在数据量不是很大的情况下可以直接使用,本文带你详细了解MyBatis-plus 批量插入的通用方法及使用方法,需要的朋友可以参考一下
    2023-04-04
  • Java使用bcrypt实现对密码加密效果详解

    Java使用bcrypt实现对密码加密效果详解

    bcrypt是一种自带盐值(自动加盐)的加密方案。本文将通过示例为大家详细介绍这一对密码进行加密的算法,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-03-03
  • 详解Spring Boot 定制HTTP消息转换器

    详解Spring Boot 定制HTTP消息转换器

    本篇文章主要介绍了详解Spring Boot 定制HTTP消息转换器,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • iBatis习惯用的16条SQL语句

    iBatis习惯用的16条SQL语句

    iBatis 是apache 的一个开源项目,一个O/R Mapping 解决方案,iBatis 最大的特点就是小巧,上手很快.这篇文章主要介绍了iBatis习惯用的16条SQL语句的相关资料,需要的朋友可以参考下
    2016-10-10
  • springboot中使用@NotNull注解无效解决方法

    springboot中使用@NotNull注解无效解决方法

    这篇文章主要给大家介绍了关于springboot中使用@NotNull注解无效的解决方法,进行参数校验的时候,加了@NotNull注解,@Validated注解和@Valid注解,但是参数校验的时候不生效,需要的朋友可以参考下
    2023-08-08
  • 详解Spring Boot实战之Filter实现使用JWT进行接口认证

    详解Spring Boot实战之Filter实现使用JWT进行接口认证

    本篇文章主要介绍了详解Spring Boot实战之Filter实现使用JWT进行接口认证,具有一定的参考价值,有兴趣的可以了解一下
    2017-07-07
  • Spring Cloud 微服务全栈实践指南

    Spring Cloud 微服务全栈实践指南

    本文详细介绍了SpringCloud在微服务架构中的核心能力,包括服务注册与发现、配置中心、服务通信与网关、稳定性工程、可观测性、数据一致性与工程落地,以及云原生的进阶演进,适合已有SpringBoot基础的工程师进行参考和实践,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • Java编写掷骰子游戏

    Java编写掷骰子游戏

    这篇文章主要介绍了Java编写掷骰子游戏,需要的朋友可以参考下
    2015-11-11

最新评论