如何基于Javers/Envers+Hibernate实现子对象修改时父实体版本化审计?
实现父对象及其子对象修改时的版本审计需求
问题背景
我有一类需要版本化(审计)的父对象,要求:当父对象自身或其任意层级的子对象被修改并持久化时,必须为父实体创建新的修订版本。
实体代码示例
@Entity class Document { @Id String name; // 其他属性... @OneToMany List<Page> pages; // getter和setter方法... } @Entity class Page { @Id Long number; @ManyToOne Document document; // 其他属性... String header; // getter和setter方法... }
需求示例
比如修改某Page的header时,需要为对应的Document生成新修订版本:
// 获取文档 Document document = documentRepository.findById(myBookId); // 修改第一个页面的header document.getPages().iterator().next().setHeader("Hello World"); // 持久化文档 documentRepository.save(document); // 此时需要生成Document的新修订版本!
我原本考虑使用Javers或Envers,但似乎二者都无法直接实现该需求,请问如何通过这些库实现该功能?或者有其他可用类库推荐吗?
解决方案
用Javers实现
其实Javers完全可以搞定这个需求,核心是让Javers把子对象的变更识别为父对象的一部分变更:
- 给父实体
Document添加@JaversAuditable注解,同时在Javers配置中,将Page标记为ValueObject(因为Page是Document的从属对象,没有独立存在的意义),或者配置Javers对集合进行深度比较。 - 当你保存Document时,调用
javers.commit("操作人", document),Javers会自动检测到子对象Page的属性变更,将其纳入Document的版本变更记录,从而为Document创建新的修订版本。 - 如果你不想把Page设为ValueObject,也可以在Javers的
JaversBuilder中配置withMappingStyle(MappingStyle.BEAN),让Javers深度遍历实体的关联关系,捕捉子对象的变更。
用Hibernate Envers实现
Envers默认是按单个实体审计的,但可以通过关联触发父对象版本更新来实现需求:
- 先给
Document和Page都加上@Audited注解。 - 方法一:给
Document添加一个用于触发版本更新的字段(比如Long versionTrigger),然后自定义一个EntityListener,监听Page的postUpdate事件,在事件中更新对应Document的versionTrigger字段(比如每次加1)。因为Envers会检测父对象的字段变更,只要versionTrigger更新,就会为Document生成新的修订版本。 - 方法二:利用Envers的
@AuditMappedBy和@AuditJoinTable配置关联关系的审计,让子对象的变更被关联到父对象的审计记录中。不过这种方式需要调整实体间的关联注解配置,确保Envers能追踪到关联变更。
其他可选类库
如果Javers和Envers的配置对你来说有点繁琐,还可以试试这些工具:
- Spring Data Envers:基于Hibernate Envers的Spring Data扩展,简化了审计版本的查询和管理,能更便捷地关联父对象与子对象的审计记录。
- Audit4j:轻量级审计框架,支持自定义审计规则,你可以编写审计拦截器,在子对象变更时主动触发父对象的版本创建。
内容的提问来源于stack exchange,提问作者Lucas Noetzold
相关产品推荐
相关产品推荐

