MyBatis-Plus 提供了强大的逻辑删除功能,通过配置可将原本的物理删除操作自动转换为更新操作(如将 deleted
字段置为 1),从而实现数据的“软删除”。
一、核心概念
1. 什么是逻辑删除?
- 物理删除:从数据库中彻底移除记录(
DELETE FROM table WHERE id = ?
)。 - 逻辑删除:不真正删除数据,而是通过一个字段(如
deleted
)标记该记录已被删除(UPDATE table SET deleted = 1 WHERE id = ?
)。
2. 为什么使用逻辑删除?
- 防止误删数据,便于恢复。
- 满足审计、合规要求。
- 支持数据统计分析(如“已删除用户”数量)。
- 避免外键约束问题。
3. MyBatis-Plus 实现机制
- 在执行
deleteById()
、delete()
等删除方法时,MP 自动将其转为UPDATE
操作。 - 查询时自动追加
AND deleted = 0
条件(默认值),屏蔽已删除数据。 - 支持自定义字段名、未删除/已删除值。
二、操作步骤(超详细)
步骤 1:数据库表结构设计
确保你的表中包含逻辑删除字段,通常命名为 deleted
。
CREATE TABLE user (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
email VARCHAR(100),
create_time DATETIME,
update_time DATETIME,
deleted INT DEFAULT 0 NOT NULL COMMENT '0-未删除, 1-已删除'
);
⚠️ 建议:
deleted
字段加索引(尤其是大表),提高查询性能。
步骤 2:实体类添加 @TableLogic
注解
在实体类中,使用 @TableLogic
标记逻辑删除字段。
import com.baomidou.mybatisplus.annotation.*;
@Data
@TableName("user")
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private String email;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
// 核心:逻辑删除字段
@TableLogic
private Integer deleted;
}
✅ 注意:字段类型可以是
Integer
、Boolean
、LocalDateTime
等,MP 支持多种类型映射。
步骤 3:全局配置(推荐方式)
方式 A:通过 application.yml
配置(Spring Boot)
mybatis-plus:
global-config:
db-config:
# 逻辑删除字段名
logic-delete-field: deleted
# 逻辑未删除值(默认0)
logic-not-delete-value: 0
# 逻辑已删除值(默认1)
logic-delete-value: 1
方式 B:通过 Java 配置类(可选)
@Configuration
@MapperScan("com.example.mapper")
public class MyBatisPlusConfig {
@Bean
public MybatisPlusConfig mybatisPlusConfig() {
MybatisPlusConfig config = new MybatisPlusConfig();
// 配置逻辑删除
config.getGlobalConfig().getDbConfig()
.setLogicDeleteField("deleted")
.setLogicNotDeleteValue(0)
.setLogicDeleteValue(1);
return config;
}
}
✅ 推荐使用
yml
方式,简洁清晰。
步骤 4:Mapper 继承 BaseMapper
确保你的 Mapper 接口继承 BaseMapper<T>
。
public interface UserMapper extends BaseMapper<User> {
// 可以添加自定义方法
}
步骤 5:测试逻辑删除功能
@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void testDelete() {
// 执行删除操作
int result = userMapper.deleteById(1L);
System.out.println("删除结果:" + result);
// 查看 SQL 输出(需开启 MP SQL 日志)
// 实际执行的是:UPDATE user SET deleted=1 WHERE id=1 AND deleted=0
}
@Test
void testSelect() {
// 查询所有用户
List<User> users = userMapper.selectList(null);
// 实际执行:SELECT * FROM user WHERE deleted=0
users.forEach(System.out::println);
}
}
三、常见错误与解决方案
错误现象 | 原因 | 解决方案 |
---|---|---|
删除操作仍是 DELETE 而非 UPDATE |
未配置 @TableLogic 或全局配置错误 |
检查实体类字段是否加注解,yml 配置是否正确 |
查询结果包含已删除数据 | 全局配置未生效或注解缺失 | 确保 @TableLogic 存在,重启应用 |
deleted 字段在插入时被赋值 |
未设置 insertFill 导致默认值为 null |
设置 @TableField(fill = FieldFill.INSERT) 并实现自动填充 |
批量删除无效 | 使用了原生 SQL 或非 MP 方法 | 使用 update() 配合条件构造器或 deleteBatchIds() |
类型不匹配(如 Boolean) | 配置值与字段类型不符 | 使用 logic-delete-value: true 或保持一致 |
四、注意事项
- 字段默认值:数据库中
deleted
字段应设置默认值为0
(未删除)。 - 索引优化:对
deleted
字段建立索引,避免全表扫描。 - 历史数据迁移:启用逻辑删除前,需将已有数据的
deleted
字段统一设为0
。 - 联表查询:若涉及多表关联,需手动处理
WHERE t1.deleted = 0 AND t2.deleted = 0
。 - 物理删除需求:如需彻底删除,使用
deleteByIdWithFill()
或原生 SQL。 - 版本兼容性:MP 3.0+ 支持逻辑删除,旧版本需升级。
五、使用技巧
1. 自定义值类型(如 Boolean)
// 实体类
@TableLogic
private Boolean deleted; // true=已删除, false=未删除
// application.yml
mybatis-plus:
global-config:
db-config:
logic-delete-value: true
logic-not-delete-value: false
2. 时间戳作为删除标记
@TableLogic
private LocalDateTime deletedTime; // null=未删除, 有值=删除时间
// yml 配置
logic-delete-value: now
logic-not-delete-value: null
3. 忽略逻辑删除(查询已删除数据)
// 使用 Wrapper 强制查询已删除数据
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("deleted", 1); // 显式指定
List<User> deletedUsers = userMapper.selectList(wrapper);
⚠️ 注意:此时不会自动追加
deleted=0
。
六、最佳实践与性能优化
✅ 最佳实践
- 统一命名规范:所有表使用
deleted
或is_deleted
作为字段名。 - 配合自动填充:使用
@TableField(fill = FieldFill.INSERT)
设置默认值。 - 软删除 + 定期归档:长期保留的已删除数据可归档到历史表。
- 接口层统一处理:提供
realDelete(id)
方法用于管理员彻底删除。 - 文档说明:在接口文档中标明“此删除为逻辑删除”。
🚀 性能优化建议
组合索引优化:
-- 针对常用查询条件 + deleted 字段建立组合索引 CREATE INDEX idx_status_deleted ON user(status, deleted);
避免 N+1 查询:在
@Select
自定义 SQL 中手动添加AND deleted = 0
。缓存策略:
- 已删除数据不应进入缓存。
- 删除操作后及时清除相关缓存(如 Redis)。
分页查询优化:
- 大数据量下,
WHERE deleted = 0
可能影响性能,建议结合分区表或归档策略。
- 大数据量下,
七、总结
项目 | 推荐做法 |
---|---|
字段名 | deleted |
类型 | INT 或 TINYINT |
默认值 | 0 (未删除) |
已删除值 | 1 |
注解 | @TableLogic |
配置方式 | application.yml |
索引 | 对 deleted 建立索引 |
扩展 | 结合自动填充、软删除日志等 |
通过以上配置,MyBatis-Plus 的逻辑删除功能即可无缝集成到项目中,提升数据安全性与系统健壮性。