Lecture09-逻辑删除与字段自动填充

文章目录

    [toc]

    单词词性音标含义
    Logicn./ˈlɒdʒɪk/逻辑(@TableLogic 逻辑删除:删除操作改为给字段打标记,不真正删数据)
    Deletev./dɪˈliːt/删除(逻辑删除后,查询自动过滤已删除数据)
    Fillv./fɪl/填充(@TableField(fill=...) 让框架自动为字段赋值,如创建时间)
    Handlern./ˈhændlə(r)/处理器(MetaObjectHandler 负责在插入/更新时给字段赋值)

    Lecture09-逻辑删除与字段自动填充

    一、逻辑删除

    1、什么是逻辑删除

    现实业务中,很多数据不能真的删掉:

    • 订单删了,财务对账就没了依据
    • 用户删了,历史关联数据会乱
    • 删错了无法恢复

    所以有了「逻辑删除」:不执行 DELETE,而是给记录打一个删除标记(比如 deleted=1 表示已删除)。查询时自动过滤掉标记为已删除的数据,看起来就像删了一样,但数据还在表里。

    本质上是把 DELETE 语句改成 UPDATE 语句,执行逻辑删除时只是把指定字段从 0 改成 1

    MyBatis-Plus 对逻辑删除做了全自动支持:加一个注解、配两行配置,删除、查询全部自动处理。


    2、准备:给表加 deleted 字段

    ALTER TABLE t_emp ADD COLUMN is_deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除标记:0未删除,1已删除';
    

    3、实体类加注解

    @Data
    @TableName("t_emp")
    public class Employee {
    
        @TableId(type = IdType.AUTO)
        private Integer empId;
    
        // ...其他字段
    
        @TableLogic          // 逻辑删除字段
        private Integer isDeleted;
    
    }
    

    4、效果演示

    ①删除变成了更新

    @Test
    public void testLogicDelete() {
        int rows = employeeMapper.deleteById(4);
        System.out.println("rows = " + rows);
    }
    

    控制台日志——不再是 DELETE,而是 UPDATE:

    ==>  Preparing: UPDATE t_emp SET deleted=1 WHERE emp_id=? AND deleted=0
    ==> Parameters: 4(Integer)
    

    ②查询自动过滤

    @Test
    public void testSelectAfterDelete() {
        List<Employee> employeeList = employeeMapper.selectList(null);
        employeeList.forEach(System.out::println);
    }
    
    ==>  Preparing: SELECT emp_id,emp_no,emp_name,emp_salary,emp_birthday,phone_num,dept_id FROM t_emp WHERE is_deleted=0
    

    emp_id=4 的员工(刚才逻辑删除的)不会出现在结果里。框架自动加了 AND is_deleted=0。

    效果总结:逻辑删除后,deleteById、selectList、updateById 等所有方法都会自动带上 is_deleted=0 条件——完全不用我们操心。


    5、补充:全局配置(推荐)

    除了在实体类上加 @TableLogic,也可以在配置文件中统一声明逻辑删除字段,两种方式可以同时存在:

    mybatis-plus.global-config.db-config.logic-delete-field=isDeleted
    mybatis-plus.global-config.db-config.logic-delete-value=1
    mybatis-plus.global-config.db-config.logic-not-delete-value=0
    

    实际项目中通常「全局配置 + 实体注解」双保险。本教程演示用实体注解就够。


    二、字段自动填充

    1、问题

    create_time(创建时间)、update_time(更新时间)这两个字段几乎每张表都有。如果每次都手动 set,代码啰嗦还容易忘。MyBatis-Plus 提供字段自动填充:插入时自动填创建时间,更新时自动填更新时间。


    2、准备:给表加时间字段

    ALTER TABLE t_emp
        ADD COLUMN create_time DATETIME COMMENT '创建时间',
        ADD COLUMN update_time DATETIME COMMENT '更新时间';
    

    3、实体类字段声明填充策略

    @Data
    @TableName("t_emp")
    public class Employee {
    
        @TableId(type = IdType.AUTO)
        private Integer empId;
    
        // ...其他字段
    
        @TableLogic
        private Integer deleted;
    
        @TableField(fill = FieldFill.INSERT)          // 插入时自动填充
        private LocalDateTime createTime;
    
        @TableField(fill = FieldFill.INSERT_UPDATE)   // 插入和更新时都自动填充
        private LocalDateTime updateTime;
    
    }
    

    FieldFill 取值:

    取值触发时机
    FieldFill.INSERT插入时填充
    FieldFill.UPDATE更新时填充
    FieldFill.INSERT_UPDATE插入和更新时都填充
    FieldFill.DEFAULT不填充(默认)

    4、编写填充处理器

    实现 MetaObjectHandler 接口,告诉框架「填充什么值」:

    package com.atguigu.mp.config;
    
    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;
    
    import java.time.LocalDateTime;
    
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
        @Override
        public void insertFill(MetaObject metaObject) {
            // 插入时:createTime 和 updateTime 都填当前时间
            this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
            this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            // 更新时:只填 updateTime
            this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        }
    
    }
    

    注意:strictInsertFill 只在字段值为 null 时才填充,避免覆盖我们手动设置的值。


    5、效果演示

    @Test
    public void testAutoFill() {
        Employee employee = new Employee();
        employee.setEmpNo("No.10006");
        employee.setEmpName("周八");
        employee.setEmpSalary(new BigDecimal("7000.00"));
        employee.setEmpBirthday(new Date());
        employee.setPhoneNum("13511110006");
        employee.setDeptId(1);
    
        // 插入:不手动设置 createTime / updateTime
        employeeMapper.insert(employee);
    
        // 插入后自动填充了
        System.out.println("createTime = " + employee.getCreateTime());
        System.out.println("updateTime = " + employee.getUpdateTime());
    }
    

    控制台日志:

    ==>  Preparing: INSERT INTO t_emp ( emp_no, emp_name, emp_salary, emp_birthday, phone_num, dept_id, create_time, update_time, deleted ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ? )
    ==> Parameters: No.10006(String), ..., 2026-09-19T10:30:00(LocalDateTime), 2026-09-19T10:30:00(LocalDateTime), 0(Integer)
    

    再调用一次 updateById,观察 update_time 自动更新、create_time 保持不变。


    三、本章小结

    • 逻辑删除:@TableLogic + 配置,删除变更新、查询自动过滤,数据不真删
    • 自动填充:@TableField(fill = ...) 声明策略 + 实现 MetaObjectHandler 提供值
    • 常用组合:deleted(逻辑删除)+ create_time / update_time(自动填充),几乎是每张表的标配
    • 两个功能都是"配一次,全表生效",这正是 MyBatis-Plus 提升效率的典型场景

    下一章学习乐观锁,解决并发更新数据丢失的问题。

    end