当前位置: 首页 > news >正文

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.lryc.cn/news/608078.html

相关文章:

  • 西门子 G120 变频器全解析:从认知到参数设置
  • 技巧|SwanLab记录ROC曲线攻略
  • LINUX82 shell脚本变量分类;系统变量;变量赋值;四则运算;shell
  • 系统性学习数据结构-第一讲-算法复杂度
  • MySQL 内置函数
  • ADB 查看 CPU 信息、查看内存信息、查看硬盘信息
  • 排序算法大全:从插入到快速排序
  • k8s使用 RBAC 鉴权
  • 论文阅读笔记:Dataset Condensation with Gradient Matching
  • [C++竞赛]数论
  • 深入 Go 底层原理(十三):interface 的内部表示与动态派发
  • [硬件电路-113]:模拟电路 - 信号处理电路 - 二极管的应用 - 精密整流电路与波形
  • sqli-labs:Less-18关卡详细解析
  • Json Jsoncpp
  • hyper-v实战系列:第一代虚拟机转第二代步骤
  • 深入理解 Docker 容器网络:为什么用 host 网络模式能解决连通性问题?
  • yolo 、Pytorch (5)IOU
  • Git、Gitee、GitHub、GitLab完整讲解:从基础到进阶
  • web:js的模块导出/导入
  • 开疆智能Profinet转Modbus网关连接信捷PLC从站配置案例
  • K8S部署ELK(二):部署Kafka消息队列
  • 深入 Go 底层原理(六):垃圾回收(GC)
  • ubuntu22.04离线一键安装gpu版docker
  • 开源列式分布式数据库clickhouse
  • pyqt5显示任务栏菜单并隐藏主窗口,环境pyqt5+vscode
  • CS课程项目设计7:基于Canvas交互友好的五子棋游戏
  • 从AI智能体出发,重构数据中台:迈向Agentic时代的数据能力体系
  • Docker容器中文PDF生成解决方案
  • Oracle 11gR2 Clusterware应知应会
  • 分布式事务----spring操作多个数据库,事务以及事务回滚还有用吗