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;
}

✅ 注意:字段类型可以是 IntegerBooleanLocalDateTime 等,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 或保持一致

四、注意事项

  1. 字段默认值:数据库中 deleted 字段应设置默认值为 0(未删除)。
  2. 索引优化:对 deleted 字段建立索引,避免全表扫描。
  3. 历史数据迁移:启用逻辑删除前,需将已有数据的 deleted 字段统一设为 0
  4. 联表查询:若涉及多表关联,需手动处理 WHERE t1.deleted = 0 AND t2.deleted = 0
  5. 物理删除需求:如需彻底删除,使用 deleteByIdWithFill() 或原生 SQL。
  6. 版本兼容性: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


六、最佳实践与性能优化

✅ 最佳实践

  1. 统一命名规范:所有表使用 deletedis_deleted 作为字段名。
  2. 配合自动填充:使用 @TableField(fill = FieldFill.INSERT) 设置默认值。
  3. 软删除 + 定期归档:长期保留的已删除数据可归档到历史表。
  4. 接口层统一处理:提供 realDelete(id) 方法用于管理员彻底删除。
  5. 文档说明:在接口文档中标明“此删除为逻辑删除”。

🚀 性能优化建议

  1. 组合索引优化

    -- 针对常用查询条件 + deleted 字段建立组合索引
    CREATE INDEX idx_status_deleted ON user(status, deleted);
    
  2. 避免 N+1 查询:在 @Select 自定义 SQL 中手动添加 AND deleted = 0

  3. 缓存策略

    • 已删除数据不应进入缓存。
    • 删除操作后及时清除相关缓存(如 Redis)。
  4. 分页查询优化

    • 大数据量下,WHERE deleted = 0 可能影响性能,建议结合分区表或归档策略。

七、总结

项目 推荐做法
字段名 deleted
类型 INTTINYINT
默认值 0(未删除)
已删除值 1
注解 @TableLogic
配置方式 application.yml
索引 deleted 建立索引
扩展 结合自动填充、软删除日志等

通过以上配置,MyBatis-Plus 的逻辑删除功能即可无缝集成到项目中,提升数据安全性与系统健壮性。