Spring Boot项目中支持JPQL的删除记录历史留存方案咨询
支持JPQL语法的删除历史留存方案
针对你的需求,这里提供几个基于JPQL+Envers的可行方案,核心思路是通过**软删除(标记删除)**替代物理删除,同时让Envers自动或手动审计变更记录:
方案一:自定义JPQL更新(软删除)+ Envers自动审计
这是最直接的方案,利用JPQL执行软删除标记的更新操作,Envers会自动审计实体的状态变更:
改造实体类
添加软删除标记字段,并开启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 }在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); }配置查询过滤已删除数据
为了避免业务查询返回已删除的实体,可以添加全局过滤:@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记录实体快照:
- 编写自定义删除方法
在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删除事件,自动留存历史:
自定义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); } }注册自定义监听器
在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
相关产品推荐
相关产品推荐

