MyBatis-Plus 三种数据库操作方式详解 + 常用方法大全
MyBatis-Plus 三种数据库操作方式详解 + 常用方法大全
在 Java 开发中,MyBatis-Plus(简称 MP)作为 MyBatis 的增强工具,极大简化了数据库操作。MP 提供了多种数据库操作方式,适配不同业务场景,掌握其核心用法能大幅提升开发效率。本文将详细拆解 MP 三种核心操作方式的区别、适用场景,并整理常用方法合集,助力开发者快速上手。
一、核心概念:三种操作方式总览
MP 的数据库操作本质是对 MyBatis 的封装,三种核心方式分别对应不同的使用层级,从简洁到灵活逐步递进:
this.***方式:基于 MP 提供的ServiceImpl基类,直接调用封装好的 CRUD 方法,极简高效。this.baseMapper().***方式:通过 Service 访问对应的 Mapper 对象,支持自定义 SQL 和复杂查询,兼顾灵活与性能。service.getBaseMapper().***方式:跨 Service 获取其他实体的 Mapper,实现跨表/跨模块操作,适用于聚合统计场景。
二、三种操作方式详细解析
2.1 this.*** 方式:极简 CRUD 首选
核心定义
Service 层接口继承 IService<T>,实现类继承 ServiceImpl<Mapper, Entity> 后,可直接通过 this 调用基类封装的 CRUD 方法。
核心特点
- 无需手动编写 Mapper 方法,开箱即用。
- 内置参数校验、异常处理和事务支持(需配合
@Transactional)。 - 支持 Lambda 条件查询,类型安全,避免字段名硬编码。
- 代码简洁可读性强,开发效率最高。
常用方法合集
// 1. 查询操作
this.getById(id); // 根据 ID 单个查询
this.listByIds(Collections.singletonList(id)); // 批量查询(传入 ID 列表)
this.list(); // 查询所有记录
this.count(); // 统计总记录数
this.getOne(new LambdaQueryChainWrapper<>(lambdaQuery()).eq(Entity::getId, id)); // 条件查询单个(避免返回多个报错)
this.lambdaQuery().eq(Entity::getStatus, 1).ge(Entity::createTime, LocalDateTime.now().minusDays(7)).list(); // Lambda 多条件查询
this.page(new Page<>(1, 10), lambdaQuery().eq(Entity::type, 2)); // 分页查询// 2. 新增/修改操作
this.save(entity); // 单条新增
this.saveBatch(Collections.singletonList(entity), 100); // 批量新增(批量大小 100)
this.saveOrUpdate(entity); // 新增或更新(存在 ID 则更新,否则新增)
this.saveOrUpdateBatch(entities, 100); // 批量新增或更新
this.updateById(entity); // 根据 ID 单条更新
this.lambdaUpdate().set(Entity::getStatus, 0).eq(Entity::id, id).update(); // Lambda 条件更新
this.updateBatchById(entities, 100); // 批量更新(需实体含 ID)// 3. 删除操作
this.removeById(id); // 根据 ID 单条删除
this.removeByIds(Collections.singletonList(id)); // 批量删除(传入 ID 列表)
this.lambdaRemove().eq(Entity::expireTime, LocalDateTime.now()).remove(); // Lambda 条件删除
适用场景
- 单表基本 CRUD 操作(新增、查询、更新、删除)。
- 简单条件查询(无复杂联表、聚合需求)。
- 批量数据处理(批量新增/更新/删除)。
- 业务逻辑简单,追求开发效率的场景。
2.2 this.baseMapper().*** 方式:复杂查询必备
核心定义
ServiceImpl 基类内置了 baseMapper 属性,对应当前 Service 关联的 Mapper 接口。通过 this.baseMapper() 可直接调用 Mapper 中的方法,包括 MP 自动生成的方法和自定义 SQL 方法。
核心特点
- 更接近原生 MyBatis,支持自定义 XML/注解 SQL。
- 可使用
QueryWrapper/LambdaQueryWrapper构建复杂条件。 - 支持动态 SQL、结果映射、联表查询等 MyBatis 高级特性。
- 性能更优,减少中间层封装开销,适合性能敏感场景。
常用方法合集
// 1. 基础查询
this.baseMapper().selectById(id); // 根据 ID 查询
this.baseMapper().selectBatchIds(Collections.singletonList(id)); // 批量查询
this.baseMapper().selectList(new LambdaQueryWrapper<Entity>().eq(Entity::name, "test")); // 条件查询列表
this.baseMapper().selectCount(new LambdaQueryWrapper<Entity>().eq(Entity::status, 1)); // 条件统计
this.baseMapper().selectOne(new LambdaQueryWrapper<Entity>().eq(Entity::id, id)); // 条件查询单个
this.baseMapper().selectPage(new Page<>(1, 10), new LambdaQueryWrapper<Entity>().eq(Entity::type, 2)); // 分页查询// 2. 新增/修改/删除
this.baseMapper().insert(entity); // 单条新增
this.baseMapper().updateById(entity); // 根据 ID 更新
this.baseMapper().update(new LambdaUpdateWrapper<Entity>().set(Entity::status, 0).eq(Entity::id, id)
); // 条件更新
this.baseMapper().deleteById(id); // 根据 ID 删除
this.baseMapper().delete(new LambdaQueryWrapper<Entity>().eq(Entity::expireTime, LocalDateTime.now())); // 条件删除// 3. 自定义方法(需在 Mapper 接口中定义)
this.baseMapper().selectByCustomCondition(param); // 自定义条件查询(如联表、复杂过滤)
this.baseMapper().customUpdate(param); // 自定义更新(如批量更新特定字段)
适用场景
- 需要自定义 SQL(联表查询、复杂过滤、存储过程调用)。
- 复杂条件查询(多字段组合、动态条件拼接)。
- 性能敏感场景(如高频查询、大数据量操作)。
- 需使用 MyBatis 高级特性(结果映射、动态 SQL、一级缓存优化)。
2.3 service.getBaseMapper().*** 方式:跨模块聚合专用
核心定义
通过注入其他 Service 实例,调用其 getBaseMapper() 方法,直接操作其他实体表的 Mapper,实现跨表/跨模块数据操作。
核心特点
- 跨 Service 直接操作数据库,绕过 Service 层业务逻辑。
- 适合多表聚合查询、跨模块统计分析。
- 灵活性极高,但需谨慎使用,避免破坏业务封装。
- 事务管理需手动控制,否则可能导致数据一致性问题。
常用方法合集
// 注入其他 Service(如 PictureService)
@Autowired
private PictureService pictureService;// 1. 跨表查询
List<Picture> pictureList = pictureService.getBaseMapper().selectList(new LambdaQueryWrapper<Picture>().eq(Picture::relatedId, entityId).eq(Picture::status, 1)
); // 查询关联图片列表// 2. 聚合统计
List<Map<String, Object>> categoryCount = pictureService.getBaseMapper().selectMaps(new QueryWrapper<Picture>().select("category", "count(*) as count").groupBy("category").having("count(*) > 10")
); // 统计各分类图片数量(大于 10 条)// 3. 跨表分页
IPage<Map<String, Object>> pageResult = pictureService.getBaseMapper().selectMapsPage(new Page<>(1, 10),new QueryWrapper<Picture>().eq("related_type", "article").orderByDesc("create_time")
); // 分页查询指定类型的关联数据// 4. 跨表删除(谨慎使用)
pictureService.getBaseMapper().delete(new LambdaQueryWrapper<Picture>().eq(Picture::relatedId, entityId)
); // 删除关联的图片数据
适用场景
- 跨模块数据聚合(如统计多个表的关联数据)。
- 复杂统计分析(多表分组、聚合函数计算)。
- 需绕过其他 Service 层复杂业务逻辑,直接操作数据库。
- 临时数据处理、报表生成等场景。
注意事项
- 破坏 Service 层封装性,可能导致业务逻辑不一致。
- 事务需手动管理(如使用
@Transactional(propagation = Propagation.REQUIRED))。 - 增加代码耦合度,后续维护成本升高。
- 非必要场景避免使用,优先通过 Service 层调用实现跨模块交互。
三、三种方式核心区别对比表
| 特性 | this.*** 方式 | this.baseMapper().*** 方式 | service.getBaseMapper().*** 方式 |
|---|---|---|---|
| 使用范围 | 当前实体表 | 当前实体表 | 其他实体表(跨模块/跨表) |
| 代码简洁性 | 极高 | 中等 | 中等 |
| 灵活性 | 低(仅支持基础操作) | 高(支持自定义 SQL) | 极高(跨表+自定义 SQL) |
| 性能 | 中等(多一层封装) | 高(接近原生 MyBatis) | 高(直接操作 Mapper) |
| 业务封装性 | 好(遵循 Service 规范) | 好(仅操作当前实体) | 差(绕过其他 Service 逻辑) |
| 事务支持 | 自动(继承 Service 事务) | 手动(需自行注解) | 需手动控制(跨 Service 事务) |
| 适用场景 | 简单 CRUD、批量操作 | 复杂查询、性能优化、自定义 SQL | 跨表聚合、统计分析、临时数据处理 |
四、MP 常用方法分类汇总
4.1 查询类(高频)
| 方法签名 | 功能描述 | 所属方式 |
|---|---|---|
getById(ID id) | 根据 ID 查询单条记录 | this.*** |
listByIds(Collection<? extends ID> ids) | 批量查询(ID 列表) | this.*** |
list(Wrapper<T> queryWrapper) | 条件查询列表 | this.***/baseMapper |
lambdaQuery().eq(...) | Lambda 条件查询(类型安全) | this.*** |
selectPage(IPage<T> page, Wrapper<T> queryWrapper) | 分页查询 | baseMapper/跨 Service |
selectCount(Wrapper<T> queryWrapper) | 条件统计记录数 | baseMapper/跨 Service |
selectMaps(Wrapper<T> queryWrapper) | 条件查询返回 Map 列表(无需实体) | baseMapper/跨 Service |
4.2 新增/修改类(高频)
| 方法签名 | 功能描述 | 所属方式 |
|---|---|---|
save(T entity) | 单条新增 | this.*** |
saveBatch(Collection<T> entityList, int batchSize) | 批量新增 | this.*** |
saveOrUpdate(T entity) | 新增或更新(根据 ID 判断) | this.*** |
updateById(T entity) | 根据 ID 更新 | 三种方式均支持 |
lambdaUpdate().set(...).eq(...) | Lambda 条件更新 | this.*** |
update(Wrapper<T> updateWrapper) | 条件更新(非 ID 字段) | baseMapper/跨 Service |
updateBatchById(Collection<T> entityList, int batchSize) | 批量更新 | this.*** |
4.3 删除类(高频)
| 方法签名 | 功能描述 | 所属方式 |
|---|---|---|
removeById(ID id) | 根据 ID 删除 | this.*** |
removeByIds(Collection<? extends ID> ids) | 批量删除(ID 列表) | this.*** |
lambdaRemove().eq(...) | Lambda 条件删除 | this.*** |
delete(Wrapper<T> queryWrapper) | 条件删除 | baseMapper/跨 Service |
五、最佳实践建议
- 优先使用
this.***方式:单表基础 CRUD 场景首选,代码简洁、维护成本低,且内置异常处理和事务支持。 - 复杂场景用
this.baseMapper().***:需要自定义 SQL、联表查询或性能优化时,切换到此方式,兼顾灵活性和性能。 - 谨慎使用跨 Service 方式:仅在跨表聚合、统计分析等必要场景使用,使用时需注意事务管理和代码耦合问题。
- 保持代码一致性:同一项目中尽量统一操作方式,避免混用导致可读性下降(如统一用
this.***处理简单 CRUD,baseMapper处理复杂查询)。 - 善用 Lambda 表达式:优先使用
LambdaQueryWrapper/LambdaUpdateWrapper,避免字段名硬编码,减少因字段修改导致的 Bug。 - 批量操作指定批次大小:使用
saveBatch/updateBatchById时,指定合理的批次大小(如 100-500),避免一次性操作过多数据导致数据库压力过大。
总结
MyBatis-Plus 的三种数据库操作方式各有侧重,从极简的 this.*** 到灵活的 baseMapper,再到跨模块的 service.getBaseMapper(),覆盖了从简单 CRUD 到复杂聚合的全场景需求。掌握其核心区别和适用场景,结合常用方法合集,能大幅提升数据库操作效率。
如果本文对你有帮助,欢迎点赞、收藏、转发~ 若需补充特定场景的用法示例(如联表查询、动态 SQL 实战),可在评论区留言!
