本文目录导读:

在Java中实现逻辑删除(软删除)通常包含以下几个核心层面:数据库设计、实体类定义、MyBatis-Plus配置(常用方案)、以及查询时的自动过滤。
以下是完整的实现方案,涵盖最常用的 MyBatis-Plus 方式以及通用的手动实现方式。
数据库层面
在表中添加一个字段用于标记是否被删除,常用的字段名有 is_deleted 或 deleted。
ALTER TABLE user ADD COLUMN `is_deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标识 0-未删除 1-已删除';
- 0:未删除(正常数据)
- 1:已删除(逻辑删除后的数据)
MyBatis-Plus 实现(推荐,最简单)
MyBatis-Plus 内置了逻辑删除插件,只需要简单配置即可。
配置全局属性
在 application.yml 或 application.properties 中配置:
# application.yml
mybatis-plus:
global-config:
db-config:
logic-delete-field: is_deleted # 全局逻辑删除字段名(1.0.0+)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
实体类添加注解
在实体类对应的字段上添加 @TableLogic 注解:
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("user")
public class User {
private Long id;
private String name;
@TableLogic
private Integer isDeleted; // 字段名与配置的 logic-delete-field 一致
// getter 和 setter 省略
}
效果验证
配置完成后,MyBatis-Plus 会自动处理:
-
删除操作:
userMapper.deleteById(1L); // 实际执行的 SQL:UPDATE user SET is_deleted=1 WHERE id=1 AND is_deleted=0 // 注意:默认只会更新 id=1 且未删除的记录
-
查询操作:
userMapper.selectById(1L); // 实际执行的 SQL:SELECT * FROM user WHERE id=1 AND is_deleted=0 // 自动追加了 is_deleted=0 条件
-
更新操作:
userMapper.updateById(user); // 实际执行的 SQL:UPDATE user SET ... WHERE id=? AND is_deleted=0 // 自动限制只能更新未删除的记录
手动实现(不使用 ORM 框架)
如果你没有使用 MyBatis-Plus,可以手动在 DAO 层或 Service 层实现。
基础编码
// Service 层实现
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
// 逻辑删除
@Override
public void logicDelete(Long id) {
User user = new User();
user.setId(id);
user.setIsDeleted(1);
userMapper.updateById(user);
// 等效 SQL:UPDATE user SET is_deleted = 1 WHERE id = ?
}
// 查询(必须加上未删除条件)
@Override
public User findById(Long id) {
// 自定义查询,手动加上 is_deleted = 0
return userMapper.selectByIdAndNotDeleted(id);
}
}
// Mapper XML 示例
// <select id="selectByIdAndNotDeleted" resultType="User">
// SELECT * FROM user WHERE id = #{id} AND is_deleted = 0
// </select>
关键注意事项
- 所有查询都必须手动过滤
is_deleted = 0,遗漏会导致数据混乱。 - 唯一约束问题:如果对字段设置了唯一约束(如邮箱、手机号),逻辑删除后再次插入相同值会冲突,解决方案有两种:
- 唯一约束联合
is_deleted字段(允许0有唯一,1可以重复)。 - 删除时对唯一字段加工(
email = id + '_deleted' + email)。
- 唯一约束联合
高级技巧:自动填充删除时间
结合 MyBatis-Plus 的自动填充功能,可以在逻辑删除时自动记录删除时间。
// 实体类增加字段
@TableField(fill = FieldFill.UPDATE) // 或 FieldFill.INSERT_UPDATE
private LocalDateTime deleteTime;
// 自定义 MetaObjectHandler 实现
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void updateFill(MetaObject metaObject) {
// 获取当前 is_deleted 值,如果为1(删除),则自动填充时间
Object isDeleted = getFieldValByName("isDeleted", metaObject);
if (isDeleted != null && (Integer) isDeleted == 1) {
this.strictUpdateFill(metaObject, "deleteTime", LocalDateTime.now());
}
}
}
总结与最佳实践
| 步骤 | 说明 | |
|---|---|---|
| 1 | 数据库添加 is_deleted 字段 |
类型为 tinyint,默认为 0 |
| 2 | 实体类添加对应字段和注解 | 推荐使用 MyBatis-Plus 的 @TableLogic |
| 3 | 配置全局逻辑删除规则 | 指定已删除/未删除对应的值 |
| 4 | 业务层正常调用 | 框架自动处理 SQL 中的条件追加 |
建议:
- 如果项目新启动,直接使用 MyBatis-Plus 的内置逻辑删除,效率最高。
- 如果已有复杂查询,注意检查自定义 SQL 是否也需要过滤
is_deleted条件(可以使用 XML 中的 SQL 片段或自定义拦截器)。 - 逻辑删除的数据仍然占用存储空间,建议定期做物理归档或清理。
这样,你的 Java 应用就能优雅地实现逻辑删除了。