使用Hibernate Envers审计一对一关联时触发NonUniqueResultException
解决Hibernate Envers查询A审计记录时的NonUniqueResultException异常
问题根源
查询A的审计记录时,Hibernate Envers会自动尝试加载其关联的B的审计数据。由于你的b_audit表保留了对已删除A实体的历史关联,导致多条B审计记录引用同一个A旧实体,而一对一映射要求关联结果必须唯一,因此触发了NonUniqueResultException。
解决方案
方案1:修改审计查询,仅加载A的实体数据
通过指定查询投影,强制Envers只返回A的实例,不初始化任何关联的审计实体,彻底避免对B的审计查询:
AuditQuery auditQuery = auditReader.createQuery() .forRevisionsOfEntity(ClassA.class, false, true) .add(AuditEntity.id().in(entityId)) .addProjection(AuditEntity.selectEntity(false));
selectEntity(false)参数会告诉Envers仅构建并返回A的对象实例,跳过所有关联实体的加载逻辑,从根源解决异常问题。
方案2:同时获取A实体与修订信息(可选)
如果需要同时获取修订号、修订时间等审计元数据,可以在投影中添加对应的字段:
AuditQuery auditQuery = auditReader.createQuery() .forRevisionsOfEntity(ClassA.class, false, true) .add(AuditEntity.id().in(entityId)) .addProjection(AuditEntity.selectEntity(false)) .addProjection(AuditEntity.revisionNumber()) .addProjection(AuditEntity.revisionTimestamp());
此时查询结果为Object[]数组,结构为:[A实例, 修订号, 修订时间戳],同样不会加载关联的B审计数据。
验证说明
删除b_audit中的旧关联记录后异常消失,是因为此时每个A旧实体仅对应一条B审计记录,一对一查询能拿到唯一结果,但这种方式破坏了你保留历史关联的需求,因此上述查询层面的方案才是最优解。
内容的提问来源于stack exchange,提问作者dev1
相关产品推荐
相关产品推荐

