在日常开发中,删除操作是再常见不过的需求。
直接DELETE FROM table
真的是最优解吗?
本文带你深入理解 逻辑删除 与 物理删除 的适用场景,并手把手教你如何在 MyBatis-Plus 中正确实现逻辑删除 ✅
一、删除方式对比:逻辑删除 vs 物理删除
类型 | 含义 | 是否可恢复 | 适用场景 |
---|---|---|---|
逻辑删除 | 标记数据为“已删除”,不真正移除 | ✅ 可恢复 | 数据需保留、关联性强、审计要求高 |
物理删除 | 从数据库中彻底删除记录 | ❌ 不可恢复 | 数据无关联、临时数据、归档清理 |
二、何时使用逻辑删除?——以商品分类为例 🛍️
假设我们有一个商品分类表 category
,当需要删除某个分类时,必须考虑以下两个问题:
1️⃣ 是否有商品正在使用该分类?
- ✅ 如果有商品使用该分类:
- 不能直接删除(无论是逻辑还是物理);
- 应先将相关商品下架或迁移至其他分类;
- 然后对该分类执行逻辑删除(标记为已删除)。
🌰 为什么选择逻辑删除?
- 订单历史中仍需显示原分类信息(如“订单详情页”);
- 避免因删除导致历史数据失真;
- 支持未来“恢复分类”或数据分析。
2️⃣ 如果没有商品使用该分类?
- 可以进行物理删除;
- 也可选择逻辑删除(便于后期复用或审计);
- ⚠️ 建议:即使无关联,也优先考虑逻辑删除,除非有明确归档策略。
三、MyBatis-Plus 中实现逻辑删除的正确姿势
✅ 步骤 1:添加 @TableLogic
注解
在实体类中标记逻辑删除字段(通常是 deleted
字段):
@TableLogic(value = "0", delval = "1")
private Integer deleted;
属性 | 含义 | 示例说明 |
---|---|---|
value |
未删除时的值(正常状态) | 如 "0" 表示未删除 |
delval |
删除后的值(删除标记) | 如 "1" 表示已逻辑删除 |
💡 提示:具体值根据公司规范调整(如用
0/1
、N/Y
、false/true
)
✅ 步骤 2:配置全局逻辑删除规则(可选)
在 application.yml
中配置(推荐统一管理):
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1 # 逻辑已删除值
logic-not-delete-value: 0 # 逻辑未删除值
配置后,实体上可省略 @TableLogic
的 value/delval
参数。
四、通用删除接口实现注意事项
实现一个健壮的删除接口,需关注以下三个关键点:
1️⃣ 🔍 参数校验(防御性编程)
if (id == null || id <= 0) {
throw new IllegalArgumentException("分类ID不能为空或非法");
}
- 对
removeById
、removeByIds
等方法,必须校验参数合法性; - 批量删除时还需校验
ids
列表是否为空。
2️⃣ 🔎 数据存在性校验
Category category = categoryService.getById(id);
if (category == null) {
return Result.fail("分类不存在,无法删除");
}
✅ 为什么需要这一步?
- 避免对不存在的数据操作,造成误判;
- 提供清晰错误信息,提升用户体验;
- 防止恶意请求探测 ID。
3️⃣ 🧩 执行逻辑删除
调用 MyBatis-Plus 提供的方法即可自动处理逻辑删除:
boolean success = categoryService.removeById(id);
- 无需手动更新
deleted
字段; - MP 会自动将
deleted
从0
改为1
; - 查询时自动过滤
deleted=1
的数据(需开启自动过滤)。
✅ 建议:使用
Result
统一封装返回结果,告知前端操作是否成功
总结
场景 | 推荐方案 |
---|---|
有关联数据(如商品、订单) | ✅ 逻辑删除 + 先解耦 |
无关联、临时数据 | ✅ 物理删除 or 逻辑删除(视业务) |
审计/回溯需求强 | ✅ 必须逻辑删除 |
高频删除、归档场景 | ✅ 物理删除 + 归档机制 |
✅ 核心原则:
- 能不删就不删,能逻辑删就不物理删;
- 删除前必校验参数与存在性;
- 借助 MyBatis-Plus 自动化能力,减少手动干预。
备注: 当处理的是临时数据,例如用户会话信息、验证码等短期内有效的数据时,一旦这些数据不再需要(比如会话过期),可以考虑进行物理删除。