前言
一般情況下,我們要删除一條資料,直接使用 delete 即可,就像這樣:delete from user where id = 1,這樣做的好處是:
- 符合我們的了解,删除就是直接删掉嘛。
- 節省資料庫空間,某些情況下資料量較大,且新增和删除比較頻繁時,delete可以幫我們回收很多的空間。
但我今天想講的是邏輯删除,那什麼是邏輯删除呢?
邏輯删除
邏輯删除就是給資料表添加一個固定字段,用該字段的值來表示這條資料目前是否被删除,并把 delete 操作修改為 update 操作。
比如,在我的項目中某些表會有一個固定的 deleted 字段,該字段是 tinyint 型的,其取值隻有 0 和 1 兩種,0表示這條資料未删除,1表示已删除,預設值為 0。
當我要删除某條資料時,我會将這條資料的 deleted 值置為 1,而不會使用 delete 去真正的把它删掉。同時,我的所有 insert 語句和 update 語句都會帶上一個固定的條件 deleted = 0 ,來過濾掉所有在邏輯上被删除的資料。
阿裡巴巴Java編碼規約提出:POJO 類中布爾類型的變量,都不要加 is,否則部分架構解析會引起序列化錯誤。
我原來是用的 is_deleted,現已全部更改為了 deleted。
△圖 / 校園部落格使用者表資料
同樣的,也來講講這樣做的好處:
- 友善資料恢複,保護資料本身的價值。
- 保證資料連續性,對主鍵的影響可能會導緻底層B+樹重建,而 delete 和 update id 都會影響主鍵。
事實上,在大多數公司裡,都會采用邏輯删除的方式,因為資料的價值更大,被删除的資料也非常有記錄價值,這樣的操作也并不會提高太多的操作難度。
使用Mybatis-Plus邏輯删除
如果你的項目使用的是Mybatis-Plus架構來操作資料庫,那你可以通過下面幾個步驟快速的轉變到邏輯删除模式。
- 在MySQL中給那些要改為邏輯删除的表添加一個 deleted 字段,當然也可以叫 flag 或者是别的名字,隻要你喜歡就好。類型 tinyint 就夠了,預設值最好也設定一下。
- -- 你也可以直接使用這條修改語句,記得把表名進行替換 ALTER TABLE user ADD `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語句,那你需要自己對他們進行調整:
- 将原有的 delete 語句統一修改為 update
- 将原有的 select、update 語句都加一個過濾條件 deleted = 0
- insert 語句,如果你在表上設定了預設值的話,則可以不用管它。如果你沒有設定預設值,那我建議你還是設定一下。
好了就這麼簡單,你甚至不需要修改你的業務代碼,因為Mybatis-Plus已經幫你處理好了。
它做了什麼
- 當你調用userMapper.deleteById(1)的時候,實際上傳到MySQL的代碼是這樣的:
- update user set deleted = 1 where id = 1 and deleted = 0 複制代碼
- 當你通過QueryWrapper查詢資料或者通過 UpdateWrapper 更新資料的時候,它也會自動幫你添加過濾條件:
- select * from user where deleted = 0 複制代碼
- 但如果你是在xml中直接寫的SQL語句,那它是不會幫你進行修改的,比如我寫的SQL是這樣的:
- <update id="deleteById"> update user set deleted = ${id} where id = ${id} </update> 複制代碼
- 執行出來的SQL是這樣的:
- 很顯然它并沒有幫我加上 deleted = 0 ,這是使用者需要注意的。
當然我隻是簡單的對Mybatis-Plus的邏輯删除功能用法進行了簡單講解,如果你需要的話,也可以參考一下官方文檔的說明:邏輯删除 | MyBatis-Plus (baomidou.com) (雖然它寫的也比較簡潔)
注意
雖然把項目過渡到邏輯删除并不太費事,但它也有一些其他需要注意的點。
首先是使用理念上,我這裡直接引用 Mybatis-Plus 的說法:
- 邏輯删除是為了友善資料恢複和保護資料本身價值等等的一種方案,但實際就是删除。
- 如果你需要頻繁查出來看就不應使用邏輯删除,而是以一個狀态去表示。
其次是使用邏輯上,對于MySQL而言,邏輯删除會導緻唯一索引(UNIQUE KEY)的異常。
- 原因很簡單,已經删除的資料仍然存在,當再次插入一條同樣的資料時,就會抛出異常。
- 比如在我的 user 表中 username 字段設定了 UNIQUE KEY ,我先插入一條username = 阿杆的資料,再把這條資料邏輯删除掉,然後再重新插入一條username = 阿杆的資料。
- 那麼理論上來說此時是應該允許插入的,但由于我使用了邏輯删除,MySQL不允許存在兩條資料出現同樣的 username = 阿杆的場景,此時就出現了異常。
- 當然,邏輯删除與唯一索引的沖突是可以解決的,解決方案也不難。
- 我們可以在原來的唯一索引裡加上deleted字段,同時再删除資料的時候把deleted修改為表id,這樣就可以保證未删除的資料不會出現重複值了,而且不會受到已删除資料的影響。但你要記得重寫SQL方法,不然Mybatis-Plus還是會幫你修改為配置檔案裡的那個預設值。
- UNIQUE KEY `username` (`username`,`deleted`) USING BTREE 複制代碼
- 方案不唯一,你也可以用别的方法,或者在這張表上不使用邏輯删除,畢竟,沒有最好的,隻有最合适的。
寫在後面的一些話
本文是我在使用Mybatis-Plus将項目過渡到邏輯删除時寫的,也算是剛接觸邏輯删除這個東西,可能會有一些考慮不周全的地方,希望各位大佬在評論區提出。
作者:阿杆
連結:https://juejin.cn/post/7189059869020782651