MyBatis-Plus逻辑删除需字段定义、@TableLogic注解和yml中logic-delete-field三者协同生效;缺一即导致物理删除,且自定义SQL、JdbcTemplate等绕过MP代理的操作不受保护。

MyBatis-Plus 的逻辑删除不是“加个配置就自动生效”的黑盒,它依赖字段定义、注解声明和全局规则三者协同;只要漏掉任意一环,deleteById() 仍会执行物理删除(尤其在未配 logic-delete-field 或实体没加 @TableLogic 时)。
数据库表必须新增 deleted 字段并建索引
逻辑删除的前提是数据层有状态载体。不加字段,MP 根本无从改写 SQL。
- 字段类型优先用
TINYINT或INTEGER(MySQL),避免用VARCHAR或BOOLEAN(后者在某些驱动下映射不稳定) - 默认值必须设为
0:ALTER TABLE user ADD COLUMN deleted TINYINT DEFAULT 0 COMMENT '0-正常,1-已删除'; - 高并发读场景下务必加索引:
CREATE INDEX idx_user_deleted ON user(deleted);,否则SELECT自动追加的AND deleted = 0会全表扫描 - 字段名可自定义(如
is_deleted、status),但后续所有配置必须严格对齐,不能大小写混用
@TableLogic 注解必须打在实体类字段上
MP 不靠字段名自动识别逻辑删除字段,@TableLogic 是触发机制的开关。没有它,即使 yml 配了全局规则,MP 也不会拦截该实体的 delete 操作。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 最简写法:
@TableLogic private Integer deleted;,此时使用默认值:未删=0,已删=1 - 若字段名不是
deleted(比如叫is_deleted),必须显式指定:@TableLogic(value = "0", delval = "1") private Integer is_deleted; - 如果用了 Lombok 的
@Data,确保字段名与数据库列名一致;否则需配合@TableField("is_deleted")显式映射 - 多个逻辑删除字段?MP 不支持——一个实体只允许一个
@TableLogic字段
application.yml 中 logic-delete-field 必须显式声明
很多人以为只配 logic-delete-value 和 logic-not-delete-value 就够了,其实 logic-delete-field 是必填项。不填,MP 默认尝试匹配字段名 deleted,一旦实体字段叫 is_deleted 就失效。
- 正确配置示例:
mybatis-plus: global-config: db-config: logic-delete-field: is_deleted logic-not-delete-value: 0 logic-delete-value: 1 - 注意缩进层级:
db-config下,不是configuration或mapper-locations下 - 如果实体类已用
@TableLogic(value = "0", delval = "1")显式标注,则 yml 中对应字段的配置会被忽略(注解优先级更高) - 多模块项目中,确保该配置落在启动模块的
application.yml,而非被@Profile隔离的 profile 文件里
自定义 XML SQL 不受逻辑删除影响
这是最容易踩坑的地方:MP 的逻辑删除仅作用于 MP 自动生成的 SQL(如 selectList()、deleteById()),所有手写的 <select></select>、<delete></delete> 标签都不会自动追加 AND deleted = 0 条件。
- 手动 SQL 查询时,必须显式加条件:
WHERE is_deleted = 0 AND ... - 手动 SQL 删除时,严禁写
DELETE FROM user WHERE id = #{id},应改为UPDATE user SET is_deleted = 1 WHERE id = #{id} AND is_deleted = 0 - 连表查询(如
user JOIN order)中,若order表也启用了逻辑删除,两个表的deleted条件都得手写,MP 不会帮你推导 - 动态 SQL(
<if test="xxx"></if>)里也别依赖 MP 自动注入——它压根不进那条执行路径
真正要防住物理删除,关键不在“配了多少”,而在“是否每处 delete 操作都经过 MP 的代理入口”。绕过 BaseMapper 直接调 JdbcTemplate 或原生 Statement,逻辑删除就彻底失效。上线前建议 grep 全局代码里的 DELETE FROM 和 drop table,确认它们只存在于测试或初始化脚本中。

















