本文目录导读:

在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 应用就能优雅地实现逻辑删除了。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播