所謂邏輯刪除是指數據已經「不須要」了,可是並無使用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 = ?)