MyBatis-Plus 逻辑删除实战:5种核心实现方案详解

在服务端开发中,数据安全与可追溯性至关重要。逻辑删除作为一种优雅的数据保护策略,通过标记字段而非物理删除来保留历史记录,已成为后端架构中的标配。MyBatis-Plus 作为流行的持久层框架,提供了灵活且强大的逻辑删除支持。本文将深入剖析5种核心实现方法,助你从容应对不同业务场景。

一、全局配置:统一规范的首选方案

对于大多数微服务项目,采用全局配置是最简洁高效的方案。只需在配置文件中定义逻辑删除的字段名、删除值与未删除值,所有继承自 BaseMapper 的接口将自动生效。

核心配置代码示例如下:

# application.yml
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted  # 全局逻辑删除字段名
      logic-delete-value: 1        # 逻辑已删除值(默认为1)
      logic-not-delete-value: 0    # 逻辑未删除值(默认为0)

配置要点:

  • logic-delete-field:指定逻辑删除字段,如 deleted,所有实体类共用此字段。
  • logic-delete-value:删除时写入的值,通常为 1true
  • logic-not-delete-value:未删除时的默认值,通常为 0false

配置完成后,删除操作会自动转换为 UPDATE 语句,而查询则会自动追加 deleted=0 条件。这种方式特别适合新项目或统一规范的后端架构,一次配置,全局生效。

二、实体类注解:灵活应对差异化需求

当不同数据表使用不同的逻辑删除字段或值时,全局配置就无法满足需求了。此时,可以在实体类的对应字段上使用 @TableLogic 注解进行精细化配置。

核心代码示例:

import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
@Data
@TableName("user")
public class User {
    private Long id;
    private String name;
    @TableLogic(value = "0", delval = "1")
    private Integer deleted;
}
@Data
@TableName("product")
public class Product {
    private Long id;
    private String productName;
    @TableLogic(value = "false", delval = "true")
    private Boolean isDeleted;
}

关键特性:

  • @TableLogic 注解标注在逻辑删除字段上,支持 IntegerBooleanString 等多种数据类型。
  • value 属性:未删除时的值,默认为 "0"
  • delval 属性:删除后的值,默认为 "1"

注解的优先级高于全局配置,非常适合老项目改造、多数据库表结构不一致或特殊业务需求。通过这种方式,可以在同一项目中实现不同表的逻辑删除差异化。

三、局部控制:通过自定义Wrapper覆盖默认行为

在某些特殊场景下,比如实现回收站功能管理员查看所有数据,我们需要临时改变逻辑删除的行为。这时,可以通过自定义 Wrapper 来实现局部控制。

核心代码示例:

// 1. 查询时忽略逻辑删除条件
List users = userMapper.selectList(
    Wrappers.lambdaQuery()
        .apply("deleted = 1 or deleted = 0")  // 自定义条件覆盖自动添加的条件
);
// 2. 使用自定义SQL完全控制
@Select("SELECT * FROM user WHERE id = #{id}")
User selectByIdIgnoreLogic(@Param("id") Long id);
// 3. 链式调用中的特殊处理
userMapper.selectList(
    new QueryWrapper()
        .eq("status", 1)
        .and(wrapper -> wrapper.eq("deleted", 0).or().isNull("deleted"))
);

实现技巧:

  • 使用 apply() 方法可以添加原生 SQL 片段,覆盖自动生成的逻辑删除条件。
  • 自定义 SQL 注解(如 @Select@Update)不会自动添加逻辑删除条件,需要手动处理。
  • 通过 or()and() 组合条件,可以实现灵活的控制逻辑。

这种方式在 API 层面提供了极高的可控性,尤其适合需要临时查看或恢复已删除数据的场景。

四、深度定制:SQL级别的过滤控制

MyBatis-Plus 默认自动过滤已删除数据,但在某些复杂业务中,我们需要更细粒度的控制。通过自定义 LogicSqlInjector 或使用 @SqlParser 注解的 filter 属性,可以实现 SQL 级别的深度定制。

核心配置与代码:

// 1. 配置类中自定义逻辑删除处理器
@Configuration
public class MybatisPlusConfig {
    @Bean
    public ISqlInjector sqlInjector() {
        return new LogicSqlInjector() {
            @Override
            protected String getLogicDeleteSql(TableInfo tableInfo, boolean withWhere) {
                // 自定义逻辑删除SQL生成逻辑
                String logicDeleteField = tableInfo.getLogicDeleteField();
                return "UPDATE " + tableInfo.getTableName() +
                       " SET " + logicDeleteField + " = #{et." + logicDeleteField + "}" +
                       (withWhere ? " WHERE " + tableInfo.getKeyColumn() + "=#{et." + tableInfo.getKeyProperty() + "}" : "");
            }
        };
    }
}
// 2. 使用@SqlParser注解控制过滤
@SqlParser(filter = true)  // 过滤逻辑删除数据
public List selectActiveUsers();
@SqlParser(filter = false) // 不过滤逻辑删除数据
public List selectAllUsers();

⚠️ 注意事项:

  • 可以在 Service 层、Mapper 层或具体方法上控制是否过滤逻辑删除数据。
  • 通过 AOP 或拦截器,可以实现更复杂的过滤逻辑,比如根据用户角色动态调整。
  • 这种方案适合需要精细权限控制的后端架构,确保数据安全。

五、多租户与逻辑删除的联合应用

在企业级微服务应用中,多租户数据隔离与逻辑删除常常需要结合使用。MyBatis-Plus 提供了无缝的集成方案,确保查询和删除操作同时考虑租户隔离与删除状态。

核心代码示例:

// 1. 多租户配置类
@Configuration
public class TenantConfig {
    @Bean
    public TenantLineHandler tenantLineHandler() {
        return new TenantLineHandler() {
            @Override
            public String getTenantIdColumn() {
                return "tenant_id";
            }
            @Override
            public Expression getTenantId() {
                return new LongValue(UserContext.getCurrentTenantId());
            }
            @Override
            public boolean ignoreTable(String tableName) {
                // 忽略不需要多租户的表
                return "system_config".equals(tableName);
            }
        };
    }
}
// 2. 实体类同时支持多租户和逻辑删除
@Data
@TableName("order")
public class Order {
    private Long id;
    private String orderNo;
    @TableField(fill = FieldFill.INSERT)  // 自动填充租户ID
    private Long tenantId;
    @TableLogic
    private Integer deleted;
}
// 3. 查询时自动添加租户和逻辑删除条件
List orders = orderMapper.selectList(
    Wrappers.lambdaQuery()
        .eq(Order::getStatus, 1)
        // 会自动添加:AND tenant_id = 当前租户ID AND deleted = 0
);

集成要点:

  • 查询时会自动添加 tenant_id = ? AND deleted = 0 条件,实现租户隔离。
  • 删除操作会同时考虑租户隔离,避免跨租户误删数据。
  • 通过 ignoreTable 方法,可以排除不需要多租户的表,如公共配置表。

这种方案是构建安全、可扩展的企业级后端架构的关键技术。

[AFFILIATE_SLOT_1]

最佳实践与性能优化

在实际项目中,除了选择合适的实现方式,还需要注意以下几点:

  • 索引优化:逻辑删除字段建议添加索引,特别是数据量大的表,以提升查询性能。
  • 字段类型选择:推荐使用 tinyintboolean 类型,占用空间小且查询效率高。避免使用字符串类型。
  • 数据一致性:在批量操作或中间件集成时,确保逻辑删除字段的更新与业务逻辑一致。以下是一个示例:

// 事务中处理逻辑删除
@Transactional
public void deleteUserWithRelatedData(Long userId) {
    // 删除用户
    userMapper.deleteById(userId);
    // 同时处理关联数据
    userRoleMapper.deleteByUserId(userId);
    // 记录操作日志
    logService.saveDeleteLog(userId);
}

  • 查询性能:定期归档已删除数据,避免表数据量过大影响性能。
  • 兼容性考虑:老数据迁移时,需要批量更新逻辑删除字段,确保新老数据格式一致。
[AFFILIATE_SLOT_2]

总结

MyBatis-Plus 的逻辑删除功能设计巧妙且灵活,通过5种不同的实现方法,可以满足从简单到复杂的各种业务场景:

  • 全局配置:适合项目统一规范,配置简单,一劳永逸。
  • 注解配置:支持差异化配置,灵活性强,适合老项目改造。
  • 局部控制:满足特殊业务需求,可控性高,适合回收站等功能。
  • 深度定制:支持 SQL 级别控制,扩展性好,适合精细权限管理。
  • 多租户集成:适合企业级应用,安全性强,是微服务架构的必备技能。

方法适用场景优点缺点
默认自动过滤常规业务查询自动生效,无需额外配置无法查询已删除数据
@InterceptorIgnore特殊场景查询已删除灵活控制单次查询需要写注解或自定义SQL
条件构造器复杂条件查询可精确控制删除状态需要手动添加条件
自定义SQL完全自定义需求灵活性最高维护成本较高
SQL注入器框架扩展需求可复用性强实现复杂度高

在实际开发中,建议根据项目规模和复杂度选择合适的方案。逻辑删除不仅能保护数据安全,还能为数据恢复、审计追踪等功能提供支持,是现代服务端应用开发中不可或缺的重要特性。掌握这些技巧,将让你的后端架构更加健壮与优雅。

posted @ 2026-05-21 09:51  ycfenxi  阅读(108)  评论(0)    收藏  举报