Hibernate Envers:父实体变更时强制子实体生成修订版本的实现咨询
Hibernate Envers:父实体变更时强制子实体生成修订版本的实现咨询
你好呀!看了你描述的手动审计流程,用同一张表存所有实体版本、靠active标记区分当前版本的方式确实很直观,但手动维护起来肯定挺麻烦的,想用Hibernate Envers来优化是个不错的选择~
先理清楚你的场景:你有employees和addresses的关联结构,当前手动审计时更新员工会插入新员工行、关联新地址行,再把旧员工行标为非活跃。现在想实现的是当父实体(员工)变更时,强制子实体(地址)也生成对应的修订版本,对吧?
下面给你几个实用的实现方案,你可以根据自己的实体关联情况来选:
方案1:用@AuditMappedBy关联审计(双向关联首选)
如果你的实体是双向关联的(员工里有地址列表,地址里也关联对应的员工),直接在父实体的关联字段上加上@AuditMappedBy注解就行。Envers会自动识别这种关联,当父实体变更时,把关联的子实体也纳入审计范围。
代码示例:
@Entity @Audited // 标记该实体需要审计 public class Employee { @Id private String id; private String name; @OneToMany(mappedBy = "employee") @AuditMappedBy(mappedBy = "employee") // 关键:指定子实体的关联字段 private List<Address> addresses; // getter、setter省略 } @Entity @Audited // 子实体也要标记审计 public class Address { @Id private String id; private String street; @ManyToOne private Employee employee; // getter、setter省略 }
方案2:手动触发子实体变更(单向关联可用)
如果是单向关联,或者不想依赖双向关联的结构,可以在父实体的更新逻辑里,手动修改子实体的某个字段(比如加个lastUpdated时间字段),这样Envers会因为子实体本身有变更,自动生成修订记录。
代码示例:
@Entity @Audited public class Employee { @Id private String id; private String name; @OneToMany(cascade = CascadeType.ALL) private List<Address> addresses; // 自定义更新方法,触发子实体变更 public void updateInfo(String newName) { this.name = newName; // 更新每个地址的最后修改时间,触发审计 addresses.forEach(address -> address.setLastUpdated(LocalDateTime.now())); } // getter、setter省略 } @Entity @Audited public class Address { @Id private String id; private String street; private LocalDateTime lastUpdated; // 新增字段用于触发审计 // getter、setter省略 }
方案3:自定义修订监听器(灵活度最高)
如果前两种方案都不符合你的需求,还可以通过自定义Envers的监听器,在父实体生成修订记录时,手动为子实体创建审计条目。
代码示例:
public class CustomRevisionListener implements EntityTrackingRevisionListener { @Override public void newRevision(Object revisionEntity) { // 这里可以初始化修订实体的信息,比如设置修订时间、操作人等 } @Override public void entityChanged(Class<?> entityClass, String entityName, Object entityId, RevisionType revisionType, Object revisionEntity) { // 检测到员工实体被修改时,触发地址的审计 if (Employee.class.equals(entityClass) && RevisionType.MOD.equals(revisionType)) { // 获取当前的EntityManager,查询该员工的所有地址 EntityManager em = getCurrentEntityManager(); Employee employee = em.find(Employee.class, entityId); // 手动merge地址实体,触发Envers生成修订记录 employee.getAddresses().forEach(address -> em.merge(address)); } } // 注意:这里需要根据你的项目环境获取当前EntityManager,比如Spring项目可以注入 private EntityManager getCurrentEntityManager() { // 示例:通过Hibernate Session获取,或者用Spring的@PersistenceContext注入 return Persistence.createEntityManagerFactory("yourPersistenceUnit").createEntityManager(); } }
然后在你的持久化配置里指定这个监听器(比如persistence.xml):
<property name="org.hibernate.envers.revision_listener" value="com.yourpackage.CustomRevisionListener"/>
最后给你提几个注意点:
- 所有需要审计的实体都必须加上
@Audited注解,否则Envers不会处理 - 用
@AuditMappedBy时,一定要保证关联是双向的,不然注解不会生效 - 手动触发子实体变更时,如果子实体数量很大,可能会影响性能,要注意优化
备注:内容来源于stack exchange,提问作者pingo_bingo
相关产品推荐
相关产品推荐

