MyBatis-Flex逻辑删除

结城 SpringBoot 5 次阅读 1382 字 发布于 7 天前 预计阅读时间: 6 分钟


关于逻辑删除配置,只要满足以下两个前提,MyBatis-Flex都会自动追加逻辑删除条件。

当然,并不是所有场景都能覆盖,需要有前置条件。

实体类已配置:对应的实体类属性标注了 @Column(isLogicDelete = true)

使用 MyBatis-Flex 的标准 API 查询:如 Mapper.selectListByQuery​、QueryWrapper

在使用 MyBatis-Flex 逻辑删除时,需要特别注意自动注入失效或需要特殊处理的场景。

不会启用逻辑删除的场景

1.自定义 XML 或 @Select 原生 SQL

如果在 .xml​ 映射文件中或者使用 @Select("SELECT * FROM user WHERE id = #{id}")​ 编写了原生 SQL,MyBatis-Flex 不会对 SQL 进行 AST 解析并自动追加 deleted = 0

在这类手写 SQL 的场景下,必须手动写明 AND deleted = 0,框架不会自动帮你填写。

2.执行不带实体类型的 Db 查询

如果直接使用 Db.selectListBySql("select * from user")​ 这种不与实体 Class 绑定的方法,框架无法获取到实体的 @Column 注解信息。

因此,在使用 Db 工具类时,推荐使用带有实体 Class 泛型的方法。

例如 Db.selectListByQuery(QueryWrapper.create().from(User.class))。只要指定了实体类,逻辑删除依然会自动追加。

3.多表联查时的行为

在进行 leftJoin​ / innerJoin​ 等多表关联查询时,MyBatis-Flex 会自动在联查的 ON​ 条件或整个 WHERE​ 尾部,为主表和所有关联表自动追加其对应的 deleted = 0 约束。

需要注意,如果您在某些特定业务中,需要关联出已被删除的子表数据,必须在 QueryWrapper​ 中显式调用 QueryWrapper.noLogicDelete()​ 或者使用LogicDeleteManager.execWithoutLogicDelete(...)包裹语句来暂时停用自动追加行为

4.更新已被逻辑删除的数据

当执行 update​ 操作时,MyBatis-Flex 同样会自动在 WHERE​ 条件中注入 AND deleted = 0​。等同于无法通过常规的 update 接口去修改一条已经被逻辑删除的数据。

4种忽略逻辑方式对比

方式作用域核心 API / 配置适用场景
单次查询忽略当前QueryWrapperQueryWrapper.noLogicDelete()仅在某一次查询中需要包含已删除数据
线程上下文忽略当前线程/代码块LogicDeleteManager.execWithoutLogicDelete(...)批量数据同步、后台管理大批物理/逻辑混合操作,免去在每个 Query 里加配置
实体类级别不启用单张表/实体移除 @Column(isLogicDelete = true)该表采用物理删除,或者不需要逻辑删除机制
全局配置关闭整个应用移除逻辑删除处理器 (LogicDeleteProcessor)全局重构,彻底弃用逻辑删除机制

详细方式及区别解析

1.单次查询忽略

通过在构建 QueryWrapper​ 时,显式调用 noLogicDelete()​ 方法,来指示当前查询不拼接 deleted = 0 的条件,忽略逻辑删除。

  • 代码示例
  QueryWrapper queryWrapper = QueryWrapper.create()
          .select()
          .from(User.class)
          .where(User::getId).eq(1L)
          .noLogicDelete(); // 关键:关闭此查询的逻辑删除

  User user = userMapper.selectOneByQuery(queryWrapper);
  • 特点
  • 安全可控:仅对当前这一个 QueryWrapper 生成的 SQL 生效,不会污染其他查询。
  • 支持多表联查:如果有多表Join,调用 noLogicDelete() 将同时使主表和关联表都忽略逻辑删除过滤,免于复杂 SQL 重复填写。

2.线程上下文忽略

此方式为 ThreadLocal 级别。

MyBatis-Flex 提供了 LogicDeleteManager​ 工具类。它利用 ThreadLocal 机制,在指定的代码块内,使所有执行的数据库操作全部忽略逻辑删除过滤。

  • 代码示例
  // 在这个 Lambda 代码块内部,执行的所有 Mapper 查询都会忽略逻辑删除
  List<User> list = LogicDeleteManager.execWithoutLogicDelete(() -> {
      return userMapper.selectAll(); 
  });
  • 特点
  • 免侵入:不需要修改任何 QueryWrapper 的构造。
  • 传播性:如果代码块内调用了其他 Service 的方法,那些方法内的查询也会同时忽略。
  • 安全性:它是基于 ThreadLocal​ 的。MyBatis-Flex 会在执行完后自动清除 ThreadLocal 变量,因此不需要担心线程池复用导致的污染问题,设计非常安全。

特别注意:

在使用 LogicDeleteManager.execWithoutLogicDelete()​ 时,由于它是基于 ThreadLocal​ 的,如果在代码块中新开了子线程,如使用 @Async​ 或 CompletableFuture,子线程中的查询仍然会有逻辑删除过滤。

此时需要通过 noLogicDelete()​ 或在子线程内部再次使用 LogicDeleteManager 来解决。

3.实体类级别不启用

如果在设计表时,某张表根本不需要逻辑删除,指定其直接 delete 物理删除。

  • 做法
    直接不在实体的属性上加 @Column(isLogicDelete = true)​,或者将其设为默认的 false,只要不填,逻辑删除便不启用。
  • 特点
  • 调用 deleteById​ 时,底层执行的是真实的 DELETE FROM table WHERE id = ? 物理删除。
  • 所有针对该表的 select​ 查询均不会拼接任何 deleted 的条件。

4.全局配置关闭

如果你在重构系统,想把整个项目的逻辑删除机制彻底停用。

  • 做法
  • 如果使用的是 Spring Boot,不注入任何 IDialect​ 中关联的 LogicDeleteProcessor
  • 或者直接将 MyBatis-Flex 的逻辑删除全局配置项 mybatis-flex.global-config.normal-value 全部注销。
  • 特点
  • 此时即便实体类上写了 @Column(isLogicDelete = true),框架也会因为找不到逻辑删除处理器而退化为正常的列,不再进行自动 SQL 注入。
给时光以生命,给岁月以文明
最后更新于 2026-06-15