MyBatisPlus之CRUD接口(IService与BaseMapper)

发布于:2025-08-03 ⋅ 阅读:(17) ⋅ 点赞:(0)

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); // 条件:年龄<18

int 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 链式调用更简洁,支持分页
事务管理 IService Service层天然适合控制事务(@Transactional
自定义SQL BaseMapper 需在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需手动添加逻辑删除。

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