一、介绍总览
Mybatis-Plus是一个Mybatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提高效率而生。因此在认识Mybatis-Plus之前我们需要了解下它的好基友Mybatis。
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
而Mybatis-Plus完全兼容原生 MyBatis 的所有功能,同时在其基础上封装了大量实用能力,大幅减少重复代码和开发工作量。以下是它的大概核心增强点:
提效:通用 CRUD、代码生成器免除大量重复 SQL 和代码编写工作,开发效率提升显著;
降错:Lambda 条件构造器、枚举映射等避免字段名拼写、类型转换等低级错误;
扩展:分页插件、逻辑锁、乐观锁等提供开箱即用的高级功能,无需手动封装
二、核心特性
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
三、常用注解
1. @TableName
作用:指定实体类对应的数据库表名(解决 "实体类名 ≠ 表名" 的问题)。
默认规则 :若不标注,MP 会默认将实体类名按 "驼峰转下划线" 匹配表名(如
UserInfo→user_info)。使用示例:
java// 场景1:实体类名 ≠ 表名(如实体类User对应表t_user) @Data @TableName("t_user") // 指定表名为t_user public class User { private Long id; private String name; } // 场景2:全局配置表前缀(替代单个@TableName) // 若项目中所有表都有前缀(如t_),可在application.yml中配置,无需每个实体加@TableName: # application.yml mybatis-plus: global-config: db-config: table-prefix: t_ # 所有实体类默认匹配 t_ + 实体类名(如User → t_user)
2. @TableId
作用:指定实体类中对应数据库主键的字段,同时可配置主键生成策略。
常用属性 :
type(主键类型,常用值如下):
IdType.AUTO:数据库自增(需确保数据库表主键已设置自增);
IdType.ASSIGN_ID:MP 自动生成雪花算法分布式唯一 ID(Long 类型,适合分布式系统);
IdType.ASSIGN_UUID:生成 UUID(String 类型,无主键自增场景);
IdType.NONE:无策略,需手动设置主键值。使用示例:
java@Data @TableName("t_user") public class User { // 场景1:主键自增(数据库表id字段设为自增) @TableId(type = IdType.AUTO) private Long id; // 场景2:雪花算法生成分布式ID(无需数据库自增) // @TableId(type = IdType.ASSIGN_ID) // private Long id; private String name; }
3. @TableField
作用:指定实体类字段与数据库列名的映射关系(解决 "字段名 ≠ 列名"、"字段不参与 CRUD" 等问题)。
常用属性:
value:指定数据库列名(如实体字段userName对应列user_name,若驼峰转下划线可省略);
exist:是否为数据库字段(默认true,设为false则 MP 忽略该字段,不参与任何 SQL 操作);
fill:字段自动填充(如创建时间、更新时间,需配合自定义填充处理器)。使用示例
java@Data @TableName("t_user") // 关联数据库表t_user public class User { // 主键(自增策略) @TableId(type = IdType.AUTO) private Long id; // 场景1:成员变量名与数据库字段名不一致(实体userName → 数据库user_name) @TableField("user_name") private String userName; // 场景2:成员变量名以is开头(布尔值),避免getter方法歧义(实体isDeleted → 数据库is_deleted) @TableField("is_deleted") private Boolean isDeleted; // 场景3:成员变量名与数据库关键字冲突(实体order → 数据库order,用反引号包裹关键字) @TableField("`order`") private Integer order; // 场景4:成员变量不是数据库字段(仅内存使用,MP生成SQL时忽略该字段) @TableField(exist = false) private String tempData; // 临时业务字段,不映射数据库 // 场景5:自动填充 - 仅插入时填充(创建时间) @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; // 场景5:自动填充 - 插入+更新时填充(更新时间) @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateTime; // 常规字段(驼峰转下划线匹配,无需@TableField) private Integer age; private String email; } /** * 配套的字段自动填充处理器(配合@TableField(fill = ...)使用) */ @Component // 必须交给Spring容器管理 public class MyMetaObjectHandler implements MetaObjectHandler { /** * 插入操作时的自动填充逻辑 */ @Override public void insertFill(MetaObject metaObject) { // 填充createTime(插入时) this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now()); // 填充updateTime(插入时) this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } /** * 更新操作时的自动填充逻辑 */ @Override public void updateFill(MetaObject metaObject) { // 填充updateTime(更新时) this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now()); } }
四、条件构造器
Wrapper的继承与实现关系图:
注:最常用的主要还是LambdaQueryWrapper,UpdateWrapper以及它的Lambda版本一般只对于特殊Set语句设置条件时才可以使用,eg->SET balance = balance - 200 where id in (.....),需要利用到UpdateWrapper分支扩展的SET字段赋值方法,这是QueryWrapper分支所不具备的。但平常的单表SELECT、UPDATE、DELETE语句都是由QueryWrapper分支构建WHERE条件。
层级 核心角色 核心作用 顶层接口 Wrapper<T>最基础的条件接口,定义了条件的容器规范,不直接使用。 抽象模板 AbstractWrapper<T>核心实现类,封装了所有的 WHERE条件拼接逻辑(如eq,like,in),是所有具体实现类的父类。功能分支 QueryWrapper<T>查询 / 删除 专用,侧重构建 SELECT、DELETE语句的WHERE条件。功能分支 UpdateWrapper<T>更新 专用,不仅能构建 WHERE,还扩展了SET字段赋值的方法。Lambda 增强 AbstractLambdaWrapper<T>为 Lambda 表达式提供支持的抽象父类。 最终常用 LambdaQueryWrapper<T>QueryWrapper的 Lambda 版,开发中最推荐使用。最终常用 LambdaUpdateWrapper<T>UpdateWrapper的 Lambda 版,开发中最推荐使用。Lambda化的优势:
- 类型安全:编译期校验字段名,杜绝运行时 SQL 错误,是最核心的价值;
- 效率提升:IDE 智能提示减少拼写错误,重构便捷,降低开发成本;
- 可读性强:语义化代码,避免魔法字符串,提升团队协作和维护效率。
实战代码示例:
- 基础查询:LambdaQueryWrapper (最常用)
需求:查询年龄大于 18 岁,且名字包含 "张" 的用户列表。
java// 1. 构建条件 LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>() .gt(User::getAge, 18) // gt = greater than (大于) .like(User::getName, "张"); // like (模糊查询) // 2. 调用 BaseMapper 方法 List<User> userList = userMapper.selectList(queryWrapper);
- 复杂查询:连表与字段筛选
需求 :只查询
id和name两个字段,且按照年龄倒序排序。
javaLambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>() .select(User::getId, User::getName) // 只查询特定字段,避免 select * .orderByDesc(User::getAge); List<User> userList = userMapper.selectList(queryWrapper);
- 更新操作:LambdaUpdateWrapper
需求:将 ID 为 1 的用户的名字改为 "张三丰",且年龄加 1。
java// 方式 A:使用 UpdateWrapper (需要手动拼 SET) UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>() .set("name", "张三丰") .setSql("age = age + 1") // 支持自定义 SQL 片段 .eq("id", 1); // 方式 B:使用 LambdaUpdateWrapper (推荐) LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<User>() .set(User::getName, "张三丰") .setSql("age = age + 1") .eq(User::getId, 1); // 注意:update 方法的第一个参数可以传 null,因为字段在 Wrapper 里设置了 userMapper.update(null, updateWrapper);
- 计数与删除
需求:统计所有邮箱不为空的用户数量。
javaLambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<User>() .isNotNull(User::getEmail); Long count = userMapper.selectCount(queryWrapper);尽管MP提供的条件构造器功能十分强大,但是有时基于企业业务规范(例如不允许使用UpdateWrapper分支的setSql方法,违背了在业务层中编写SQL语句)亦或是它在复杂多表关联查询、复杂聚合、分组统计、特殊SQL、批量操作等方面的限制,需要使用半自动依赖MP框架,通过自定义SQL解决,即Wrapper写WHERE条件、用户自己定义SQL语句(区别于之前的全自动,全部交由MP解决)。
关键在于取消使用BaseMapper中现成的API,自定义SQL方法,将条件在业务层作为参数传入,而且Wrapper变量名称前必须用@Param("ew")声明,并在Mapper层自定义SQL语句片段,而WHERE条件则是以
${ew.customSqlSegment}引用 Wrapper 拼接的WHERE条件(注意是${}而非#{}),此方式适用于"固定多表关联 + 动态条件过滤" 的场景。
五、BaseMapper
1.介绍
BaseMapper是 MyBatis-Plus 提供的通用 Mapper 接口,内置了单表操作的所有核心 CRUD 方法(新增、删除、修改、查询),无需手动编写 Mapper 方法和 SQL,只需让自定义 Mapper 接口继承BaseMapper,即可直接使用这些通用方法,大幅减少重复编码工作。
2.使用步骤
(1)前置条件
导入依赖,完成数据库连接和MP的基础配置。
XML<!-- MyBatis-Plus 核心依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> <!-- 推荐使用稳定版 --> </dependency>
XMLspring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 # MyBatis-Plus 基础配置 mybatis-plus: mapper-locations: classpath:mapper/**/*.xml # 自定义XML文件路径(可选) type-aliases-package: com.example.demo.entity # 实体类包路径 configuration: map-underscore-to-camel-case: true # 开启驼峰转下划线(默认开启)(2)定义实体类
javaimport com.baomidou.mybatisplus.annotation.*; import lombok.Data; import java.time.LocalDateTime; @Data @TableName("t_user") // 关联数据库表名 public class User { @TableId(type = IdType.AUTO) // 主键自增 private Long id; @TableField("user_name") // 字段名映射(可选,驼峰转下划线可省略) private String userName; private Integer age; private String email; @TableLogic // 逻辑删除字段 private Integer deleted; @TableField(fill = FieldFill.INSERT) private LocalDateTime createTime; }(3)自定义Mapper继承BaseMapper
javaimport com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.example.demo.entity.User; import org.apache.ibatis.annotations.Mapper; // 必须加@Mapper注解(或在启动类加@MapperScan扫描包) @Mapper public interface UserMapper extends BaseMapper<User> { // 空接口即可,BaseMapper已内置所有单表CRUD方法 // 若有自定义SQL,可在此添加(不影响BaseMapper功能) }(4)业务层调用BaseMapper方法
javaimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.demo.entity.User; import com.example.demo.mapper.UserMapper; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class UserService { // 注入Mapper(@Autowired或@Resource均可) @Resource private UserMapper userMapper; // ==================== 新增操作 ==================== public void addUser() { User user = new User(); user.setUserName("张三"); user.setAge(20); user.setEmail("zhangsan@test.com"); // 插入单条数据(返回受影响行数) int rows = userMapper.insert(user); // 插入后,主键会自动回显到user对象中(如自增id) System.out.println("新增用户ID:" + user.getId()); } // ==================== 修改操作 ==================== public void updateUser() { // 方式1:根据ID修改(只修改非null字段) User user = new User(); user.setId(1L); // 主键 user.setAge(21); // 要修改的字段 int rows = userMapper.updateById(user); // 方式2:条件修改(配合Wrapper) LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getUserName, "张三"); User updateUser = new User(); updateUser.setEmail("zhangsan_update@test.com"); // 根据条件修改(返回受影响行数) userMapper.update(updateUser, wrapper); } // ==================== 删除操作 ==================== public void deleteUser() { // 方式1:根据ID删除 int rows = userMapper.deleteById(1L); // 方式2:批量删除(根据ID列表) List<Long> idList = List.of(1L, 2L, 3L); userMapper.deleteBatchIds(idList); // 方式3:条件删除 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getAge, 20); userMapper.delete(wrapper); } // ==================== 查询操作 ==================== public void queryUser() { // 方式1:根据ID查询单条 User user = userMapper.selectById(1L); // 方式2:批量查询(根据ID列表) List<Long> idList = List.of(1L, 2L, 3L); List<User> userList1 = userMapper.selectBatchIds(idList); // 方式3:条件查询单条(若有多条会抛异常) LambdaQueryWrapper<User> wrapper1 = new LambdaQueryWrapper<>(); wrapper1.eq(User::getUserName, "张三"); User userOne = userMapper.selectOne(wrapper1); // 方式4:条件查询列表 LambdaQueryWrapper<User> wrapper2 = new LambdaQueryWrapper<>(); wrapper2.gt(User::getAge, 18) // 年龄>18 .like(User::getUserName, "张"); // 姓名含"张" List<User> userList2 = userMapper.selectList(wrapper2); // 方式5:条件查询总数 LambdaQueryWrapper<User> wrapper3 = new LambdaQueryWrapper<>(); wrapper3.eq(User::getDeleted, 0); long count = userMapper.selectCount(wrapper3); // 方式6:分页查询(需先配置分页插件) Page<User> page = new Page<>(1, 10); // 第1页,每页10条 LambdaQueryWrapper<User> wrapper4 = new LambdaQueryWrapper<>(); wrapper4.orderByDesc(User::getId); IPage<User> userPage = userMapper.selectPage(page, wrapper4); // 分页结果 System.out.println("总条数:" + userPage.getTotal()); System.out.println("总页数:" + userPage.getPages()); System.out.println("当前页数据:" + userPage.getRecords()); } }(5)API概览
方法名 功能描述 常用场景 insert(T entity)插入单条数据 新增记录 updateById(T entity)根据主键修改(只改非 null 字段) 单条记录更新 update(T entity, Wrapper<T> wrapper)条件修改 批量更新 / 按条件更新 deleteById(Serializable id)根据主键删除 单条记录删除 deleteBatchIds(Collection<? extends Serializable> idList)批量删除 批量删除多条记录 delete(Wrapper<T> wrapper)条件删除 按条件删除记录 selectById(Serializable id)根据主键查询 查询单条记录 selectBatchIds(Collection<? extends Serializable> idList)批量查询 根据 ID 列表查询多条记录 selectOne(Wrapper<T> wrapper)条件查询单条 确保条件唯一时查询单条记录 selectList(Wrapper<T> wrapper)条件查询列表 按条件查询多条记录 selectCount(Wrapper<T> wrapper)条件查询总数 统计符合条件的记录数 selectPage(IPage<T> page, Wrapper<T> wrapper)分页查询 分页展示数据
3.总结
BaseMapper的核心使用要点可归纳为 3 点:
极简集成 :自定义 Mapper 只需继承
BaseMapper<实体类>,无需编写基础 CRUD 方法;方法全覆盖:内置插入、修改、删除、查询、分页等所有单表操作方法,满足日常开发需求;
高度兼容:完全兼容 MyBatis 原生功能,支持自定义 SQL 和条件构造器,灵活度高。
六、IService
1.介绍
IService是 MyBatis-Plus 提供的通用服务层接口,基于BaseMapper做了进一步封装,补充了批量操作、链式查询、事务控制、分页查询(更简化) 等高级能力,是 MP 对 "Service 层" 的标准化封装,相比直接用BaseMapper更贴合实际业务开发习惯。核心特点:
完全兼容
BaseMapper的所有功能,且提供更丰富的批量 / 高级方法;内置默认实现类
ServiceImpl<M extends BaseMapper<T>, T>,无需手动实现接口方法;支持 Lambda 链式调用(
lambdaQuery()/lambdaUpdate()),简化条件构造。
2.使用步骤
(1)前置条件
已完成 MP 依赖引入、数据库配置、实体类(User)、Mapper 接口(UserMapper 继承 BaseMapper),参考之前
BaseMapper的前置配置(无额外新增依赖)。(2)自定义Service接口继承IService
javaimport com.baomidou.mybatisplus.extension.service.IService; import com.example.demo.entity.User; // 继承IService<User>,获取所有通用服务方法 public interface UserService extends IService<User> { // 若有自定义业务方法,在此添加(如复杂联表查询、多表操作) // 示例:自定义根据年龄查询用户数量 long countByAge(Integer age); }(3)实现Service 类继承ServiceImpl
javaimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.entity.User; import com.example.demo.mapper.UserMapper; import org.springframework.stereotype.Service; // @Service 交给Spring容器管理 @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { // 实现自定义方法(可直接用baseMapper调用Mapper方法) @Override public long countByAge(Integer age) { LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getAge, age); // baseMapper 是ServiceImpl内置的Mapper对象,等价于注入的UserMapper return baseMapper.selectCount(wrapper); } }关键说明:
ServiceImpl是IService的默认实现类,泛型参数:
第一个参数:对应的 Mapper 接口(如 UserMapper);
第二个参数:实体类(如 User);
baseMapper是ServiceImpl内置的 Mapper 对象,等价于@Autowired注入的 UserMapper,可直接使用。(4)控制层或业务层调用IService方法
javaimport com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.demo.entity.User; import com.example.demo.service.UserService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; @RestController public class UserController { @Resource private UserService userService; // ==================== 新增/保存操作 ==================== @GetMapping("/add") public String addUser() { User user = new User(); user.setUserName("李四"); user.setAge(22); user.setEmail("lisi@test.com"); // 1. 新增单条(等价于baseMapper.insert) boolean saveFlag = userService.save(user); // 返回是否成功(boolean) // 2. 批量新增(效率高于循环insert) List<User> userList = List.of( new User(null, "王五", 23, "wangwu@test.com"), new User(null, "赵六", 24, "zhaoliu@test.com") ); boolean saveBatchFlag = userService.saveBatch(userList); // 批量新增 // 3. 新增或更新(根据主键判断:有主键则更新,无则新增) User saveOrUpdateUser = new User(1L, "张三更新", 21, "zhangsan_update@test.com"); boolean saveOrUpdateFlag = userService.saveOrUpdate(saveOrUpdateUser); return "操作成功:" + saveFlag; } // ==================== 修改操作 ==================== @GetMapping("/update") public String updateUser() { User user = new User(); user.setId(1L); user.setAge(25); // 1. 根据ID修改(等价于baseMapper.updateById) boolean updateFlag = userService.updateById(user); // 2. 条件修改(Lambda链式调用,无需手动new Wrapper) boolean updateByWrapperFlag = userService.lambdaUpdate() .eq(User::getUserName, "李四") .set(User::getEmail, "lisi_update@test.com") .update(); // 直接执行更新 return "修改成功:" + updateFlag; } // ==================== 删除操作 ==================== @GetMapping("/delete") public String deleteUser() { // 1. 根据ID删除(等价于baseMapper.deleteById) boolean deleteFlag = userService.removeById(1L); // 2. 批量删除(等价于baseMapper.deleteBatchIds) boolean removeBatchFlag = userService.removeBatchByIds(List.of(2L, 3L)); // 3. 条件删除(Lambda链式调用) boolean removeByWrapperFlag = userService.lambdaRemove() .eq(User::getAge, 20) .remove(); return "删除成功:" + deleteFlag; } // ==================== 查询操作 ==================== @GetMapping("/query") public String queryUser() { // 1. 根据ID查询(等价于baseMapper.selectById) User user = userService.getById(1L); // 2. 批量查询(等价于baseMapper.selectBatchIds) List<User> userList1 = userService.listByIds(List.of(1L, 2L, 3L)); // 3. 条件查询单条(等价于baseMapper.selectOne,无数据返回null) User userOne = userService.getOne(userService.lambdaQuery().eq(User::getUserName, "张三")); // 4. 条件查询列表(等价于baseMapper.selectList) List<User> userList2 = userService.list(userService.lambdaQuery().gt(User::getAge, 18)); // 5. 条件查询总数(等价于baseMapper.selectCount) long count = userService.count(userService.lambdaQuery().eq(User::getDeleted, 0)); // 6. 分页查询(更简化,无需手动new Page后调用selectPage) Page<User> page = new Page<>(1, 10); // 第1页,每页10条 IPage<User> userPage = userService.page(page, userService.lambdaQuery().orderByDesc(User::getId)); // 7. Lambda链式查询(流式操作) List<User> lambdaList = userService.lambdaQuery() .eq(User::getAge, 22) .like(User::getUserName, "李") .list(); // 直接返回列表 // 8. 调用自定义方法 long ageCount = userService.countByAge(22); return "查询成功,总数:" + count; } // ==================== 批量操作(IService核心增强) ==================== @GetMapping("/batch") public String batchOperate() { // 1. 批量新增(指定批次大小,默认1000条/批) List<User> bigList = List.of(); // 假设是10000条数据 userService.saveBatch(bigList, 500); // 每500条提交一次 // 2. 批量更新(根据ID批量更新) List<User> updateList = List.of( new User(1L, "批量更新1", 26, "batch1@test.com"), new User(2L, "批量更新2", 27, "batch2@test.com") ); boolean updateBatchFlag = userService.updateBatchById(updateList); return "批量操作成功"; } }(5)API概览
方法分类 IService 方法 功能描述 对应 BaseMapper 方法 核心优势 新增 save(T entity)新增单条 insert(T entity)返回 boolean 结果(更直观) 新增 saveBatch(Collection<T> list)批量新增 无(需循环 insert) 优化批量插入性能(分批提交) 新增 / 更新 saveOrUpdate(T entity)主键存在则更新,不存在则新增 无(需手动判断) 简化 "新增或更新" 业务逻辑 修改 updateById(T entity)根据 ID 修改 updateById(T entity)返回 boolean 结果 修改 lambdaUpdate()Lambda 链式条件更新 update(T entity, Wrapper)无需手动创建 Wrapper,链式调用更简洁 删除 removeById(Serializable id)根据 ID 删除 deleteById(Serializable id)返回 boolean 结果 删除 lambdaRemove()Lambda 链式条件删除 delete(Wrapper)链式调用,无需手动创建 Wrapper 查询 getById(Serializable id)根据 ID 查询 selectById(Serializable id)返回 null(无数据时),避免空指针 查询 list(Wrapper<T> wrapper)条件查询列表 selectList(Wrapper<T> wrapper)方法名更语义化(list 替代 selectList) 查询 lambdaQuery()Lambda 链式条件查询 selectList(Wrapper<T> wrapper)链式调用,支持直接 list()/count()查询 page(IPage<T> page, Wrapper<T> wrapper)分页查询 selectPage(IPage<T> page, Wrapper<T> wrapper)方法名更语义化,与 Service 层命名习惯匹配 批量操作 updateBatchById(Collection<T> list)批量更新 无(需循环 updateById) 优化批量更新性能
3.注意事项
(1)事务控制
IService内置的批量方法(如saveBatch、updateBatchById)默认不包含事务 ,若需要事务,需在 Service 方法上添加@Transactional注解:
java@Override @Transactional // 开启事务 public boolean batchSaveUser(List<User> list) { return saveBatch(list); // 批量插入失败则整体回滚 }(2)Lambda 链式调用
lambdaQuery()/lambdaUpdate()是IService最实用的增强,支持流式操作,无需手动创建 Wrapper 对象,示例:
java// 链式查询 User user = userService.lambdaQuery() .eq(User::getId, 1L) .eq(User::getDeleted, 0) .one(); // 查单条 // 链式更新 boolean updateFlag = userService.lambdaUpdate() .eq(User::getId, 1L) .set(User::getAge, 30) .set(User::getUpdateTime, LocalDateTime.now()) .update();(3)自定义方法兼容
IService不限制自定义方法,可在接口中添加任意业务方法,实现类中通过baseMapper调用 Mapper 自定义 SQL,完全兼容原生 MyBatis 功能。(4)分页插件依赖
使用
page()方法需提前配置 MP 分页插件(与BaseMapper的selectPage要求一致),否则分页失效。
4.总结
IService的核心使用要点可归纳为 3 点:
极简集成 :自定义 Service 接口继承
IService<T>,实现类继承ServiceImpl<Mapper, T>,无需编写基础 CRUD 方法;能力增强 :相比
BaseMapper,补充了批量操作、Lambda 链式调用、语义化方法名,更贴合 Service 层开发习惯;高度兼容 :完全基于
BaseMapper封装,支持自定义方法、事务控制,兼容所有原生 MyBatis/MP 功能。
IService是 MP 对 Service 层的标准化封装,推荐在实际项目中使用(而非直接用BaseMapper),能大幅简化 Service 层代码,提升开发效率和代码可读性。
七、代码生成器
通常我们在写项目时往往都是基于三层架构,前期都需要重复建包,创建每层架构的相关类以及大量实体类,工作量十分庞大。MP的扩展功能为我们提供了代码生成器,一键生成实体类、Mapper、Service、Controller 等基础代码,保证项目代码结构统一,节省 80% 以上的重复编码工作。
传统手动配置MP的代码生成器不仅需要引入各种依赖,还需要配置一个极其复杂的Java启动类。因此在这里我将讲解更为便捷的插件,一键生成我们需要的骨架。
1.下载插件
在IDEA的Marketplace中找到MyBatisPlus插件,选择第一个初音未来图像的插件进行下载。
2.配置数据库
在IDEA的上方工具Tools栏找到Config Database,点击进行配置。
注意这一步可能会报错Public Key Retrieval is not allowed
在你的 JDBC URL 后面加上参数
allowPublicKeyRetrieval=true,完整示例如下:
javajdbc:mysql://localhost:3306/mysql?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true显示test successful!即可。
3.配置代码生成选项
上方的横栏是选择要生成代码的数据库表,下方则是你要生成哪些代码Controller、Entity、Mapper、Service等等,需要的勾选即可,下面还有是否启用lombok、resetController注解以及swagger文档等等。
点击check field可以概览生成了哪些东西,以及它们的详细信息。
配置完成后,点击"Code Generator"按钮,插件将自动为您生成所需的全套代码,包括:
Controller层
Service层
ServiceImpl实现类
Mapper接口
XML映射文件
实体类Entity
八、DB静态工具
1.介绍
MP 中的
DB是com.baomidou.mybatisplus.core.toolkit.Db的简称,是 MP 提供的无 Mapper/Service 依赖的全局静态 CRUD 工具类,它封装了通用的单表操作方法,无需定义 Mapper 接口、Service 接口,直接通过静态方法即可完成数据库操作,是 MP 提供的 "极简操作" 方案。
2.核心作用
DB工具的设计目标是简化简单场景下的数据库操作,核心价值体现在:
无侵入式:无需定义 Mapper/Service 接口,直接通过静态方法操作数据库,减少模板代码;
全局通用:一套静态方法适配所有实体类,无需为每个实体编写 Mapper/Service;
轻量便捷:适合快速开发、临时查询、工具类 / 测试类中快速操作数据库;
完全兼容:底层复用 MP 的核心逻辑(条件构造器、主键策略、逻辑删除等),与原有 Mapper/Service 无冲突。
3.使用步骤
(1)前置条件
DB工具依赖 MP 核心环境,只需保证:
已引入 MP 依赖(
mybatis-plus-boot-starter);已配置数据库连接和 MP 基础配置;
实体类已通过
@TableName/@TableId等注解完成数据库映射。
(2)使用示例
DB工具的方法与BaseMapper高度一致,均为静态方法,直接通过Db.xxx()调用,以下是高频方法示例(以User实体为例)
javaimport com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Db; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.example.demo.entity.User; import java.util.List; /** * DB 静态工具类使用示例 */ public class DbToolDemo { // ==================== 新增操作 ==================== public static void testInsert() { User user = new User(); user.setUserName("测试DB工具"); user.setAge(25); user.setEmail("db_test@test.com"); // 新增单条数据(主键自动回显) boolean success = Db.save(user); System.out.println("新增用户ID:" + user.getId()); // 批量新增 List<User> userList = List.of( new User(null, "批量1", 26, "batch1@test.com"), new User(null, "批量2", 27, "batch2@test.com") ); Db.saveBatch(userList); } // ==================== 修改操作 ==================== public static void testUpdate() { User user = new User(); user.setId(1L); // 主键 user.setAge(28); // 要修改的字段 // 根据ID修改(只改非null字段) boolean success = Db.updateById(user); // 条件修改(配合Lambda条件构造器) LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getUserName, "测试DB工具"); User updateUser = new User(); updateUser.setEmail("db_update@test.com"); Db.update(updateUser, wrapper); } // ==================== 删除操作 ==================== public static void testDelete() { // 根据ID删除 boolean success = Db.removeById(User.class, 1L); // 批量删除(ID列表) Db.removeByIds(User.class, List.of(2L, 3L)); // 条件删除 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getAge, 25); Db.remove(wrapper); } // ==================== 查询操作 ==================== public static void testQuery() { // 根据ID查询 User user = Db.getById(User.class, 1L); // 批量查询(ID列表) List<User> userList1 = Db.listByIds(User.class, List.of(1L, 2L, 3L)); // 条件查询列表 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.gt(User::getAge, 20) .like(User::getUserName, "测试"); List<User> userList2 = Db.list(wrapper); // 条件查询总数 long count = Db.count(wrapper); // 分页查询(需先配置分页插件) Page<User> page = new Page<>(1, 10); IPage<User> userPage = Db.page(page, wrapper); System.out.println("总条数:" + userPage.getTotal()); System.out.println("当前页数据:" + userPage.getRecords()); } // ==================== 新增或更新(核心便捷方法) ==================== public static void testSaveOrUpdate() { // 有主键则更新,无主键则新增 User user = new User(1L, "新增或更新", 29, "save_or_update@test.com"); Db.saveOrUpdate(user); } }
4.适用场景
场景 使用优势 快速开发 / 原型验证 无需编写 Mapper/Service,直接通过静态方法操作数据库,节省开发时间 工具类 / 测试类中的临时查询 无需注入 Mapper/Service,直接静态调用,简化工具类代码 简单单表操作(无复杂业务) 避免编写大量模板化的 Mapper/Service,减少代码冗余 多实体通用操作 一套方法适配所有实体,无需为每个实体重复定义 CRUD 方法 临时数据修复 / 批量处理 快速编写批量插入 / 更新 / 删除逻辑,无需集成到业务层
5.对比BaseMapper与IService
特性 DB 静态工具 BaseMapper IService 依赖 无(静态调用) 需定义 Mapper 接口 需定义 Service/Mapper 接口 使用方式 静态方法( Db.xxx())注入 Mapper 实例调用 注入 Service 实例调用 返回值 多为 boolean(更直观) 多为 int(受影响行数) 多为 boolean 批量操作 支持( saveBatch等)需手动循环 / 自定义 内置优化的批量方法 事务支持 需手动加 @Transactional需手动加 @Transactional可在 Service 层统一控制 适用场景 简单操作、工具类、快速开发 常规业务开发(单表) 复杂业务开发(含业务逻辑)
6.总结
DB 静态工具的核心知识点可归纳为 3 点:
核心定位:MP 提供的无依赖静态 CRUD 工具,无需定义 Mapper/Service 即可操作数据库;
使用场景:快速开发、工具类 / 测试类、简单单表操作、临时数据处理;
核心优势:极简调用、无模板代码、完全兼容 MP 原有功能,是 BaseMapper/IService 的补充而非替代。
DB工具是 MP 为简化简单场景开发提供的 "快捷方式",在不破坏原有分层架构的前提下,能大幅减少辅助场景的代码量。
九、逻辑删除
1.介绍
MP 的逻辑删除本质是拦截删除操作,将
DELETE语句转为UPDATE语句,同时拦截查询操作,自动添加逻辑删除字段的过滤条件(只查未删除数据)。
2.实现步骤
(1)添加逻辑删除字段
首先在需要逻辑删除的表中新增一个字段(如
deleted),用于标记删除状态:
sql-- 示例:给 t_user 表添加逻辑删除字段(MySQL) ALTER TABLE t_user ADD COLUMN deleted TINYINT(1) DEFAULT 0 COMMENT '逻辑删除:0-未删除,1-已删除';
(2)添加@TableLogic注解
在实体类中对逻辑删除字段添加
@TableLogic注解:
javaimport com.baomidou.mybatisplus.annotation.TableLogic; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; @Data @TableName("t_user") public class User { @TableId(type = IdType.AUTO) private Long id; private String userName; private Integer age; // 逻辑删除字段(核心注解) @TableLogic( value = "0", // 未删除的值(默认 0) delval = "1" // 已删除的值(默认 1) ) private Integer deleted; }
(3)全局配置(可选)
若项目中所有表的逻辑删除字段规则统一(如均为
deleted、0=未删/1=已删),可在application.yml中全局配置,无需在每个实体类加注解属性:
XML# application.yml mybatis-plus: global-config: db-config: # 全局逻辑删除字段名(默认 null,指定后所有实体默认使用该字段) logic-delete-field: deleted # 逻辑未删除值(默认 0) logic-not-delete-value: 0 # 逻辑已删除值(默认 1) logic-delete-value: 1
(4)业务层使用
置完成后,调用 MP 的删除方法会自动转为逻辑删除,查询方法会自动过滤已删除数据:
javaimport com.example.demo.entity.User; import com.example.demo.service.UserService; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.List; @Service public class UserBizService { @Resource private UserService userService; // 1. 逻辑删除:deleteById 转为 UPDATE public void testDelete() { // 原物理删除:DELETE FROM t_user WHERE id = 1 // 现逻辑删除:UPDATE t_user SET deleted = 1 WHERE id = 1 AND deleted = 0 boolean success = userService.removeById(1L); } // 2. 查询自动过滤:selectList 自动加 WHERE deleted = 0 public void testQuery() { // 执行 SQL:SELECT id, user_name, age, deleted FROM t_user WHERE deleted = 0 List<User> userList = userService.list(); } // 3. 批量删除:同样转为批量 UPDATE public void testBatchDelete() { // 执行 SQL:UPDATE t_user SET deleted = 1 WHERE id IN (1,2,3) AND deleted = 0 userService.removeBatchByIds(List.of(1L, 2L, 3L)); } }
3.总结
MP 逻辑删除的核心实现要点可归纳为 3 点:
基础配置 :数据库加逻辑删除字段 + 实体类加
@TableLogic注解(或全局配置);自动拦截:MP 拦截删除操作转为 UPDATE,拦截查询操作自动加过滤条件,无需修改业务代码;
灵活扩展:支持自定义删除值、手动查询已删除数据、兼容自定义 SQL(需手动加条件)。
逻辑删除是业务系统中最常用的删除策略(避免数据丢失),MP 的封装让逻辑删除实现零侵入,大幅简化开发工作。











