Lecture11-Service层与链式操作

文章目录

    [toc]

    单词词性音标含义
    Servicen./ˈsɜːvɪs/服务(Service 层承载业务逻辑,接口继承 IService 获得通用业务方法)
    Batchn./bætʃ/批量(saveBatch 批量插入,updateBatchById 批量更新)
    Chainn./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

    方式代码
    手动 wrapperLambdaQueryWrapper 单独创建 → 拼接条件 → 传给 list(wrapper)
    链式操作lambdaQuery() 开头,条件直接跟在后面,结尾方法收口

    两种写法效果完全一样,链式操作代码更短、更直观,推荐优先使用。


    七、IService 与 BaseMapper 的分工

    对比项BaseMapper(Mapper 层)IService(Service 层)
    定位单表数据访问业务方法 + 批量 + 链式
    返回类型int / 对象 / Listboolean / 对象 / 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 层

    下一章综合实战:用全部知识点做一个完整的员工管理模块。

    end