核心概念
@TableLogic
用于实现 逻辑删除(软删除):
- 不物理删除数据,而是通过字段标记(如
deleted=1
)表示数据“已删除” - 查询时自动过滤被标记的数据
- 优势:保留数据历史、避免误删、支持恢复
操作步骤(详细版)
1. 添加依赖
确保 pom.xml
包含最新 MyBatis-Plus 依赖:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.5</version> <!-- 使用最新版本 -->
</dependency>
2. 配置全局逻辑删除规则
在 application.yml
中配置:
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 逻辑删除字段名
logic-delete-value: 1 # 删除标记值
logic-not-delete-value: 0 # 未删除标记值
3. 实体类添加注解
public class User {
@TableId
private Long id;
private String name;
@TableLogic // 关键注解
private Integer deleted; // 类型可以是 int/Integer/Boolean 等
}
4. 数据库表添加字段
ALTER TABLE user ADD COLUMN deleted INT DEFAULT 0; -- 0=未删除,1=已删除
5. CRUD 操作演示
删除数据(自动转逻辑删除)
userMapper.deleteById(1L); // 执行SQL: UPDATE user SET deleted=1 WHERE id=1 AND deleted=0
查询数据(自动过滤已删除)
userMapper.selectList(null); // 执行SQL: SELECT * FROM user WHERE deleted=0
物理删除(需手动编写SQL)
@Delete("DELETE FROM user WHERE id=#{id}") void physicalDelete(Long id);
常见错误与解决方案
字段类型不匹配
- ❌ 错误:配置
logic-delete-value: "YES"
,但字段类型为Integer
- ✅ 解决:确保字段类型与配置值匹配(如
Integer
字段用数字)
- ❌ 错误:配置
忘记添加数据库字段
- ❌ 错误:
Unknown column 'deleted' in 'field list'
- ✅ 解决:按步骤4添加数据库字段
- ❌ 错误:
自定义SQL未过滤逻辑删除
- ❌ 错误:手写SQL查询到已删除数据
- ✅ 解决:手动添加条件
WHERE deleted=0
注意事项
唯一索引冲突
- 若某字段需唯一约束(如
username
),被逻辑删除的数据可能导致新数据插入冲突 - 解决方案:
- 添加
deleted
到联合唯一索引 - 使用时间戳标记删除(如
delete_time
),未删除时为NULL
- 添加
- 若某字段需唯一约束(如
全局配置优先级
- 实体类注解优先级高于全局配置:
@TableLogic(value = "0", delval = "2") // 覆盖全局配置 private Integer deleted;
- 实体类注解优先级高于全局配置:
关联查询
- 多表关联时需手动过滤
deleted=0
,MyBatis-Plus 仅自动处理当前实体
- 多表关联时需手动过滤
使用技巧
混合删除标记
用delete_time
同时标记删除状态和时间:@TableLogic(value = "NULL", delval = "NOW()") private LocalDateTime deleteTime;
- 查询条件:
WHERE delete_time IS NULL
- 查询条件:
查询包含已删除数据
new QueryWrapper<User>().isNotNull("delete_time"); // 自定义条件覆盖默认过滤
逻辑删除恢复
UpdateWrapper<User> wrapper = new UpdateWrapper<>(); wrapper.set("deleted", 0).eq("id", 1); userMapper.update(null, wrapper);
最佳实践与性能优化
索引优化
ALTER TABLE user ADD INDEX idx_deleted (deleted); -- 为逻辑删除字段建索引
定期归档历史数据
- 方案:将
deleted=1
的数据迁移到历史表,减少主表体积
- 方案:将
统一字段命名
- 全系统使用相同逻辑删除字段名(如
deleted
),简化配置
- 全系统使用相同逻辑删除字段名(如
避免全表更新
- 逻辑删除的
UPDATE
操作需明确指定WHERE
条件,防止误操作
- 逻辑删除的
完整代码示例
// 实体类
public class User {
@TableId
private Long id;
private String name;
@TableLogic(value = "0", delval = "1") // 自定义值
private Integer deleted;
}
// 测试逻辑删除
@Test
public void testLogicDelete() {
// 删除ID=100的数据(转UPDATE)
userMapper.deleteById(100L);
// 查询结果自动排除已删除数据
List<User> list = userMapper.selectList(null);
Assert.assertTrue(list.stream().noneMatch(u -> u.getId().equals(100L)));
}
📌 关键总结:
- 配置三步:依赖 → 全局配置 → 实体注解
- 删除变更新,查询自动过滤
- 警惕唯一索引冲突,关联查询需手动处理
- 索引+归档优化大表性能