在開發應用程序時,我們經常會遇到需要刪除數據的場景。但直接從數據庫中物理刪除(DELETE?)數據有時并非最佳選擇。為什么呢?
- 數據恢復: 一旦物理刪除,數據通常難以恢復,誤操作可能導致災難性后果。
- 審計追蹤: 很多業務場景需要保留操作記錄,包括刪除記錄。
- 數據關聯: 物理刪除可能破壞與其他表的關聯關系,導致數據不一致或需要復雜的級聯刪除策略。
這時,邏輯刪除就成了一個非常優雅的解決方案。邏輯刪除并非真的從數據庫移除數據,而是通過一個特定的字段(例如 deleted?、is_deleted?)來標記記錄的狀態,將其標記為“已刪除”。在應用程序層面,這些被標記的數據通常表現為“不存在”,但在數據庫層面,它們仍然保留著。
手動實現邏輯刪除通常意味著:
- 將所有的 DELETE? 操作改為 UPDATE ... SET deleted = 1 ...?。
- 在所有的 SELECT? 查詢中,手動添加 WHERE deleted = 0? 的條件。
這無疑會增加大量重復代碼,且容易遺漏,維護起來也相當痛苦。幸運的是,強大的 MyBatis-Plus (MP) 框架為我們提供了 @TableLogic? 注解,讓實現邏輯刪除變得極其簡單和自動化。
什么是 @TableLogic??
?@TableLogic? 是 MyBatis-Plus 提供的一個注解,用于標記實體類中代表邏輯刪除狀態的字段。一旦配置完成,MP 會在底層通過攔截器自動處理邏輯刪除相關的 SQL 轉換。
如何使用 @TableLogic??
使用 @TableLogic? 非常簡單,只需三步:
第一步:數據庫表設計
在需要支持邏輯刪除的表中,添加一個用于標記刪除狀態的字段。通常使用 TINYINT?、INT? 或 BOOLEAN? 類型。例如,我們添加一個 deleted? 字段:
ALTER TABLE `your_table_name`
ADD COLUMN `deleted` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '邏輯刪除標記(0:未刪除; 1:已刪除)';-- 為該字段添加索引通常是個好主意,特別是當已刪除數據量較大時
ALTER TABLE `your_table_name` ADD INDEX `idx_deleted` (`deleted`);
第二步:實體類配置
在對應的 Java 實體類中,添加該字段,并使用 @TableLogic? 注解標記它:
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;@Data
@TableName("your_table_name")
public class YourEntity {private Long id;private String name;// ... 其他字段@TableLogic // 標記為邏輯刪除字段private Integer deleted; // 字段類型與數據庫對應
}
- ?@TableLogic? 注解本身就足以啟用邏輯刪除功能。
第三步:全局配置 (application.yml/properties)
雖然 @TableLogic? 注解本身已經能工作,但最佳實踐是在項目的配置文件中指定全局的“未刪除值”和“已刪除值”。這樣 MP 就知道在生成 SQL 時應該用哪些具體的值。
mybatis-plus:global-config:db-config:# 邏輯刪除字段名(如果實體類中字段名就是這個,可以省略,但推薦寫上保持清晰)# 注意:即使配置了全局字段名,實體類中仍然需要 @TableLogic 注解來 *標識* 哪個字段是邏輯刪除字段logic-delete-field: deleted# 邏輯已刪除值(默認為 1)logic-delete-value: 1# 邏輯未刪除值(默認為 0)logic-not-delete-value: 0
如果你的邏輯刪除字段名、已刪除值、未刪除值與全局配置不同,也可以在 @TableLogic? 注解中單獨指定:
// 假設用 status 字段,0表示正常,-1表示刪除
@TableLogic(value = "0", delval = "-1")
private Integer status;
但通常建議保持全局一致性。
@TableLogic? 如何工作?
配置完成后,MP 內置的 LogicSqlInnerInterceptor?(需要通過 MybatisPlusInterceptor? 注冊)就會自動生效:
-
對于刪除操作: 當你調用 BaseMapper? 提供的標準刪除方法時,例如:
yourMapper.deleteById(1L); // 或者 yourMapper.removeById(1L);
MP 不會執行物理 DELETE?。它會自動將 SQL 轉換為:
UPDATE your_table_name SET deleted = 1 WHERE id = 1 AND deleted = 0;
它只會將當前狀態為“未刪除”(deleted = 0?) 的記錄標記為“已刪除”(deleted = 1?)。
-
對于查詢操作: 當你調用 BaseMapper? 提供的所有標準查詢方法時,例如:
YourEntity entity = yourMapper.selectById(1L); List<YourEntity> list = yourMapper.selectList(null); // 查詢所有 IPage<YourEntity> page = yourMapper.selectPage(pageParam, wrapper); // ...等等
MP 會自動在這些查詢的 WHERE? 子句后面(或 WHERE? 子句內,如果沒有其他條件)追加邏輯刪除的過濾條件:
-- selectById(1L) 可能的 SQL SELECT id, name, deleted /*, ...*/ FROM your_table_name WHERE id = 1 AND deleted = 0;-- selectList(null) 可能的 SQL SELECT id, name, deleted /*, ...*/ FROM your_table_name WHERE deleted = 0;-- selectPage(page, wrapper) 可能的 SQL (假設 wrapper 有其他條件) SELECT id, name, deleted /*, ...*/ FROM your_table_name WHERE name LIKE '%test%' AND deleted = 0 LIMIT ?, ?;
這意味著,通過標準方法查詢時,你永遠只會得到未被邏輯刪除的數據。
如何查詢包含已刪除的數據?
這是一個常見需求,比如在后臺管理界面需要查看回收站內容,或者進行數據恢復操作。
既然 MP 的標準查詢方法會自動添加 AND deleted = 0? 的限制,那么要查詢包含已刪除的數據(甚至只查詢已刪除的數據),我們就必須繞過這個自動行為。
方法就是:使用自定義 SQL (在 Mapper XML 文件中)。
-
在 Mapper 接口定義方法:
public interface YourMapper extends BaseMapper<YourEntity> {// 查詢指定 ID 的記錄,無論其刪除狀態YourEntity selectByIdIncludeDeleted(@Param("id") Long id);// 查詢所有已刪除的記錄List<YourEntity> selectDeletedList(); }
-
在 Mapper XML 文件中編寫 SQL:
<mapper namespace="com.yourpackage.mapper.YourMapper"><resultMap id="BaseResultMap" type="com.yourpackage.domain.YourEntity"><!-- ... 字段映射 ... --><result column="deleted" property="deleted"/></resultMap><select id="selectByIdIncludeDeleted" resultMap="BaseResultMap">SELECT id, name, deleted /*, ...*/FROM your_table_nameWHERE id = #{id}<!-- 注意:這里我們沒有寫 deleted = 0 條件,MP 也不會自動加 --></select><select id="selectDeletedList" resultMap="BaseResultMap">SELECT id, name, deleted /*, ...*/FROM your_table_nameWHERE deleted = 1 <!-- 手動指定查詢已刪除的數據 --></select> </mapper>
關鍵在于:對于 XML 中手寫的 SQL,MP 的邏輯刪除攔截器不會去修改它,也不會自動追加 deleted? 條件。你可以完全控制查詢邏輯。
@TableLogic? 的優勢
- 代碼簡潔: 無需在業務代碼中重復編寫 deleted = 0? 的查詢條件和 UPDATE? 式的刪除邏輯。
- 不易出錯: 自動化處理減少了手動編碼時遺漏條件的風險。
- 全局一致: 保證了整個應用處理邏輯刪除行為的一致性。
- 低侵入性: 對業務代碼的侵入非常小,主要通過注解和配置完成。
注意事項
- 性能: 如果邏輯刪除的數據量非常大,查詢性能可能會受到影響。確保在邏輯刪除字段上建立了索引。考慮定期歸檔或物理清除不再需要的已刪除數據。
- 唯一約束: 如果表中有唯一約束,邏輯刪除可能導致問題(例如,用戶注銷賬號后,其郵箱被標記為刪除,但新用戶無法使用該郵箱注冊,因為唯一約束仍然存在)。需要根據業務場景設計合適的處理策略。
總結
MyBatis-Plus 的 @TableLogic? 為 Java 應用實現邏輯刪除提供了一個極其便捷和強大的解決方案。它通過簡單的注解和配置,將開發者從繁瑣、重復的 SQL 處理中解放出來,讓我們能更專注于業務邏輯本身。掌握并使用好 @TableLogic?,無疑能提升代碼質量和開發效率,構建更健壯、更易于維護的應用程序。