所谓逻辑删除是指数据已经“不须要”了,可是并无使用delete语句将这些数据真实的从数据库中删除,而只是用一个标志位将其设置为已经删除。java
对数据进行逻辑删除,通常存在如下缘由:sql
对数据进行逻辑删除,能够保证数据的安全性和完整性。可是,逻辑删除也会带来的一些问题:数据库
因此是否须要对数据进行逻辑删除,须要根据具体的业务场景,以及逻辑删除的优缺点进行综合考虑。api
网友的一些建议安全
综合考虑,对于中小型的项目,逻辑删除所带来的好处有限,但带来的问题却不少。若是平时作好数据备份工做,仍是能够预防物理删除隐患的。但内心应该清除,当项目大到必定程度,对数据安全性的要求高到必定程度,使用逻辑删除代替物理删除是必然的,在后面的数据库设计中,能够先小范围的尝试使用逻辑删除,一旦开发模式成熟,就全面使用逻辑删除代替物理删除。mybatis
设计方案一:在表中加一个字段deleted字段并发
deleted字段的值为0表示数据未删除,值为1表示数据已经删除。app
插入数据数据时,这个值默认为0。删除数据时将这个值设置为1。查询和更新数据时都将‘deleted=0’这个条件带上,只查询和更新没有删除的数据。数据库设计
这个方案比较简单,可是会有些问题。好比说你表中的一个字段user_name
设置了惟一性约束,可是若是你只是进行了逻辑删除的话,相同的user_name
就不能进行数据插入了。ide
但若是不将该字段设置为惟一性约束的,那么在每次插入数据的时候,都需先进行一次查询,看看有无未(逻辑)删除的同名记录存在,低效率是一回事,并且在高并发的系统中,很难保证其正确性。
然而你的服务运行了一段时间后你仍是发现了数据库中存在 name = a 且 is_delete = 0 的多条字段,大部分是因为如下缘由(并发问题):
这个问题有下面两个解决方案:
解决方案1:为数据库添加新的一列delete_token,当某一条记录须要删除时,将该字段设置为一个UUID,将name、delete_token设置为惟一键,这样当is_delete=0时,delete_token保持一个默认值,可以有效地限制name惟一,当记录被删除时,因为delete_token是一个惟一的UUID,便能保证删除的记录不会被惟一约束束缚。但正如该文章的博主所说,UUID会占用很大的空间,因此不推荐使用。评论网友针对该问题提出优化对策:将删除记录的delete_token设置为该记录的id。
我的认为,索引太大只是其中一个弊端,该方法还会面临一个很棘手的问题:当须要批量删除时,须要对每一条记录进行逐行删除。例如该表还有一个字段叫age,如今须要删除age > 18的记录,共有50条,在业务中,因为须要为每条的delete_token字段插入一个UUID因此须要将其拆分为50条更新操做来进行。这样的代价显然很难接受。
解决方案2:将删除标记设置默认值(例如0),将惟一字段与删除标记添加惟一键约束。当某一记录须要删除时,将删除标记置为NULL。
因为NULL不会和其余字段有组合惟一键的效果,因此当记录被删除时(删除标记被置为NULL时),解除了惟一键的约束。此外该方法能很好地解决批量删除的问题(只要置为NULL就完事了),消耗的空间也并很少(1位 + 联合索引)。
设计方案一:表备份
将删除的数据备份到其余备份表再进行删除。若是有级联数据,也须要进行删除备份。否则数据的完整性就不存在了。
这边,咱们使用MyBatis-Plus的逻辑删除功能来实现下上面介绍的方案一。
MyBatis-Plus(简称MP)是对MyBatis的加强,能够彻底兼容MyBatis的原生功能,并且几乎能够省略单表操做的全部增删改查方法,大大提高了开发效率。详细的使用方式能够参考官网
下面就来介绍下,MP的逻辑删除功能。
step1:进行配置
mybatis-plus: global-config: db-config: # 全局逻辑删除的实体字段名(since 3.3.0,配置后能够忽略不配置步骤2) # logic-delete-field: flag # 逻辑已删除值(默认为 1) logic-delete-value: 1 # 逻辑未删除值(默认为 0) logic-not-delete-value: 0
step2: 添加注解
@TableLogic() @TableField(select = false) private Integer deleted;
step3: 使用
@Test public void apiTest(){ // UPDATE test.user SET deleted=1 WHERE user_id=? AND deleted=0 logger.info("开始逻辑删除"); int count = userDAO.deleteById(356); // SELECT * FROM test.user WHERE user_id=? AND deleted=0 logger.info("开始查询"); User user = userDAO.selectById(357); // UPDATE test.user SET user_name=?, telephone_no=?, id_card_no=?, identity_type=?, sex=?, birth_date=?, marital_status=?, asset_code=?, asset_branch_code=?, issuing_authority=?, job_type=?, address=?, work_unit=?, create_time=? WHERE user_id=? AND deleted=0 logger.info("开始更新"); userDAO.updateById(user); }
MP的逻辑删除功能使用起来很是简单。可是须要咱们注意如下几点:
deleted=0
,也就是只对没有删除的数据进行操做;deleted
字段作默认限制,默认为0(未删除),插入数据时这个值能够不用设置;下面是使用 QueryWrapper
deleted=0
条件会让后面咱们本身加的deleted条件失效
SELECT * FROM test.user WHERE deleted=0 AND (user_id = ? AND deleted = ? AND user_name = ?)