天天看点

领导说谁再直接删数据就滚蛋,悄悄用上MyBatis-Plus逻辑删除

作者:石添的编程哲学
[啤酒]满怀忧思,不如先干再说!专注Java从基础到架构的硬核干货分享!

一个产品甚至于一个企业最重要的就是数据,现在磁盘价格也并不贵,企业租用云服务器的成本也在下降,在很早之前企业就已经选择用户在删除数据时并不会真正的从磁盘上移除,而是逻辑上的一种删除,即用户查询不到,以此来发现和使用数据的未来或者说剩余价值。比如游戏产品中常见的免费删除角色和收费恢复角色[看][看][看]

本文就以mybatis-plus为基础实现逻辑删除

一、准备环境

在介绍mybatis-plus逻辑删除和自动填充时首先搭建项目和准备一张数据表,此案例通过springboot+mysql+mybatis-plus实现

1.1、创建数据表

CREATE TABLE `tb_student` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '学生ID',
  `class_id` int(11) NOT NULL COMMENT '班级编号',
  `dorm_id` int(11) DEFAULT NULL COMMENT '宿舍编号',
  `major_id` int(11) DEFAULT NULL COMMENT '专业id',
  `stu_no` varchar(50) NOT NULL COMMENT '学号',
  `stu_name` varchar(50) NOT NULL COMMENT '学生姓名',
  `stu_sex` char(1) NOT NULL DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
  `stu_mobile` varchar(20) DEFAULT NULL COMMENT '手机号',
  `status` char(1) NOT NULL COMMENT '学生状态(0正常 1请假 2休学 3退学 4毕业)',
  `is_delete` BINARY(1) DEFAULT '0' COMMENT '删除标志(0未删除 1已删除)',
  `create_id` bigint(11) not null COMMENT '创建者',
	`update_id` bigint(63) not null COMMENT '修改者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='学生信息记录';           

1.2、创建maven项目

在IDEA中创建一个maven项目,引入springboot、mysql、mybatis-plus依赖即可

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.7.12</version>
    </parent>
    <groupId>com.stt</groupId>
    <artifactId>stt-mybatis-plus</artifactId>
    <version>1.0-SNAPSHOT</version>
    <description>Mybatis-plus技术栈</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.7.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.32</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
        </dependency>
    </dependencies>

</project>           

1.3、application.yml配置文件

在springboot配置文件中配置数据库

# 数据库配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis-plus?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
    username: root
    password: Shitian123456
# mybatis-plus配置
mybatis-plus:
  configuration:
  	# 配置输出sql到控制台
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl           

1.4、创建实体类

package com.stt.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * @author 石添
 * 学生实体类
 */
@Data
// 映射的数据表名称
@TableName(value = "tb_student")
public class Student implements Serializable {

    @TableId(type = IdType.AUTO)
    private Long id;
    private Integer classId;
    private Integer dormId;
    private Integer majorId;
    private String stuNo;
    private String stuName;
    private Integer stuSex;
    private String stuMobile;
    private Integer status;
    private Integer isDelete;
    private Long createId;
    private Long updateId;
    private LocalDateTime createTime;
    private LocalDateTime updateTime;
    private String remark;

}
           

1.5、创建Mapper

Mapper接口

public interface StudentMapper extends BaseMapper<Student> {
}           

Mapper映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.stt.mapper.StudentMapper">

</mapper>           

1.6、创建Service

service接口

public interface IStudentService extends IService<Student> {
}           

service实现类

@Service
public class StudentServiceImpl extends ServiceImpl<StudentMapper, Student> implements IStudentService {

}           

1.7、springboot项目主类

在该类中注上mapper扫描

@SpringBootApplication
// 扫描mapper
@MapperScan("com.stt.mapper")
public class MyBatisPlusApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyBatisPlusApplication.class,args);
    }
}           

此时准备工作完成,即创建数据表和springboot项目

二、逻辑删除

逻辑删除是为了方便数据恢复和保护数据潜在价值的一种手段,即当用户不需要某一个数据时就希望删除掉,此时有物理删除和逻辑删除两种方案。

  • 物理删除:将数据真实的从数据库【磁盘】中删除掉
  • 逻辑删除:数据并没有真正删除,还在数据库【磁盘】上个保存,只是在查询时过滤掉被删除的数据,对用户来说就是删除掉了

2.1、实现原理

物理删除通知数据库执行删除操作。即删除的sql

-- 删除id为1的学生
DELETE FROM tb_student WHERE id = 1           

逻辑删除的实现方式是在数据表中添加一个表示是否删除的字段,注意该字段不要和数据的状态字段耦合。

比如学生有很多状态:正常,请假,休学,退学。可以使用status字段表示;

而该学生被删除,则可以使用is_delete字段表示,我们暂定0为未删除,1为删除,那么在删除数据时其实就是修改该字段的值了

UPDATE tb_student set is_delete = 1 WHERE id = 2;           

在查询时,就需要带上is_delete字段作为判断条件

-- 查询所有学生
SELECT * FROM tb_student WHERE is_delete = 0;           

在项目中实现原理相同,此时发现一个不方便的问题就是在查询时都要带上is_delete = 0的判断,删除的话修改该字段的值,mybatis-plus提供了逻辑删除的机制

查询:自动追加is_delete = 0的条件,过滤掉已被删除的数据

修改:修改时也会追加is_delete = 0的条件,防止修改已被删除的数据

删除:将删除变为update语句,语句同上述逻辑删除sql

则数据新增时则不需要太过于关心isDelete的值,一般都会在数据表中设置该值的默认值为0

使用方法

使用方法有两种方式,但都需要保障一个前提:

  • 保障数据表中有一个字段标识该数据是否删除,类型无所谓【推荐使用Integer、Boolean】类型,官方还推荐使用LocalDateTime,emmm~~~~

实现方式:

  • 在实体类中的对应字段上添加@TableLogic注解
  • 或者 在application.yml中配置mybatis-plus的逻辑删除

此时数据表和实体类中都已经有一个名为is_delete的字段标识是否删除,当然该字段的名字你可以任意取名,下边就从第二步开始

@TableLogic注解

在实体类中的isDelete字段上添加该注解,该注解有两个属性,意义如下

  • value:表示未删除的值,0代表如果值为0则是没有删除
  • delval:表示已删除的值,1代表该数据已删除
@TableLogic(value = "0",delval = "1")
private Integer isDelete;           

查询测试

@Test
public void search() {
    List<Student> list = studentService.list();
    list.forEach(System.out::println);
}           

执行执行,发现控制台输出的sql中自动拼接上where条件

领导说谁再直接删数据就滚蛋,悄悄用上MyBatis-Plus逻辑删除

删除测试

@Test
public void remove() {
    boolean remove = studentService.removeById(3L);
    if(remove) System.out.println("删除成功");
    else System.out.println("删除失败");
}           

此时的删除sql就变成了update

领导说谁再直接删数据就滚蛋,悄悄用上MyBatis-Plus逻辑删除

application配置方式

还有一种方式就是不使用注解,在application.yml文件中配置

# Mybatis-plus配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      logic-delete-field: isDelete # 全局逻辑删除的实体字段名(since 3.3.0)
    	# 如果你的逻辑删除值和默认值相同则可以不配置以下两项
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)           

两种效果相同,不过推荐使用配置文件的方式,好处在于全局共用,即不需要每个实体类中都在isDelete属性上写@TableLogic注解,前提是你的项目中的逻辑删除规则是一样的哦

注意

也不是说项目中都要使用逻辑删除,是需求而定,逻辑删除有一个致命缺陷在于如果数据量剧增,mysql在单表数据达到300万以上时查询性能就会明显下降,此时可以将逻辑删除的数据统一迁移到另一个表中存储,正常的业务还是使用原表,如果需要用到原来被删除的数据,则可以到另一张表中查询。