核心概念

@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);
    

常见错误与解决方案

  1. 字段类型不匹配

    • ❌ 错误:配置 logic-delete-value: "YES",但字段类型为 Integer
    • ✅ 解决:确保字段类型与配置值匹配(如 Integer 字段用数字)
  2. 忘记添加数据库字段

    • ❌ 错误:Unknown column 'deleted' in 'field list'
    • ✅ 解决:按步骤4添加数据库字段
  3. 自定义SQL未过滤逻辑删除

    • ❌ 错误:手写SQL查询到已删除数据
    • ✅ 解决:手动添加条件 WHERE deleted=0

注意事项

  1. 唯一索引冲突

    • 若某字段需唯一约束(如 username),被逻辑删除的数据可能导致新数据插入冲突
    • 解决方案
      • 添加 deleted 到联合唯一索引
      • 使用时间戳标记删除(如 delete_time),未删除时为 NULL
  2. 全局配置优先级

    • 实体类注解优先级高于全局配置:
      @TableLogic(value = "0", delval = "2") // 覆盖全局配置
      private Integer deleted;
      
  3. 关联查询

    • 多表关联时需手动过滤 deleted=0,MyBatis-Plus 仅自动处理当前实体

使用技巧

  1. 混合删除标记
    delete_time 同时标记删除状态和时间:

    @TableLogic(value = "NULL", delval = "NOW()")
    private LocalDateTime deleteTime;
    
    • 查询条件:WHERE delete_time IS NULL
  2. 查询包含已删除数据

    new QueryWrapper<User>().isNotNull("delete_time"); // 自定义条件覆盖默认过滤
    
  3. 逻辑删除恢复

    UpdateWrapper<User> wrapper = new UpdateWrapper<>();
    wrapper.set("deleted", 0).eq("id", 1);
    userMapper.update(null, wrapper);
    

最佳实践与性能优化

  1. 索引优化

    ALTER TABLE user ADD INDEX idx_deleted (deleted); -- 为逻辑删除字段建索引
    
  2. 定期归档历史数据

    • 方案:将 deleted=1 的数据迁移到历史表,减少主表体积
  3. 统一字段命名

    • 全系统使用相同逻辑删除字段名(如 deleted),简化配置
  4. 避免全表更新

    • 逻辑删除的 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)));
}

📌 关键总结

  • 配置三步:依赖 → 全局配置 → 实体注解
  • 删除变更新,查询自动过滤
  • 警惕唯一索引冲突,关联查询需手动处理
  • 索引+归档优化大表性能