MyBatisPlus之CRUD接口(IService与BaseMapper)

MyBatisPlus之CRUD接口—IService与BaseMapper

    • 一、BaseMapper与IService的关系
    • 二、BaseMapper核心方法详解
      • 2.1 新增操作(Insert)
      • 2.2 查询操作(Select)
      • 2.3 更新操作(Update)
      • 2.4 删除操作(Delete)
    • 三、IService核心方法详解
      • 3.1 新增操作(Save)
      • 3.2 查询操作(Get/List)
      • 3.3 更新操作(Update)
      • 3.4 删除操作(Remove)
      • 3.5 其他实用方法
    • 四、BaseMapper与IService的选择策略
    • 五、实战技巧与避坑指南
      • 5.1 Wrapper的灵活使用
      • 5.2 批量操作的性能优化
      • 5.3 避免N+1查询问题
      • 5.4 逻辑删除与查询的注意事项

MyBatisPlus(MP)的核心优势之一是提供了开箱即用的CRUD接口,通过BaseMapper(DAO层)和IService(Service层)封装了单表操作的常用方法,无需编写SQL即可完成大部分数据库操作。

一、BaseMapper与IService的关系

在MyBatisPlus中,BaseMapperIService是实现CRUD操作的两大核心接口,二者分工明确又相互配合:

  • BaseMapper:位于DAO层,直接与数据库交互,提供基础的CRUD方法(如insertselectById),需由用户自定义的Mapper接口继承。
  • IService:位于Service层,基于BaseMapper封装了更丰富的业务方法(如批量操作、分页查询),并提供事务支持,需由用户自定义的Service接口继承。

调用关系IService的实现类(如ServiceImpl)会注入BaseMapper实例,通过调用BaseMapper的方法完成数据库操作,同时添加业务逻辑和事务控制。

使用建议

  • 简单查询直接使用BaseMapper
  • 复杂业务(如批量操作、事务管理)优先使用IService
  • 自定义SQL通过BaseMapper的方法扩展。

二、BaseMapper核心方法详解

BaseMapper<T>是MP的基础接口,泛型T为实体类类型。所有自定义Mapper接口只需继承它,即可获得17个基础CRUD方法。

2.1 新增操作(Insert)

方法签名功能描述示例
int insert(T entity)插入一条记录userMapper.insert(user)

说明

  • 插入时会根据实体类的注解(如@TableId)自动处理主键生成;
  • 若字段未设置值,会插入null(除非配置了自动填充);
  • 返回值为受影响的行数(成功插入返回1)。

示例

User user = new User();
user.setUsername("张三");
user.setAge(20);
user.setEmail("zhangsan@example.com");
int rows = userMapper.insert(user); // 插入成功后,user.getId()会自动回填主键
System.out.println("插入行数:" + rows + ",生成ID:" + user.getId());

2.2 查询操作(Select)

BaseMapper提供了7种查询方法,覆盖单条查询、批量查询、条件查询等场景:

方法签名功能描述适用场景
T selectById(Serializable id)根据ID查询已知主键的单条查询
List<T> selectBatchIds(Collection<?> ids)批量查询(根据ID集合)批量获取多条记录
List<T> selectByMap(Map<String, Object> map)根据Map条件查询简单条件查询(键为字段名)
T selectOne(@Param("ew") Wrapper<T> queryWrapper)根据条件查询单条确保结果唯一的查询(如唯一索引)
Integer selectCount(@Param("ew") Wrapper<T> queryWrapper)条件查询总数统计符合条件的记录数
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper)条件查询列表复杂条件的多条查询
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper)条件查询(返回Map)只需要部分字段,无需实体类

示例1:根据ID查询

User user = userMapper.selectById(1L); // ID为1的用户

示例2:批量查询

List<Long> ids = Arrays.asList(1L, 2L, 3L);
List<User> users = userMapper.selectBatchIds(ids); // 查询ID为1、2、3的用户

示例3:条件查询(使用QueryWrapper)

// 查询年龄≥20且用户名包含"张"的用户
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("age", 20) // 年龄≥20.like("username", "张"); // 用户名含"张"
List<User> users = userMapper.selectList(queryWrapper);

示例4:查询总数

// 统计年龄<18的用户数量
Integer count = userMapper.selectCount(new QueryWrapper<User>().lt("age", 18)
);

2.3 更新操作(Update)

方法签名功能描述适用场景
int updateById(@Param("et") T entity)根据ID更新已知主键,更新部分字段
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper)根据条件更新按条件批量更新

示例1:根据ID更新

User user = new User();
user.setId(1L); // 必须设置ID
user.setAge(21); // 只更新年龄
int rows = userMapper.updateById(user); // SQL:UPDATE user SET age=21 WHERE id=1

示例2:条件更新

// 将所有年龄<18的用户状态改为"禁用"
User user = new User();
user.setStatus(StatusEnum.DISABLE);UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.lt("age", 18); // 条件:年龄<18int rows = userMapper.update(user, updateWrapper); 
// SQL:UPDATE user SET status=0 WHERE age < 18

2.4 删除操作(Delete)

方法签名功能描述适用场景
int deleteById(Serializable id)根据ID删除删除单条记录
int deleteBatchIds(Collection<?> ids)批量删除(根据ID集合)批量删除多条记录
int deleteByMap(Map<String, Object> map)根据Map条件删除简单条件的批量删除
int delete(@Param("ew") Wrapper<T> queryWrapper)根据条件删除复杂条件的批量删除

示例1:根据ID删除

int rows = userMapper.deleteById(1L); // 删除ID为1的用户

示例2:条件删除

// 删除邮箱为空的用户
int rows = userMapper.delete(new QueryWrapper<User>().isNull("email")
);

三、IService核心方法详解

IService<T>是Service层的接口,基于BaseMapper扩展了更丰富的方法,尤其适合复杂业务场景。自定义Service接口需继承IService,实现类需继承ServiceImpl<Mapper, T>

3.1 新增操作(Save)

方法签名功能描述与BaseMapper的区别
boolean save(T entity)插入一条记录返回boolean(成功/失败),BaseMapper返回int
boolean saveBatch(Collection<T> entityList)批量插入内部默认分批插入(默认1000条/批)
boolean saveBatch(Collection<T> entityList, int batchSize)自定义批次大小的批量插入可指定每批插入数量

示例1:单条插入

User user = new User();
user.setUsername("李四");
boolean success = userService.save(user); // 成功返回true

示例2:批量插入

List<User> userList = new ArrayList<>();
for (int i = 0; i < 100; i++) {User user = new User();user.setUsername("用户" + i);user.setAge(18 + i % 20);userList.add(user);
}
// 批量插入(默认每批1000条)
boolean success = userService.saveBatch(userList);
// 自定义每批50条
// userService.saveBatch(userList, 50);

3.2 查询操作(Get/List)

IService的查询方法在BaseMapper基础上增加了分页查询和链式调用支持:

方法签名功能描述特色功能
T getById(Serializable id)根据ID查询selectById,返回null时无异常
List<T> listByIds(Collection<?> ids)批量查询(ID集合)selectBatchIds
List<T> list(Wrapper<T> queryWrapper)条件查询列表selectList
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper)分页查询支持分页插件,返回分页对象
long count(Wrapper<T> queryWrapper)条件查询总数selectCount,返回long类型
boolean exists(Wrapper<T> queryWrapper)判断是否存在符合条件的记录简化count > 0的判断

示例1:分页查询

// 分页查询:第2页,每页10条,条件:年龄≥20
Page<User> page = new Page<>(2, 10); // 页码从1开始
IPage<User> userPage = userService.page(page, new QueryWrapper<User>().ge("age", 20)
);List<User> records = userPage.getRecords(); // 当前页数据
long total = userPage.getTotal(); // 总条数
long pages = userPage.getPages(); // 总页数

示例2:判断记录是否存在

// 判断是否存在用户名=张三的用户
boolean exists = userService.exists(new QueryWrapper<User>().eq("username", "张三")
);

3.3 更新操作(Update)

方法签名功能描述特色功能
boolean updateById(T entity)根据ID更新updateById,返回boolean
boolean update(Wrapper<T> updateWrapper)根据条件更新(无实体类)直接通过Wrapper设置更新字段
boolean update(T entity, Wrapper<T> updateWrapper)根据条件更新BaseMapper.update
boolean updateBatchById(Collection<T> entityList)批量更新(根据ID)批量更新多条记录
boolean updateBatchById(Collection<T> entityList, int batchSize)自定义批次的批量更新可指定每批更新数量

示例1:通过Wrapper直接更新

// 无需实体类,直接设置更新字段
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("username", "张三") // 条件.set("age", 22) // 更新字段.set("email", "new@example.com");
boolean success = userService.update(updateWrapper);

示例2:批量更新

List<User> userList = new ArrayList<>();
// 假设userList中包含多个设置了ID和待更新字段的User对象
boolean success = userService.updateBatchById(userList);

3.4 删除操作(Remove)

方法签名功能描述与BaseMapper的区别
boolean removeById(Serializable id)根据ID删除返回boolean,BaseMapper返回int
boolean removeByIds(Collection<?> ids)批量删除(ID集合)deleteBatchIds,返回boolean
boolean remove(Wrapper<T> queryWrapper)根据条件删除delete,返回boolean
boolean removeByMap(Map<String, Object> map)根据Map条件删除deleteByMap,返回boolean

示例

// 根据条件删除
boolean success = userService.remove(new QueryWrapper<User>().eq("status", StatusEnum.DISABLE)
);

3.5 其他实用方法

方法签名功能描述示例
T getOne(Wrapper<T> queryWrapper, boolean throwEx)查询单条,支持是否抛异常getOne(wrapper, true) 结果不唯一时抛异常
List<T> list(IPage<T> page, Wrapper<T> queryWrapper)分页查询(返回List)只获取分页数据,忽略总条数
boolean saveOrUpdate(T entity)新增或更新(根据ID判断)有ID则更新,无ID则插入
boolean saveOrUpdateBatch(Collection<T> entityList)批量新增或更新批量处理新增/更新

示例:saveOrUpdate(新增或更新)

User user1 = new User();
user1.setId(1L); // 有ID → 更新
user1.setAge(23);User user2 = new User();
user2.setUsername("新用户"); // 无ID → 新增userService.saveOrUpdate(user1);
userService.saveOrUpdate(user2);

四、BaseMapper与IService的选择策略

场景推荐使用理由
简单CRUD操作(单表)优先IService方法返回boolean,更符合业务逻辑判断;支持批量操作
复杂查询(多条件)IService + Wrapper链式调用更简洁,支持分页
事务管理IServiceService层天然适合控制事务(@Transactional
自定义SQLBaseMapper需在Mapper接口中定义方法,通过@Select等注解实现
批量操作(1000+条)IService的批量方法内置分批处理,避免SQL过长导致性能问题
分布式事务IService结合@Transactional(rollbackFor = Exception.class)确保事务一致性

五、实战技巧与避坑指南

5.1 Wrapper的灵活使用

  • LambdaQueryWrapper:避免硬编码字段名,推荐使用:

    LambdaQueryWrapper<User> lambdaWrapper = new LambdaQueryWrapper<>();
    lambdaWrapper.ge(User::getAge, 20) // 引用方法,类型安全.like(User::getUsername, "张");
    
  • 条件判断:通过if动态组装条件:

    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    if (minAge != null) {wrapper.ge(User::getAge, minAge);
    }
    if (keyword != null) {wrapper.like(User::getUsername, keyword).or().like(User::getEmail, keyword);
    }
    

5.2 批量操作的性能优化

  • 设置合理的批次大小saveBatch默认每批1000条,可根据数据库性能调整(如MySQL建议500-1000条/批);
  • 关闭批量操作的自动填充:若无需更新时间等字段,可通过全局配置关闭,提升性能;
  • 使用INSERT INTO ... VALUES (...), (...)语法:MP的批量插入默认使用此语法,比循环单条插入效率高10倍以上。

5.3 避免N+1查询问题

当查询列表后需要根据关联ID查询其他表时,容易出现N+1问题(1次查列表,N次查详情)。解决方案:

  • 手动编写联表查询SQL(通过BaseMapper扩展);
  • 使用MP的@TableField关联查询(适合简单关联);
  • 结合PageHelper等插件实现分页联表查询。

5.4 逻辑删除与查询的注意事项

  • 逻辑删除后,BaseMapperIService的查询方法会自动过滤已删除数据(添加is_deleted = 0条件);
  • 自定义SQL需手动添加逻辑删除。

若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917310.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917310.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

axios请求的取消

axios请求的取消解决&#xff1a;axios请求的取消解决&#xff1a;axios请求的取消 在使用 Axios 发起请求时&#xff0c;有时候你可能需要取消这些请求&#xff0c;比如当组件销毁时或者用户操作导致不再需要获取之前发起的请求结果。Axios 支持通过 Cancel Token 取消请求。 …

深入理解C++中的Lazy Evaluation:延迟计算的艺术

在编程世界里&#xff0c;“最好的运算就是从未执行的运算” —— 这句话深刻揭示了性能优化的核心思路。如果一个计算过程最终不会被使用&#xff0c;那么提前执行它就是纯粹的资源浪费。这种思想衍生出了 Lazy Evaluation&#xff08;缓式评估&#xff09; 技术&#xff1a;延…

php完整处理word中表单数据的方法

使用php基础方式实现word中表单处理<?php/*** zipFile 类用于处理 .docx 文件的解压、修改和重新打包*/ class zipFile {/** var ZipArchive ZIP 文件对象 */private $zipFile;/** var string 临时目录路径 */private $tempDir;/** var string 嵌入的 Excel 文件临时目录路…

Node.js 操作 MongoDB

目录 Node.js 操作 MongoDB 一、什么是 MongoDB&#xff1f; 二、MongoDB 的功能概览 三、MongoDB 的安装与启动 安装 MongoDB&#xff08;以本地安装为例&#xff09; 启动 MongoDB 四、Node.js 如何连接 MongoDB&#xff1f; 使用 Mongoose ODM 工具 建立连接 五、…

先学Python还是c++?

选择先学Python还是C&#xff0c;取决于你的学习目标、应用场景和职业规划。以下是两者的对比分析和建议&#xff0c;帮助你做出更适合自己的选择&#xff1a;一、核心差异对比维度PythonC学习曲线简单易上手&#xff08;语法接近自然语言&#xff09;复杂&#xff08;需理解指…

Trae + Notion MCP:将你的Notion数据库升级为智能对话机器人

前言 Notion作为一款功能强大的信息管理工具&#xff0c;被广泛用于项目跟踪、知识库构建和数据整理。然而&#xff0c;随着数据量的增长&#xff0c;我们常常会发现自己陷入了重复和繁琐的操作中。比如&#xff0c;为了找到符合特定条件的几条数据&#xff0c;需要在庞大的数…

【iOS】retain/release底层实现原理

文章目录前言前情知识retain和release的实现原理&#xff08;MRC手动管理&#xff09;retain&#xff08;MRC手动管理&#xff09;retain源码内联函数rootRetain源码相关的sidetable_tryRetain()方法retain底层工作流程总结releaserelease源码内联函数rootRelease源码小结前言 …

文件同步神器-rsync命令讲解

rsync 是一个强大的文件同步与传输工具&#xff0c;广泛用于本地或远程服务器之间的高效文件备份、镜像或同步。其核心优势是通过增量传输​&#xff08;仅传输文件差异部分&#xff09;和压缩减少数据传输量&#xff0c;同时支持保留文件元数据&#xff08;如权限、时间戳、所…

Rust: 工具链版本更新

遇到 cargo build --release 错误&#xff0c;比如&#xff0c;当前 Rust 工具链版本&#xff08;1.78.0&#xff09;低于依赖项所需的最低版本&#xff08;部分依赖要求 ≥1.82.0&#xff09;。以下是系统化的解决方案&#xff1a; &#x1f527; 一、升级 Rust 工具链&#x…

Prompt-to-Prompt| 修改Attention会有“反向传播”或梯度计算?

需要注意的几个问题&#xff1a;额外计算开销&#xff1a;Cross-Attention Control原因&#xff1a;Prompt-to-Prompt的编辑方法需要动态干预交叉注意力&#xff08;Cross-Attention&#xff09;层的权重&#xff0c;这会引入额外的计算和显存占用&#xff1a;需要缓存注意力矩…

电商API接口的优势、数据采集方法及功能说明

一、电商API接口的核心优势1. 高效性与准确性数据采集效率&#xff1a;API通过标准化参数&#xff08;如商品ID、类目&#xff09;直接获取结构化数据&#xff08;JSON/XML&#xff09;&#xff0c;无需解析HTML&#xff0c;减少误差。例如&#xff0c;采集1000条商品信息&…

iOS企业签名掉签,iOS企业签名掉签了怎么办?

不能上架到App Store的iOS应用 &#xff0c;几乎每一个开发者的选择都是通过iOS签名这种内测渠道来完成APP的上架任务&#xff0c;最常用的就是企业签名、超级签名以及TF上架&#xff0c;其中最受欢迎的当属于企业签名了。不过企业签名会出现掉签的现象&#xff0c;那么企业签名…

存储成本深度优化:冷热分层与生命周期管理——从视频平台年省200万实践解析智能存储架构

一、冷热分层&#xff1a;存储成本优化的核心逻辑1.1 数据访问的“二八定律”据行业统计&#xff0c;80%的访问集中在20%的热数据上&#xff0c;而超过90天的历史数据访问频率下降70%以上。某视频平台存储超10PB媒体文件&#xff0c;未分层前年存储成本高达680万元&#xff0c;…

Java设计模式之《备忘录模式》

目录 1. 概念 1.1、定义 1.2、适用场景 2、角色划分 3、实现 1、Originator&#xff08;发起人&#xff09; 2、Memento&#xff08;备忘录&#xff09; 3、Caretaker&#xff08;管理者&#xff09; 4、使用示例 4、优缺点 4.1、优点 4.2、缺点 前言 备忘录模式是…

SpringBoot 多环境配置

在实际项目开发中&#xff0c;不同环境往往有不同的配置需求&#xff1a; 开发环境&#xff08;dev&#xff09;&#xff1a;本地调试&#xff0c;连接测试数据库&#xff1b;测试环境&#xff08;test&#xff09;&#xff1a;接口联调&#xff0c;接近真实场景&#xff1b;生…

延凡智慧医院数字孪生平台

延凡智慧医院数字孪生平台是延凡科技依托物联网、数字孪生、AI 算法及边缘计算技术打造的医疗场景全要素数字化解决方案&#xff0c;通过构建医院物理实体与虚拟空间的实时映射&#xff0c;实现医疗资源优化、运营效率提升及患者体验升级。一、平台价值&#xff08;一&#xff…

谈谈WebAssembly、PWA、Web Workers的作用和场景

WebAssembly、PWA 和 Web Workers 是现代 Web 开发中提升性能、扩展能力的重要技术&#xff0c;各自解决不同场景的问题&#xff0c;以下结合实际使用经验分析&#xff1a;一、WebAssembly&#xff08;Wasm&#xff09;&#xff1a;高性能代码执行作用&#xff1a;WebAssembly …

嵌入式第十八课!!数据结构篇入门及单向链表

在前几章对C语言的学习中&#xff0c;我们学到了&#xff1a;基本的C语法和简单算法面向过程的编程思想而在数据结构这一篇章&#xff0c;我们将要学习&#xff1a;常用的数据存储结构算法面向对象的编程思想数据结构在正式开始学习之前&#xff0c;我们先来了解一下什么是数据…

win10任务栏出问题了,原来是wincompressbar导致的

问题描述兄弟们客户说自己电脑现在有问题了&#xff0c;任务栏显示的都不对&#xff0c;和之前的都不一样&#xff0c;现在使用起来非常难受&#xff0c;我们来看一下&#xff0c;这到底是什么问题吧&#xff01;到客户现场&#xff0c;查看发现&#xff0c;客户桌面系统最底下…

FFmpegHandler 功能解析,C语言程序化设计与C++面向对象设计的核心差异

FFmpegHandler 功能解析 本文件记录了关于 FFmpegHandler 类中核心函数工作流程的详细解释。Q: FFmpeg逐帧解码&#xff0c;FFmpegHandler::openVideo 和 FFmpegHandler::readAVFrame 这两个函数都分别做了什么&#xff1f; A: 可以把整个过程想象成“准备播放一部电影”&#…