You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot项目中支持JPQL的删除记录历史留存方案咨询

支持JPQL语法的删除历史留存方案

针对你的需求,这里提供几个基于JPQL+Envers的可行方案,核心思路是通过**软删除(标记删除)**替代物理删除,同时让Envers自动或手动审计变更记录:

方案一:自定义JPQL更新(软删除)+ Envers自动审计

这是最直接的方案,利用JPQL执行软删除标记的更新操作,Envers会自动审计实体的状态变更:

  1. 改造实体类
    添加软删除标记字段,并开启Envers审计:

    @Entity
    @Audited // 开启Envers审计
    public class YourEntity {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        
        // 其他业务字段...
        
        @Column(name = "is_deleted", nullable = false)
        private boolean deleted = false;
        
        // Getter & Setter
    }
    
  2. 在Repository中定义JPQL软删除方法
    用@Query注解编写JPQL更新语句,替代物理删除:

    public interface YourEntityRepository extends JpaRepository<YourEntity, Long> {
        @Modifying
        @Query("UPDATE YourEntity e SET e.deleted = true WHERE e.id = :id")
        void softDeleteById(@Param("id") Long id);
        
        // 批量软删除示例
        @Modifying
        @Query("UPDATE YourEntity e SET e.deleted = true WHERE e.id IN :ids")
        void softDeleteByIds(@Param("ids") List<Long> ids);
    }
    
  3. 配置查询过滤已删除数据
    为了避免业务查询返回已删除的实体,可以添加全局过滤:

    @Entity
    @Audited
    @Where(clause = "is_deleted = false")
    public class YourEntity {
        // ...
    }
    

    这样默认的findAll()、findById()等方法都会自动过滤已删除的记录。

Envers会自动记录这次软删除的变更(deleted字段从false变为true),你可以通过AuditReader查询历史记录:

AuditReader auditReader = AuditReaderFactory.get(entityManager);
List<Number> revisions = auditReader.getRevisions(YourEntity.class, entityId);

方案二:自定义JPQL删除拦截 + Envers手动审计

如果必须执行JPQL的DELETE语句(不推荐,物理删除后Envers无法自动审计原数据),可以在执行删除前手动用Envers记录实体快照:

  1. 编写自定义删除方法
    在Repository中先查询要删除的实体,手动创建审计记录,再执行JPQL删除:
    public interface YourEntityRepository extends JpaRepository<YourEntity, Long>, YourEntityCustomRepository {
    }
    
    // 自定义Repository实现
    public class YourEntityCustomRepositoryImpl implements YourEntityCustomRepository {
        @PersistenceContext
        private EntityManager entityManager;
        
        @Override
        @Modifying
        @Transactional
        public void deleteWithAudit(Long id) {
            // 1. 查询要删除的实体
            YourEntity entity = entityManager.find(YourEntity.class, id);
            if (entity != null) {
                // 2. 手动让Envers记录当前状态
                AuditManager auditManager = AuditManager.getInstance(entityManager);
                AuditEntityListener auditListener = auditManager.getAuditEntityListener(YourEntity.class);
                auditListener.preUpdate(entity); // 模拟更新审计,记录删除前的状态
            }
            // 3. 执行JPQL物理删除
            entityManager.createQuery("DELETE FROM YourEntity e WHERE e.id = :id")
                .setParameter("id", id)
                .executeUpdate();
        }
    }
    
    注意:需确保查询、审计记录、删除操作在同一个事务中执行。

方案三:结合自定义AuditEventListener捕获JPQL删除事件

如果需要更灵活的JPQL操作,可以自定义AuditEventListener来捕获JPQL删除事件,自动留存历史:

  1. 自定义AuditEventListener
    继承Envers的AuditEventListener,重写onPostDelete方法,在JPQL删除时手动保存实体快照:

    public class CustomAuditEventListener extends AuditEventListener {
        @Override
        protected void onPostDelete(PostDeleteEvent event) {
            // JPQL删除时event.getEntity()可能为null,需手动查询实体
            EntityManager em = event.getEntityManager();
            Object entity = em.find(event.getEntityClass(), event.getId());
            if (entity != null) {
                AuditManager auditManager = AuditManager.getInstance(em);
                AuditEntityListener listener = auditManager.getAuditEntityListener(event.getEntityClass());
                listener.preUpdate(entity);
            }
            super.onPostDelete(event);
        }
    }
    
  2. 注册自定义监听器
    在Spring Boot配置类中替换默认的Envers监听器:

    @Configuration
    public class EnversConfig {
        @Bean
        public AuditEventListener auditEventListener() {
            return new CustomAuditEventListener();
        }
    }
    

注意事项

  • 优先选择方案一,软删除+Envers自动审计是最符合JPA和Envers设计理念的方式,避免物理删除带来的数据丢失风险,同时Envers的审计逻辑更稳定。
  • JPQL的DELETE语句属于批量操作,不会触发实体的生命周期回调(如@PreRemove),所以需要手动处理审计记录。
  • 确保所有删除操作都通过自定义的软删除方法执行,避免直接调用delete()或deleteById()等默认物理删除方法。

内容的提问来源于stack exchange,提问作者ccc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 15:35:26