[toc]
| 单词 | 词性 | 音标 | 含义 |
|---|---|---|---|
| Service | n. | /ˈsɜːvɪs/ | 服务(Service 层承载业务逻辑,接口继承 IService 获得通用业务方法) |
| Batch | n. | /bætʃ/ | 批量(saveBatch 批量插入,updateBatchById 批量更新) |
| Chain | n. | /tʃeɪn/ | 链(lambdaQuery() 链式查询:一行代码完成 条件+查询) |
Lecture11-Service层与链式操作
一、为什么需要 Service 层
到目前为止,我们直接在测试类里调 Mapper。真实项目中代码是分层的:
- Controller 层:接收请求、返回 JSON
- Service 层:写业务逻辑(判断、组合、事务)
- Mapper 层:只做单表数据访问
业务逻辑(比如"发工资前先检查余额""删除部门前先检查有没有员工")都写在 Service 层。MyBatis-Plus 为 Service 层提供了现成的 IService 接口 + ServiceImpl 实现类,常见业务方法开箱即用,还带批量操作和链式调用。
二、创建 Service
1、接口:继承 IService
package com.atguigu.mp.service;
import com.atguigu.mp.entity.Employee;
import com.baomidou.mybatisplus.extension.service.IService;
public interface EmployeeService extends IService<Employee> {
// 自定义业务方法可以在这里声明(以后补充)
}
2、实现类:继承 ServiceImpl
package com.atguigu.mp.service.impl;
import com.atguigu.mp.entity.Employee;
import com.atguigu.mp.mapper.EmployeeMapper;
import com.atguigu.mp.service.EmployeeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
// 继承 ServiceImpl 后,IService 的全部方法都已实现
}
泛型两个参数:
ServiceImpl<EmployeeMapper, Employee>第一个是 Mapper,第二个是实体。
三、IService 常用方法
| 分类 | 方法 | 作用 |
|---|---|---|
| 新增 | save(entity) | 插入一条 |
| 新增 | saveBatch(集合) | 批量插入 |
| 新增/修改 | saveOrUpdate(entity) | 未指定主键,则执行insert 指定了主键: ●主键对应的数据存在:执行update ●主键对应的数据不存在:执行insert |
| 删除 | removeById(id) | 按主键删除(逻辑删除自动生效) |
| 删除 | removeByIds(集合) | 批量删除 |
| 删除 | remove(wrapper) | 按条件删除 |
| 修改 | updateById(entity) | 按主键更新 |
| 修改 | updateBatchById(集合) | 批量更新 |
| 查询 | getById(id) | 按主键查一个 |
| 查询 | getOne(wrapper) | 按条件查一个 |
| 查询 | list() | 查询全部 |
| 查询 | list(wrapper) | 按条件查列表 |
| 查询 | count(wrapper) | 统计数量 |
| 分页 | page(page, wrapper) | 分页查询 |
| 链式查询 | lambdaQuery() | 一行代码完成条件+查询(重点) |
| 链式更新 | lambdaUpdate() | 一行代码完成条件+更新(重点) |
四、基本用法示例
@SpringBootTest
public class EmployeeServiceTest {
@Autowired
private EmployeeService employeeService;
// 查询全部
@Test
public void testList() {
employeeService.list().forEach(System.out::println);
}
// 批量插入
@Test
public void testSaveBatch() {
List<Employee> list = new ArrayList<>();
for (int i = 1; i <= 3; i++) {
Employee employee = new Employee();
employee.setEmpNo("No.Batch" + i);
employee.setEmpName("批量员工" + i);
employee.setDeptId(1);
list.add(employee);
}
boolean saved = employeeService.saveBatch(list);
System.out.println("saved = " + saved);
}
// 按条件查一个
@Test
public void testGetOne() {
Employee employee = employeeService.getOne(
new LambdaQueryWrapper<Employee>().eq(Employee::getEmpNo, "No.10001"));
System.out.println("employee = " + employee);
}
}
注意:Service 方法的返回类型大多是
boolean(成功/失败),和 Mapper 的int(影响行数)不同。
五、链式操作:一行代码完成查询/更新
这是 Service 层最方便的用法——不用 new wrapper,直接链式写。
1、链式查询 lambdaQuery()
// 查询:部门 1、姓名含「张」、按工资降序
List<Employee> employeeList = employeeService.lambdaQuery()
.eq(Employee::getDeptId, 1)
.like(Employee::getEmpName, "张")
.orderByDesc(Employee::getEmpSalary)
.list(); // 以 list() 结尾:查询列表
其他结尾方式:
// 查一个
Employee one = employeeService.lambdaQuery()
.eq(Employee::getEmpNo, "No.10001")
.one();
// 统计数量
Long count = employeeService.lambdaQuery()
.eq(Employee::getDeptId, 1)
.count();
// 只查指定列
List<Employee> names = employeeService.lambdaQuery()
.select(Employee::getEmpName)
.list();
2、链式更新 lambdaUpdate()
// 部门 1 的所有员工工资统一改成 8500(固定值)
boolean updated = employeeService.lambdaUpdate()
.set(Employee::getEmpSalary, new BigDecimal("8500.00"))
.eq(Employee::getDeptId, 1)
.update();
// 部门 1 的所有员工工资在原值基础上 +500(基于原值计算,用 setSql)
boolean updated = employeeService.lambdaUpdate()
.setSql("emp_salary = emp_salary + 500")
.eq(Employee::getDeptId, 1)
.update();
// 按主键更新个别字段:先 set 再 eq(id)
boolean updated = employeeService.lambdaUpdate()
.set(Employee::getPhoneNum, "13900000002")
.eq(Employee::getEmpId, 1)
.update();
习惯之后,业务代码可以写得很紧凑:
employeeService.lambdaQuery()...list()就是一条"动态查询",不用再单独 new wrapper。
六、链式操作 vs 手动 wrapper
| 方式 | 代码 |
|---|---|
| 手动 wrapper | LambdaQueryWrapper 单独创建 → 拼接条件 → 传给 list(wrapper) |
| 链式操作 | lambdaQuery() 开头,条件直接跟在后面,结尾方法收口 |
两种写法效果完全一样,链式操作代码更短、更直观,推荐优先使用。
七、IService 与 BaseMapper 的分工
| 对比项 | BaseMapper(Mapper 层) | IService(Service 层) |
|---|---|---|
| 定位 | 单表数据访问 | 业务方法 + 批量 + 链式 |
| 返回类型 | int / 对象 / List | boolean / 对象 / List |
| 批量操作 | 无现成方法 | saveBatch / updateBatchById |
| 链式调用 | 无 | lambdaQuery() / lambdaUpdate() |
| 业务逻辑 | 不适合写 | 适合写 |
实际分层写法:Controller 调 Service,Service 调 Mapper。Service 里的通用 CRUD 用 IService 自带方法,特殊 SQL 再在 Mapper 里补。
八、本章小结
- 接口继承
IService<T>,实现类继承ServiceImpl<Mapper, T>,通用方法全都有 - 批量操作用
saveBatch/updateBatchById - 链式操作
lambdaQuery()...list()/lambdaUpdate()...update()一行搞定 - 分层:Controller → Service → Mapper,业务逻辑写在 Service 层
下一章综合实战:用全部知识点做一个完整的员工管理模块。