Java数据删除流程如何统一

wen java案例 28

本文目录导读:

Java数据删除流程如何统一

  1. 核心原则
  2. 统一删除流程架构
  3. 典型删除接口示例(Controller → Service)
  4. 物理删除的规范约束
  5. 异常处理与监控
  6. 统一删除流程图(Text)
  7. 最佳实践总结

在Java开发中,统一数据删除流程(通常称为软删除逻辑删除,配合硬删除的规范)是保证数据一致性、可追溯性和安全性的关键,以下是一套标准化的统一删除流程设计,涵盖数据访问层(DAO)、业务层(Service)、以及缓存与消息队列的协同。


核心原则

  1. 首选软删除(逻辑删除):默认不物理删除记录,而是标记为删除状态。
  2. 统一硬删除时机:仅允许在指定时间窗口(如凌晨低峰期)或特定系统任务(如数据归档)中执行物理删除。
  3. 事务强一致性:删除操作必须在一个数据库事务内完成。
  4. 删除溯源:记录删除人、删除时间及原因。

统一删除流程架构

数据模型设计(推荐使用MyBatis-Plus或JPA)

  • 基础字段(建议所有业务表都包含):
    `deleted`       tinyint(1) DEFAULT 0 COMMENT '逻辑删除标识: 0-未删除, 1-已删除',
    `deleted_time`  datetime DEFAULT NULL COMMENT '删除时间',
    `deleted_by`    varchar(50) DEFAULT NULL COMMENT '删除人ID',
    `delete_reason` varchar(255) DEFAULT NULL COMMENT '删除原因/理由'
  • 唯一约束处理:若表有联合唯一索引,需将 deleted 纳入索引中,避免软删除后重复插入。

DAO层的统一封装(以MyBatis-Plus为例)

// 自定义统一删除基类
public interface BaseMapper<T> extends MyBatisPlusBaseMapper<T> {
    // 1. 逻辑删除(默认走Update)
    int softDeleteById(Serializable id, String userId);
    // 2. 批量逻辑删除
    int softDeleteByBatchIds(@Param("coll") Collection<? extends Serializable> idList, 
                             @Param("userId") String userId);
    // 3. 硬删除(仅限特定管理员或定时任务)
    int hardDeleteById(Serializable id);
}
  • SQL示例
    UPDATE your_table 
    SET deleted = 1, 
        deleted_time = NOW(), 
        deleted_by = #{userId},
        delete_reason = #{reason}
    WHERE id = #{id} AND deleted = 0;

Service层的统一拦截

  • 使用Spring AOP或自定义注解实现统一的删除入参检查与记录:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UnifiedDelete {
    boolean enable() default true;
    String deleteReason() default "";
}
@Aspect
@Component
public class DeleteAspect {
    @Around("@annotation(unifiedDelete)")
    public Object aroundDelete(ProceedingJoinPoint pjp, UnifiedDelete unifiedDelete) throws Throwable {
        // 1. 获取当前操作用户(从SecurityContext或ThreadLocal)
        String deletor = SecurityUtils.getCurrentUserId();
        String reason = unifiedDelete.deleteReason();
        // 2. 对参数进行增强:传递删除人信息(可注入参数或通过上下文传递)
        // 3. 执行原始方法(若rollback则自动同步)
        Object result = pjp.proceed();
        // 4. 可选:记录删除操作日志到 audit_log 表
        logDeleteEvent(pjp);
        return result;
    }
}

缓存与关联数据清理

  • 缓存策略:软删除后立即失效对应缓存(如Redis):
    @CacheEvict(value = "userCache", key = "#id")
    public int preDelete(Long id, String reason) {
        return baseMapper.softDeleteById(id, reason);
    }
  • 关联数据检查:在删除前验证是否存在外键依赖(例如删除用户时,检查其订单); 如果有级联需求,使用软删除传递(如:“用户”软删除 → 关联的“订单”也软删除)。

典型删除接口示例(Controller → Service)

// Rest API 设计
@PostMapping("/{id}/delete")
@PreAuthorize("hasAuthority('DATA:DELETE')")
@UnifiedDelete(deleteReason = "用户主动删除")
public R<Void> delete(@PathVariable Long id, 
                      @RequestBody(required = false) DeleteRequest request) {
    userService.deleteUser(id, request);
    return R.ok();
}
// Service 实现
@Transactional(rollbackFor = Exception.class)
public void deleteUser(Long id, DeleteRequest request) {
    // 1. 校验:用户是否存在且未删除
    User user = lambdaQuery()
                 .eq(User::getId, id)
                 .eq(User::getDeleted, 0)
                 .oneOpt()
                 .orElseThrow(() -> new BusinessException("数据不存在或已删除"));
    // 2. 业务规则校验(如:用户不能删除自己)
    if (Objects.equals(user.getId(), CurrentUser.getId())) {
        throw new BusinessException("不能删除自己");
    }
    // 3. 执行软删除
    boolean success = lambdaUpdate()
                     .set(User::getDeleted, 1)
                     .set(User::getDeletedTime, LocalDateTime.now())
                     .set(User::getDeletedBy, CurrentUser.getId())
                     .set(User::getDeleteReason, 
                          request != null ? request.getReason() : "手动删除")
                     .eq(User::getId, id)
                     .update();
    // 4. 触发关联数据删除(如有)
    if (success) {
        // 级联软删除关联订单、角色等
        orderService.cascadeDeleteByUserId(id);
        userRoleService.removeByUserId(id);
    }
}

物理删除的规范约束

  • 定时任务统一执行:每日凌晨通过定时任务清理超过30天的已删除记录。
  • 物理删除前置条件
    • 该数据已完成归档到历史库(如ClickHouse)。
    • 从业务角度该数据无恢复可能(如错误数据)。
  • 代码如
    @Scheduled(cron = "0 0 3 * * ?") // 凌晨3点
    @Transactional
    public void purgeDeletedRecords() {
        LocalDateTime threshold = LocalDateTime.now().minusDays(30);
        baseMapper.hardDeleteBatchByCondition(
            "deleted = 1 AND deleted_time < ?", threshold);
    }

异常处理与监控

  1. 唯一性约束处理:若使用逻辑删除且字段上有唯一索引,可以将deleted字段加入联合索引或采用-1 * id方式(非推荐,尽量数据库层面解耦)。
  2. 删除冲突报错:分布式环境下重试机制,幂等检查。
  3. 操作日志全记录:所有删除操作写入 sys_audit_log 表;大厂建议发送到消息队列(MQ)做异步审计。

统一删除流程图(Text)

用户请求删除
    ↓
Controller 接收参数,携带删除人信息
    ↓
@Service 执行
    ├─ AOP切面:记录删除原因 + 权限校验
    ├─ DAO:软删除 UPDATE(deleted=1)
    ├─ 业务校验(关联数据检查、规则判断)
    ├─ 缓存失效(@CacheEvict)
    ├─ 消息队列发送“删除事件”(可选)
    ↓
事务提交(Rollback保证一致性)
    ↓
返回成功结果
    ↓
后续定时任务进行物理删除(归档清理)

最佳实践总结

层次 关键点
数据模型 统一字段命名规范:deleted/deleted_time/deleted_by
DAO 只暴露软删除方法,禁止在普通业务代码中使用硬删除
Service 严格使用 @Transactional ,支持异常回滚
AOP 提取统一的删除日志、权限检查、前置校验逻辑
缓存 软删除后立即执行缓存剔除,避免脏读
硬删除 仅限定时任务或管理员后台,需留白日志

通过以上方式,你可以将Java应用中的删除操作统一化、标准化,这不仅减少了开发中删除逻辑的重复代码,还能有效防止因误删带来的数据丢失风险,并满足审计合规要求。

抱歉,评论功能暂时关闭!