IService<T>
是 MyBatis-Plus 提供的服务层通用接口,封装了常用的 CRUD 操作,配合 ServiceImpl<M,T>
实现类,可极大简化业务开发,避免重复编写基础增删改查代码。
一、核心概念
1. 什么是 IService<T>
?
IService<T>
是 MyBatis-Plus 定义的服务层通用接口,位于com.baomidou.mybatisplus.extension.service
包中。- 它提供了丰富的 CRUD 方法,基于
BaseMapper<T>
实现,无需手动编写 SQL。 - 开发者只需继承
IService<User>
,MyBatis-Plus 会通过ServiceImpl<M,T>
提供默认实现。
2. 核心优势
- ✅ 减少样板代码:无需重复写
insert
,selectById
等方法。 - ✅ 链式调用:支持
lambdaQuery()
、lambdaUpdate()
等便捷构造器。 - ✅ 事务支持:批量操作(如
saveBatch
)默认开启事务。 - ✅ 扩展性强:可自定义通用方法,注入到全局。
3. 继承关系
public interface IService<T> { ... }
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> { ... }
二、操作步骤(非常详细)
步骤 1:准备实体类与 Mapper
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
private LocalDateTime createTime;
private Integer status;
}
@Mapper
public interface UserMapper extends BaseMapper<User> {}
步骤 2:创建 Service 接口继承 IService<User>
public interface UserService extends IService<User> {
// 可在此定义业务方法,如:
// List<User> getUsersByDept(Long deptId);
}
步骤 3:创建 Service 实现类继承 ServiceImpl<UserMapper, User>
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
// 无需实现 IService 方法,已由父类提供
// 可在此重写或扩展方法
}
步骤 4:在 Controller 或其他 Service 中注入并使用
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
步骤 5:核心方法使用详解
1. save(T entity)
—— 保存单条记录
@PostMapping
public R<Boolean> saveUser(@RequestBody User user) {
boolean result = userService.save(user);
return R.ok(result);
}
- 功能:插入一条记录。
- 自动填充:若字段有
@TableField(fill = FieldFill.INSERT)
,会自动填充(需配置MetaObjectHandler
)。 - ID 生成:根据
@TableId
类型自动处理(如IdType.AUTO
,IdType.ASSIGN_ID
)。
2. saveOrUpdate(T entity)
—— 保存或更新
@PutMapping
public R<Boolean> saveOrUpdateUser(@RequestBody User user) {
boolean result = userService.saveOrUpdate(user);
return R.ok(result);
}
- 判断逻辑:
- 若
entity.id != null
且数据库存在该 ID,则执行UPDATE
。 - 否则执行
INSERT
。
- 若
- 注意:仅根据主键判断,不判断唯一索引。
3. saveBatch(Collection<T> entityList)
—— 批量保存
@PostMapping("/batch")
public R<Boolean> saveUsers(@RequestBody List<User> users) {
boolean result = userService.saveBatch(users, 100); // 每 100 条提交一次
return R.ok(result);
}
- 性能优化:使用
JDBC batch
,减少数据库交互次数。 - 事务控制:默认开启事务,任一条失败则回滚(可通过
throwEx = false
关闭)。
4. remove(Wrapper<T> queryWrapper)
—— 根据条件删除
@DeleteMapping("/by-name")
public R<Boolean> removeByName(@RequestParam String name) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", name);
boolean result = userService.remove(wrapper);
return R.ok(result);
}
- 等价于:
DELETE FROM user WHERE name = ?
- 逻辑删除:若字段标注
@TableLogic
,则执行UPDATE
标记删除。
5. list(Wrapper<T> queryWrapper)
—— 查询列表
@GetMapping
public R<List<User>> listUsers() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("status", 1).orderByDesc("create_time");
List<User> users = userService.list(wrapper);
return R.ok(users);
}
- 返回值:
List<T>
,空列表不会返回null
。
6. page(IPage<T> page, Wrapper<T> queryWrapper)
—— 分页查询
@GetMapping("/page")
public R<IPage<User>> pageUsers(Page<User> page,
@RequestParam(required = false) String name) {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StringUtils.hasText(name), User::getName, name);
IPage<User> result = userService.page(page, wrapper);
return R.ok(result);
}
- 参数:
page
:Page<T>
对象,包含当前页、页大小。queryWrapper
:查询条件。
- 返回值:
IPage<T>
,包含总记录数、当前页数据等。
7. count(Wrapper<T> queryWrapper)
—— 统计数量
@GetMapping("/count")
public R<Long> countUsers(@RequestParam Integer status) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("status", status);
long count = userService.count(wrapper);
return R.ok(count);
}
- 性能提示:生成
SELECT COUNT(*)
,建议对WHERE
字段建索引。
8. update(T entity, Wrapper<T> updateWrapper)
—— 条件更新
@PutMapping("/status")
public R<Boolean> updateStatus(@RequestParam Integer status,
@RequestParam Long id) {
User user = new User();
user.setStatus(status); // 要更新的字段
LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(User::getId, id);
boolean result = userService.update(user, wrapper);
return R.ok(result);
}
- 注意:
entity
中null
值字段不会更新(除非配置fieldStrategy
)。 - 推荐:使用
LambdaUpdateWrapper
避免字段名硬编码。
9. getOne(Wrapper<T> queryWrapper)
—— 查询单条记录
@GetMapping("/one")
public R<User> getOneUser(@RequestParam String email) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("email", email);
User user = userService.getOne(wrapper);
return R.ok(user);
}
- 注意:若查询到多条记录,会抛出
TooManyResultsException
。 - 安全用法:加
last("LIMIT 1")
或使用getOne(wrapper, false)
忽略异常。
三、常见错误与解决方案
错误现象 | 原因 | 解决方案 |
---|---|---|
save 报主键冲突 |
@TableId 类型配置错误 |
使用 IdType.AUTO 或 ASSIGN_ID |
update 不更新 null 值 |
默认策略忽略 null |
使用 updateWrapper.set("column", null) |
page 不分页,查全表 |
未传 Page 对象 |
确保第一个参数是 Page<T> |
saveBatch 性能差 |
未设置 batchSize |
设置合理 batchSize (如 100~500) |
remove 执行 UPDATE 而非 DELETE |
启用了逻辑删除 | 检查 @TableLogic 注解 |
getOne 抛异常 |
查询到多条数据 | 加 LIMIT 1 或使用 false 参数忽略异常 |
四、注意事项
主键策略必须明确
使用@TableId(type = IdType.AUTO)
或分布式 ID(如IdType.ASSIGN_ID
)。逻辑删除字段需标注
@TableLogic
否则remove
会物理删除。批量操作注意内存
大数据量saveBatch
建议分批处理,避免 OOM。update
方法不更新null
值
如需更新null
,使用updateWrapper.set("col", null)
。IService
方法默认无事务
但saveBatch
、remove
等内部方法有事务。跨方法调用需自行加@Transactional
。避免在
IService
中写复杂业务逻辑
复杂逻辑应在 Service 实现类中重写方法。
五、使用技巧
技巧 1:链式查询(推荐)
// 查询状态为 1 的用户,按年龄排序
List<User> users = userService.lambdaQuery()
.eq(User::getStatus, 1)
.orderByDesc(User::getAge)
.list();
// 更新邮箱
userService.lambdaUpdate()
.eq(User::getId, 1L)
.set(User::getEmail, "new@example.com")
.update();
// 删除
userService.lambdaUpdate()
.eq(User::getStatus, 0)
.remove();
✅ 强烈推荐使用
lambdaQuery()
/lambdaUpdate()
,类型安全,代码简洁。
技巧 2:条件动态拼接
userService.lambdaQuery()
.eq(age != null, User::getAge, age)
.like(StringUtils.hasText(name), User::getName, name)
.list();
技巧 3:自定义通用方法
// 1. 扩展 IService
public interface CustomIService<T> extends IService<T> {
List<T> findAll();
}
// 2. 扩展 ServiceImpl
@Service
public class CustomServiceImpl<T> extends ServiceImpl<UserMapper, User>
implements CustomIService<User> {
@Override
public List<User> findAll() {
return this.list();
}
}
六、最佳实践
实践 | 说明 |
---|---|
✅ 服务层继承 IService<T> |
减少重复代码 |
✅ 实现类继承 ServiceImpl<M,T> |
获得默认实现 |
✅ 优先使用 lambdaQuery() |
类型安全,避免硬编码 |
✅ 批量操作设置 batchSize |
控制内存与性能平衡 |
✅ 分页使用 IPage + Page |
标准化分页接口 |
✅ 复杂逻辑重写方法 | 在 ServiceImpl 中覆盖默认行为 |
✅ 合理使用 @Transactional |
控制事务边界 |
七、性能优化
优化点 | 说明 |
---|---|
批量操作 | 使用 saveBatch(list, batchSize) 提升插入性能 |
索引优化 | WHERE 、ORDER BY 字段必须建索引 |
避免 N+1 查询 | 不要在循环中调用 getById |
分页合理 | 避免 OFFSET 过大,可用游标分页(WHERE id > lastId ) |
减少字段查询 | 使用 select 指定字段,避免 SELECT * |
缓存热点数据 | 结合 Redis 缓存频繁查询结果 |
JDBC Batch | 确保数据库连接配置 rewriteBatchedStatements=true (MySQL) |
八、总结
方法 | 用途 | 注意事项 |
---|---|---|
save |
插入单条 | 主键策略 |
saveOrUpdate |
存在更新,否则插入 | 仅判断主键 |
saveBatch |
批量插入 | 设置 batchSize |
remove |
条件删除 | 注意逻辑删除 |
list |
查询列表 | 配合 QueryWrapper |
page |
分页查询 | 传 Page 对象 |
count |
统计数量 | 建议索引 |
update |
条件更新 | null 值不更新 |
getOne |
查询单条 | 防止多条异常 |
✅ 一句话总结:
IService<T>
是 MyBatis-Plus 服务层的核心抽象,通过继承它和 ServiceImpl
,开发者可快速获得一套完整、高效、可维护的 CRUD 能力,是构建 Spring Boot 应用的推荐模式。