首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >分享一些Mybatis-Plus的核心功能

分享一些Mybatis-Plus的核心功能

原创
作者头像
小焱
发布2025-11-05 14:05:34
发布2025-11-05 14:05:34
1270
举报
文章被收录于专栏:Java开发Java开发

MyBatis-Plus(MP)的核心价值是 “增强 MyBatis 但不改变其本质”,通过封装高频操作、提供便捷工具,大幅减少重复编码。以下是其最核心、最常用的功能,结合实际用法和场景说明,让你快速抓住 MP 的精髓:

一、 基础 CRUD 增强(BaseMapper + IService)

MP 最核心的功能之一,通过继承 ​​BaseMapper​​(Mapper 层)或 ​​IService​​(Service 层),零 XML/注解实现 80% 的 CRUD 操作,无需手动编写 ​​insert/delete/update/select​​ 基础 SQL。

1. BaseMapper(Mapper 层核心)

所有 Mapper 接口只需继承 ​​BaseMapper<T>​​,即可直接使用内置的 CRUD 方法,无需编写任何接口方法。

核心方法(常用)

方法

功能描述

示例代码

​​insert(T entity)​​

新增一条记录

​​userMapper.insert(new User("张三", 20));​​

​​deleteById(Serializable id)​​

根据主键删除

​​userMapper.deleteById(1L);​​

​​updateById(T entity)​​

根据主键更新(非 null 字段)

​​User user = new User(); user.setId(1L); user.setAge(21); userMapper.updateById(user);​​

​​selectById(Serializable id)​​

根据主键查询

​​User user = userMapper.selectById(1L);​​

​​selectList(Wrapper<T> queryWrapper)​​

条件查询列表

配合条件构造器使用(见下文)

​​selectPage(Page<T> page, Wrapper<T> queryWrapper)​​

分页查询

配合分页插件使用(见下文)

示例
代码语言:javascript
复制
// Mapper 接口(无需写任何方法)
public interface UserMapper extends BaseMapper<User> {
}

// 调用示例(Service 层)
@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public void addUser(User user) {
        userMapper.insert(user); // 直接使用 BaseMapper 方法
    }
}
2. IService(Service 层增强)

Service 层推荐继承 ​​IService<T>​​ + 实现类继承 ​​ServiceImpl<Mapper, T>​​,在 BaseMapper 基础上增加了 批量操作、逻辑判断 等更贴合业务的方法,比直接用 BaseMapper 更优雅。

核心增强方法

方法

功能描述

示例代码

​​save(T entity)​​

新增(同 insert,语义更清晰)

​​userService.save(user);​​

​​removeById(Serializable id)​​

逻辑删除(配合全局配置)

​​userService.removeById(1L);​​

​​updateById(T entity)​​

更新(同 BaseMapper)

​​userService.updateById(user);​​

​​getById(Serializable id)​​

查询单条(同 selectById)

​​User user = userService.getById(1L);​​

​​list(Wrapper<T> queryWrapper)​​

条件查询列表

​​List<User> list = userService.list(queryWrapper);​​

​​page(Page<T> page, Wrapper<T> queryWrapper)​​

分页查询

​​IPage<User> page = userService.page(new Page<>(1,10), queryWrapper);​​

​​saveBatch(Collection<T> entityList, int batchSize)​​

批量新增

​​userService.saveBatch(userList, 500);​​

​​updateBatchById(Collection<T> entityList, int batchSize)​​

批量更新

​​userService.updateBatchById(userList, 300);​​

​​count(Wrapper<T> queryWrapper)​​

条件统计总数

​​long count = userService.count(queryWrapper);​​

示例
代码语言:javascript
复制
// Service 接口
public interface UserService extends IService<User> {
}

// Service 实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 无需重写基础 CRUD,直接使用父类方法
}

// 调用示例
@Controller
public class UserController {
    @Autowired
    private UserService userService;

    public IPage<User> getUserPage(Integer pageNum, Integer pageSize) {
        // 分页查询:第1页,每页10条
        return userService.page(new Page<>(pageNum, pageSize), null);
    }
}

二、 条件构造器(QueryWrapper/LambdaQueryWrapper)

MP 最强大的功能之一,动态拼接 SQL 条件,无需手动写 ​​WHERE​​ 子句,支持所有 SQL 条件(等于、模糊查询、范围查询等),且提供 Lambda 版本(类型安全,避免字段名写错)。

1. 核心优势
  • 动态条件:支持“条件成立才拼接”,避免无效 SQL;
  • 类型安全:Lambda 版本直接引用实体类方法(如 ​​User::getUserName​​),无需硬编码字段名(如 ​​"user_name"​​),减少字段修改导致的 Bug;
  • 链式调用:代码简洁,可读性高。
2. 常用条件方法(LambdaQueryWrapper 示例)

方法

功能描述

示例代码(查询年龄>18且用户名含“张”的用户)

​​eq​​

等于(=)

​​eq(User::getId, 1L)​​

​​ne​​

不等于(!=)

​​ne(User::getStatus, 0)​​

​​gt​​

大于(>)

​​gt(User::getAge, 18)​​

​​ge​​

大于等于(>=)

​​ge(User::getAge, 18)​​

​​lt​​

小于(<)

​​lt(User::getAge, 30)​​

​​le​​

小于等于(<=)

​​le(User::getAge, 30)​​

​​like​​

模糊查询(LIKE %值%)

​​like(User::getUserName, "张")​​

​​likeLeft​​

左模糊(LIKE %值)

​​likeLeft(User::getUserName, "张")​​

​​likeRight​​

右模糊(LIKE 值%)

​​likeRight(User::getUserName, "张")​​

​​in​​

IN 查询

​​in(User::getId, 1L, 2L, 3L)​​

​​notIn​​

NOT IN 查询

​​notIn(User::getId, 4L, 5L)​​

​​isNull​​

字段为 NULL

​​isNull(User::getPhone)​​

​​isNotNull​​

字段不为 NULL

​​isNotNull(User::getPhone)​​

​​orderByAsc​​

升序排序

​​orderByAsc(User::getCreateTime)​​

​​orderByDesc​​

降序排序

​​orderByDesc(User::getCreateTime)​​

​​and​​​/​​or​​

逻辑与/或(嵌套条件)

​​and(w -> w.gt(User::getAge,18).like(User::getUserName,"张"))​​

3. 动态条件拼接(实战常用)
代码语言:javascript
复制
// 场景:根据前端传入的参数(可能为 null)动态筛选
public List<User> queryUser(String userName, Integer age, Integer status) {
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
            // 只有 userName 不为 null 且不为空,才拼接模糊查询
            .like(StringUtils.hasText(userName), User::getUserName, userName)
            // 只有 age 不为 null,才拼接 age >= age
            .ge(age != null, User::getAge, age)
            // 只有 status 不为 null,才拼接 status = status
            .eq(status != null, User::getStatus, status)
            // 固定条件:未删除(逻辑删除)
            .eq(User::getIsDeleted, 0)
            // 排序:按创建时间降序
            .orderByDesc(User::getCreateTime);
    return userService.list(queryWrapper);
}
4. 注意事项
  • LambdaQueryWrapper 依赖实体类的 getter 方法(Lombok ​​@Data​​ 可自动生成);
  • 避免硬编码字段名:优先用 LambdaQueryWrapper,而非 QueryWrapper(如 ​​QueryWrapper<User>().like("user_name", "张")​​);
  • 复杂条件用 ​​and​​/​​or​​ 嵌套:提高代码可读性,例如:
代码语言:javascript
复制
// 条件:(age > 18 且 user_name 含"张") 或 (age < 10 且 user_name 含"李")
queryWrapper.and(w -> w.gt(User::getAge,18).like(User::getUserName,"张"))
            .or(w -> w.lt(User::getAge,10).like(User::getUserName,"李"));

三、 分页插件(内置分页,零配置)

MP 内置分页插件,无需手动编写分页 SQL(无需 ​​LIMIT​​ 关键字),只需传入 ​​Page​​ 对象,即可自动实现分页查询,支持总数统计、分页参数自动计算。

1. 核心特点
  • 零配置:MP 3.5+ 版本自动注入分页插件,无需额外配置;
  • 支持多表联查:自定义 XML 中的复杂 SQL 也能自动分页;
  • 分页结果丰富:返回 ​​IPage​​ 对象,包含总记录数、总页数、当前页数据等。
2. 分页查询示例
代码语言:javascript
复制
// 场景:分页查询年龄>18的用户,第1页,每页10条
public IPage<User> getUserPage(Integer pageNum, Integer pageSize) {
    // 1. 构建分页对象(pageNum:页码,pageSize:每页条数)
    Page<User> page = new Page<>(pageNum, pageSize);
    
    // 2. 构建查询条件
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>()
            .gt(User::getAge, 18)
            .eq(User::getIsDeleted, 0);
    
    // 3. 分页查询(Service 层 page 方法)
    IPage<User> userPage = userService.page(page, queryWrapper);
    
    // 4. 分页结果解析
    long total = userPage.getTotal(); // 总记录数
    long pages = userPage.getPages(); // 总页数
    List<User> records = userPage.getRecords(); // 当前页数据
    return userPage;
}
3. 自定义 XML 分页(多表联查场景)

即使是多表联查的自定义 SQL,MP 也能自动分页,只需在 Mapper 方法中传入 ​​Page​​ 对象作为第一个参数:

代码语言:javascript
复制
// Mapper 接口
public interface UserMapper extends BaseMapper<User> {
    // 自定义分页:多表联查(用户+部门)
    IPage<UserVO> selectUserWithDept(Page<User> page, @Param("query") UserQueryDTO query);
}

// XML 文件(无需写 LIMIT,MP 自动拼接)
<select id="selectUserWithDept" resultType="com.example.vo.UserVO">
    SELECT u.id, u.user_name, d.dept_name 
    FROM t_user u
    LEFT JOIN t_dept d ON u.dept_id = d.id
    WHERE u.is_deleted = 0
    <if test="query.userName != null">
        AND u.user_name LIKE CONCAT('%', #{query.userName}, '%')
    </if>
</select>

// 调用示例
public IPage<UserVO> queryUserWithDept(Integer pageNum, Integer pageSize, UserQueryDTO query) {
    return userMapper.selectUserWithDept(new Page<>(pageNum, pageSize), query);
}
4. 分页优化
  • 大数据量分页:若总记录数超 10 万条,​​COUNT(*)​​ 会影响性能,可关闭总数统计:
代码语言:javascript
复制
Page<User> page = new Page<>(pageNum, pageSize, false); // 第三个参数:是否查询总数(默认 true)
  • 索引优化:​​ORDER BY​​ 字段和查询条件字段必须建索引,避免全表扫描导致分页卡顿。

四、 逻辑删除(避免物理删除,数据安全)

MP 支持逻辑删除(假删除),通过字段标记数据是否删除(如 ​​is_deleted​​),而非直接删除数据库记录,保障数据可恢复性。

1. 核心优势
  • 全局配置:统一配置逻辑删除字段和值,无需每个实体类重复注解;
  • 自动拼接条件:查询/更新时自动拼接 ​​is_deleted = 未删除值​​,无需手动写条件;
  • 兼容基础方法:​​removeById​​ 自动改为更新 ​​is_deleted = 已删除值​​,而非 ​​DELETE​​ 语句。
2. 配置步骤
(1)全局配置(application.yml)
代码语言:javascript
复制
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: isDeleted # 全局逻辑删除字段名(实体类属性名)
      logic-delete-value: 1 # 已删除值(数据库存储值)
      logic-not-delete-value: 0 # 未删除值(数据库存储值)
(2)实体类注解(可选,全局配置后可省略)
代码语言:javascript
复制
@Data
@TableName("t_user")
public class User {
    // 逻辑删除字段(全局配置后,@TableLogic 可省略,若字段名与全局配置不一致需显式指定)
    @TableLogic
    private Integer isDeleted;
}
(3)使用效果
  • 删除操作:​​userService.removeById(1L)​​ 自动执行 ​​UPDATE t_user SET is_deleted = 1 WHERE id = 1 AND is_deleted = 0​​;
  • 查询操作:​​userService.getById(1L)​​ 自动执行 ​​SELECT * FROM t_user WHERE id = 1 AND is_deleted = 0​​;
  • 逻辑删除后的数据不会被基础 CRUD 方法查询到,保障数据安全。
3. 注意事项
  • 自定义 XML 需手动过滤:MP 只对 BaseMapper/IService 基础方法自动拼接逻辑删除条件,自定义 XML 需手动加 ​​AND is_deleted = 0​​;
  • 字段类型:逻辑删除字段建议用 ​​tinyint​​(0/1),避免用 ​​boolean​​(兼容性差)。

五、 自动填充(创建时间/更新时间)

对于 ​​create_time​​(创建时间)、​​update_time​​(更新时间)等通用字段,MP 支持自动填充,无需手动设置值,减少重复编码。

1. 配置步骤
(1)实体类注解(指定填充时机)
代码语言:javascript
复制
@Data
@TableName("t_user")
public class User {
    // 插入时填充(仅新增时设置)
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    
    // 插入+更新时填充(新增和修改时都设置)
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}
(2)实现填充处理器(MetaObjectHandler)
代码语言:javascript
复制
@Component // 必须交给 Spring 管理
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 插入时填充
    @Override
    public void insertFill(MetaObject metaObject) {
        // 严格填充:只有字段为 null 时才填充(避免覆盖手动设置的值)
        strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
        strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
    
    // 更新时填充
    @Override
    public void updateFill(MetaObject metaObject) {
        strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
    }
}
(3)使用效果
  • 新增用户:​​userService.save(user)​​ 自动设置 ​​createTime​​ 和 ​​updateTime​​ 为当前时间;
  • 更新用户:​​userService.updateById(user)​​ 自动更新 ​​updateTime​​ 为当前时间;
  • 无需手动 ​​user.setCreateTime(LocalDateTime.now())​​,减少重复代码。

六、 主键策略(灵活适配不同场景)

MP 提供多种主键生成策略,适配单库、分布式等不同场景,无需手动管理主键值。

1. 核心主键策略(IdType)

策略类型

适用场景

配置方式

​​AUTO​​

MySQL 自增字段(单库场景)

​​@TableId(type = IdType.AUTO)​​

​​ASSIGN_ID​​

分布式场景(雪花算法)

​​@TableId(type = IdType.ASSIGN_ID)​​

​​ASSIGN_UUID​​

分布式场景(UUID 字符串)

​​@TableId(type = IdType.ASSIGN_UUID)​​

​​INPUT​​

手动输入主键

​​@TableId(type = IdType.INPUT)​​

2. 全局配置(推荐)

在 ​​application.yml​​ 中全局配置主键策略,避免每个实体类重复注解:

代码语言:javascript
复制
mybatis-plus:
  global-config:
    db-config:
      id-type: AUTO # 单库场景(MySQL 自增)
      # id-type: ASSIGN_ID # 分布式场景(雪花算法,生成 19 位 Long 型 ID)
3. 场景说明
  • 单库场景:用 ​​AUTO​​(需数据库字段设置自增),简单高效;
  • 分布式场景:用 ​​ASSIGN_ID​​(MP 内置雪花算法,无需数据库配置,生成唯一 ID),避免 ID 冲突;
  • 字符串主键:用 ​​ASSIGN_UUID​​(生成无中划线的 UUID 字符串,如 ​​1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed​​)。

七、 乐观锁插件(解决并发更新冲突)

在并发场景下(如多用户同时编辑同一条数据),乐观锁通过版本号机制避免数据覆盖,MP 内置乐观锁插件,配置简单。

1. 核心原理
  • 实体类添加 ​​version​​ 字段(版本号),数据库对应字段设为 ​​int​​ 类型,默认值 0;
  • 更新时自动拼接 ​​version = 原版本号​​,并设置 ​​version = 原版本号 + 1​​;
  • 若并发时版本号不匹配(如 A 用户更新后版本号+1,B 用户的版本号已失效),则更新失败(返回受影响行数 0)。
2. 配置步骤
(1)实体类注解
代码语言:javascript
复制
@Data
@TableName("t_user")
public class User {
    // 乐观锁版本号字段(必须加 @Version 注解)
    @Version
    private Integer version; // 数据库字段默认值 0
}
(2)插件配置(MP 3.5+ 自动注入,可选)
代码语言:javascript
复制
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        // 乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}
(3)使用示例
代码语言:javascript
复制
// 并发更新用户信息
public boolean updateUser(UserUpdateDTO dto) {
    // 1. 查询当前用户(获取最新版本号)
    User user = userService.getById(dto.getId());
    if (user == null) {
        throw new RuntimeException("用户不存在");
    }
    
    // 2. 校验版本号(前端需传入当前版本号,避免并发覆盖)
    if (!user.getVersion().equals(dto.getVersion())) {
        throw new RuntimeException("数据已被修改,请刷新后重试");
    }
    
    // 3. 更新数据(MP 自动拼接 version 条件)
    BeanUtils.copyProperties(dto, user);
    return userService.updateById(user);
}
(4)执行 SQL 说明

更新时自动执行:

代码语言:javascript
复制
UPDATE t_user 
SET user_name = '李四', version = 1 
WHERE id = 1 AND version = 0;
  • 若版本号不匹配(如 ​​version = 0​​ 已变为 ​​1​​),则受影响行数为 0,更新失败。

总结:核心功能使用优先级

  1. 基础 CRUD:优先用 ​​IService​​(Service 层)+ ​​BaseMapper​​(Mapper 层),零 XML 实现;
  2. 条件查询:优先用 ​​LambdaQueryWrapper​​,类型安全,动态拼接;
  3. 分页查询:直接用 ​​Page​​ + ​​IPage​​,无需手动写 ​​LIMIT​​;
  4. 通用字段:用「自动填充」(创建/更新时间)+「逻辑删除」,减少重复编码;
  5. 并发场景:用「乐观锁」解决更新冲突;
  6. 分布式场景:用 ​​ASSIGN_ID​​ 主键策略,避免 ID 冲突。

掌握以上核心功能,可覆盖 90% 以上的业务场景,大幅减少重复编码,让开发精力聚焦在业务逻辑上。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、 基础 CRUD 增强(BaseMapper + IService)
    • 1. BaseMapper(Mapper 层核心)
      • 核心方法(常用)
      • 示例
    • 2. IService(Service 层增强)
      • 核心增强方法
      • 示例
  • 二、 条件构造器(QueryWrapper/LambdaQueryWrapper)
    • 1. 核心优势
    • 2. 常用条件方法(LambdaQueryWrapper 示例)
    • 3. 动态条件拼接(实战常用)
    • 4. 注意事项
  • 三、 分页插件(内置分页,零配置)
    • 1. 核心特点
    • 2. 分页查询示例
    • 3. 自定义 XML 分页(多表联查场景)
    • 4. 分页优化
  • 四、 逻辑删除(避免物理删除,数据安全)
    • 1. 核心优势
    • 2. 配置步骤
      • (1)全局配置(application.yml)
      • (2)实体类注解(可选,全局配置后可省略)
      • (3)使用效果
    • 3. 注意事项
  • 五、 自动填充(创建时间/更新时间)
    • 1. 配置步骤
      • (1)实体类注解(指定填充时机)
      • (2)实现填充处理器(MetaObjectHandler)
      • (3)使用效果
  • 六、 主键策略(灵活适配不同场景)
    • 1. 核心主键策略(IdType)
    • 2. 全局配置(推荐)
    • 3. 场景说明
  • 七、 乐观锁插件(解决并发更新冲突)
    • 1. 核心原理
    • 2. 配置步骤
      • (1)实体类注解
      • (2)插件配置(MP 3.5+ 自动注入,可选)
      • (3)使用示例
      • (4)执行 SQL 说明
  • 总结:核心功能使用优先级
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档