Hibernate软删除时如何更新updated_by与updated_date字段
解决软删除时更新updated_by和updated_date的问题
问题原因
你用@SQLDelete实现软删除时,Hibernate会直接执行你指定的UPDATE SQL,完全绕过了JPA的实体生命周期流程——这就是为什么你用@PreRemove修改实体字段后,变更不会持久化的核心原因:Hibernate根本没处理实体的状态变更,直接执行了你写的SQL语句。
推荐解决方案:JPA实体生命周期事件+@Where实现软删除
放弃@SQLDelete,改用@PreRemove结合@Where注解,既能触发实体事件更新字段,又能自动过滤已删除记录,流程更符合JPA规范,维护成本更低。
步骤1:定义实体类
给实体添加@Where注解过滤已软删数据,同时指定实体监听器:
@Entity @Table(name = "table_name") @Where(clause = "deleted = false") // 查询时自动过滤已删除的记录 @EntityListeners(SoftDeleteEntityListener.class) public class YourEntity { @Id private Long id; private Boolean deleted = false; private LocalDateTime updatedDate; private Long updatedBy; // 其他业务字段 // Getter & Setter }
步骤2:编写实体监听器
在@PreRemove方法中设置软删标记、更新时间和操作用户ID:
public class SoftDeleteEntityListener { @PreRemove public void handleSoftDelete(YourEntity entity) { // 设置软删标记 entity.setDeleted(true); // 设置更新时间 entity.setUpdatedDate(LocalDateTime.now()); // 从SecurityContext获取当前用户ID Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication != null && authentication.getPrincipal() instanceof CustomUserDetails) { CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal(); entity.setUpdatedBy(userDetails.getId()); // 替换成你项目中实际获取用户ID的逻辑 } } }
(注:CustomUserDetails是你自定义的Spring Security用户详情实现类,根据实际业务调整获取用户ID的方式即可)
步骤3:执行软删除
调用Spring Data JPA的delete()或EntityManager的remove()方法即可,JPA会自动生成包含所有更新字段的SQL:
UPDATE table_name SET deleted = ?, updated_date = ?, updated_by = ? WHERE id = ?
备选方案:保留@SQLDelete,通过Hibernate事件传递updated_by参数
如果一定要保留@SQLDelete,需要通过Hibernate的事件拦截器传递用户ID参数,但实现复杂且维护成本高,仅推荐用于特殊场景:
- 修改
@SQLDelete语句,添加updated_by占位符:
@SQLDelete(sql = "UPDATE table_name SET deleted = true, updated_date = now(), updated_by = ? WHERE id = ?")
- 实现
BeforeDeleteEventListener拦截删除事件,将用户ID注入SQL参数:
public class SoftDeleteParameterInjector implements BeforeDeleteEventListener { @Override public void onPreDelete(PreDeleteEvent event) { Object entity = event.getEntity(); if (entity instanceof YourEntity) { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); if (auth != null && auth.getPrincipal() instanceof CustomUserDetails) { Long userId = ((CustomUserDetails) auth.getPrincipal()).getId(); // 此处需要操作Hibernate内部API修改SQL参数列表,将userId插入参数首位 // 该实现依赖Hibernate版本,容易出现兼容性问题,需谨慎使用 } } } }
内容的提问来源于stack exchange,提问作者Mitch
相关产品推荐
相关产品推荐

