
一般情况下,我们要删除一条数据,直接使用 delete 即可,就像这样:delete from user where id = 1,这样做的好处是:
但我今天想讲的是逻辑删除,那什么是逻辑删除呢?
逻辑删除就是给数据表添加一个固定字段,用该字段的值来表示这条数据当前是否被删除,并把 delete 操作修改为 update 操作。
比如,在我的项目中某些表会有一个固定的 is_deleted 字段,该字段是 tinyint 型的,其取值只有 0 和 1 两种,0表示这条数据未删除,1表示已删除,默认值为 0。
当我要删除某条数据时,我会将这条数据的 is_deleted 值置为 1,而不会使用 delete 去真正的把它删掉。同时,我的所有 insert 语句和 update 语句都会带上一个固定的条件 is_deleted = 0 ,来过滤掉所有在逻辑上被删除的数据。

△图 / 校园博客用户表数据
同样的,也来讲讲这样做的好处:
事实上,在大多数公司里,都会采用逻辑删除的方式,因为数据的价值更大,被删除的数据也非常有记录价值,这样的操作也并不会提高太多的操作难度。
如果你的项目使用的是Mybatis-Plus框架来操作数据库,那你可以通过下面几个步骤快速的转变到逻辑删除模式。
在MySQL中给那些要改为逻辑删除的表添加一个 is_deleted 字段,当然也可以叫 flag 或者是别的名字,只要你喜欢就好。类型 tinyint 就够了,默认值最好也设置一下。
-- 你也可以直接使用这条修改语句,记得把表名进行替换
ALTER TABLE user ADD `is_deleted` tinyint UNSIGNED NOT NULL DEFAULT 0 COMMENT '0为未删除,1为已删除';
在你的Java代码中给刚刚修改过的表的实体类添加对应的属性:
public class User {// 添加isDeleted字段Integer isDeleted;
}
在项目的配置文件(application.yml)当中添加对应的配置:
mybatis-plus:global-config:db-config:logic-delete-field: isDeleted # 全局逻辑删除的实体字段名logic-delete-value: 1 # 逻辑已删除值(默认为 1)logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
如果你的Mybatis-Plus版本在 3.3.0 以下,那你还需要在实体类的字段上添加 @TableLogic 注解:
@TableLogic
Integer isDeleted;
如果你的项目中有通过xml或者@Update、@Select等注解编写的SQL语句,那你需要自己对他们进行调整:
is_deleted = 0好了就这么简单,你甚至不需要修改你的业务代码,因为Mybatis-Plus已经帮你处理好了。
当你调用userMapper.deleteById(1)的时候,实际上传到MySQL的代码是这样的:
update user set is_deleted = 1 where id = 1 and is_deleted = 0
当你通过QueryWrapper查询数据或者通过 UpdateWrapper 更新数据的时候,它也会自动帮你添加过滤条件:
select * from user where is_deleted = 0
但如果你是在xml中直接写的SQL语句,那它是不会帮你进行修改的,比如我写的SQL是这样的:
update userset is_deleted = ${id}where id = ${id}
执行出来的SQL是这样的:

很显然它并没有帮我加上 is_deleted = 0 ,这是使用者需要注意的。
当然我只是简单的对Mybatis-Plus的逻辑删除功能用法进行了简单讲解,如果你需要的话,也可以参考一下官方文档的说明:逻辑删除 | MyBatis-Plus (baomidou.com) (虽然它写的也比较简洁👨💻)
虽然把项目过渡到逻辑删除并不太费事,但它也有一些其他需要注意的点。
首先是使用理念上,我这里直接引用 Mybatis-Plus 的说法:
其次是使用逻辑上,对于MySQL而言,逻辑删除会导致唯一索引(UNIQUE KEY)的异常。
原因很简单,已经删除的数据仍然存在,当再次插入一条同样的数据时,就会抛出异常。
比如在我的 user 表中 username 字段设置了 UNIQUE KEY ,我先插入一条username = 阿杆的数据,再把这条数据逻辑删除掉,然后再重新插入一条username = 阿杆的数据。
那么理论上来说此时是应该允许插入的,但由于我使用了逻辑删除,MySQL不允许存在两条数据出现同样的 username = 阿杆的场景,此时就出现了异常。
当然,逻辑删除与唯一索引的冲突是可以解决的,解决方案也不难。
我们可以在原来的唯一索引里加上is_deleted字段,同时再删除数据的时候把is_deleted修改为表id,这样就可以保证未删除的数据不会出现重复值了,而且不会受到已删除数据的影响。但你要记得重写SQL方法,不然Mybatis-Plus还是会帮你修改为配置文件里的那个默认值。
UNIQUE KEY `username` (`username`,`is_deleted`) USING BTREE
方案不唯一,你也可以用别的方法,或者在这张表上不使用逻辑删除,毕竟,没有最好的,只有最合适的。
本文是我在使用Mybatis-Plus将项目过渡到逻辑删除时写的,也算是刚接触逻辑删除这个东西,可能会有一些考虑不周全的地方,希望各位大佬在评论区提出。
另外,如果你想找一个项目参考一下逻辑删除的具体代码或者过渡方案,可以来看看我的校园博客项目,也可以看看我过渡时修改的代码,这几个PR就是我修改的全部代码了:
也欢迎大家来参与项目贡献或者star😋,项目地址:https://github.com/stick-i/scblogs
