深入理解 MyBatis-Plus 的 `ServiceImpl`
在使用 MyBatis-Plus 时,除了熟悉 BaseMapper
接口,很多开发者还会接触到 ServiceImpl
—— 它是 MyBatis-Plus 提供的一个 通用业务层基础实现类。合理理解与使用 ServiceImpl
,可以极大地简化 Service 层的 CRUD 编码工作,提升开发效率。
一、背景与定位
在典型的三层架构中:
- Controller 层:接收请求、返回结果
- Service 层:业务逻辑编排
- DAO 层 (Mapper):直接操作数据库
MyBatis-Plus 提供了 BaseMapper
简化了 DAO 层的 CRUD,而在 Service 层,通常需要编写一堆相似的增删改查逻辑。为此,MyBatis-Plus 提供了 IService
接口 + ServiceImpl
实现类,将通用逻辑抽象出来。
其主要类关系如下:
IService<T> ←—— 通用 Service 接口(定义通用方法)↑
ServiceImpl<M extends BaseMapper<T>, T> ←—— 通用 Service 实现类↑
自定义 Service 实现类(继承 ServiceImpl)
二、源码结构
ServiceImpl
位于:
package com.baomidou.mybatisplus.extension.service.impl;
其类定义大致如下:
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {@Autowiredprotected M baseMapper;@Overridepublic boolean save(T entity) { ... }@Overridepublic boolean removeById(Serializable id) { ... }@Overridepublic T getById(Serializable id) { ... }@Overridepublic boolean updateById(T entity) { ... }@Overridepublic List<T> list() { ... }@Overridepublic Page<T> page(Page<T> page, Wrapper<T> queryWrapper) { ... }// 其他更多通用方法 ...
}
关键点:
- 泛型 M:指定对应的
Mapper
,即 DAO 层 - 泛型 T:指定实体类
- baseMapper:自动注入,承载底层数据库操作
- 方法实现:大部分方法直接委托给
BaseMapper
三、使用方式
1. 定义实体与 Mapper
@Data
@TableName("user")
public class User {private Long id;private String name;private Integer age;
}
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
2. 定义 Service 接口与实现
public interface UserService extends IService<User> {// 可以扩展自定义方法
}
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>implements UserService {
}
3. 在 Controller 中调用
@RestController
@RequestMapping("/user")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/add")public String addUser(@RequestBody User user) {return userService.save(user) ? "ok" : "fail";}@GetMapping("/{id}")public User getUser(@PathVariable Long id) {return userService.getById(id);}
}
这样,一个基础的 CRUD 就完成了,几乎无需自己写实现逻辑。
四、常用方法分类
ServiceImpl
提供了大量常用方法,可以归类如下:
1. 新增
save(T entity)
saveBatch(Collection<T> entityList)
saveOrUpdate(T entity)
2. 删除
removeById(Serializable id)
removeByIds(Collection<? extends Serializable> idList)
remove(Wrapper<T> queryWrapper)
3. 修改
updateById(T entity)
update(T entity, Wrapper<T> updateWrapper)
updateBatchById(Collection<T> entityList)
4. 查询
getById(Serializable id)
listByIds(Collection<? extends Serializable> idList)
list(Wrapper<T> queryWrapper)
page(Page<T> page, Wrapper<T> queryWrapper)
getOne(Wrapper<T> queryWrapper)
5. 统计与判断
count()
exists(Wrapper<T> queryWrapper)
五、常见问题
1. 必须继承 ServiceImpl
吗?
不是必须。你也可以只用 BaseMapper
,在 Controller 里直接调用。但这样会让业务层缺失,代码耦合度较高,推荐通过 ServiceImpl 来隔离业务逻辑与持久化。
2. 如果要写自定义方法怎么办?
直接在 Service 接口和实现类中新增即可,比如:
@Override
public List<User> findByAge(Integer age) {return lambdaQuery().eq(User::getAge, age).list();
}
3. 与 Mapper
的关系?
Mapper
:最贴近数据库,负责 SQL 层ServiceImpl
:封装Mapper
,并为业务提供统一接口
六、最佳实践
- 接口分离:定义 Service 接口,避免直接在 Controller 注入
ServiceImpl
- 扩展业务逻辑:在继承的 ServiceImpl 上增加自己的方法,不要随意修改通用方法
- 批量操作:使用
saveBatch
、updateBatchById
提升性能 - LambdaQueryWrapper:结合
ServiceImpl
的查询方法,写法更简洁安全
七、总结
ServiceImpl
是 MyBatis-Plus 提供的 通用业务实现基类,它:
- 封装了常用 CRUD 逻辑
- 统一了 Service 层接口风格
- 提升了开发效率与代码可维护性