@ManyToOne关联审计异常:entity_b_audit中entity_a_id始终为空求助
问题分析
你当前的核心问题是:entity_b_audit表中entity_a_id字段始终为空,原因在于EntityA的自增ID是插入数据库后才生成的,而Envers审计时无法捕获这个延迟生成的关联ID;同时为了避免Envers生成不需要的中间审计表,你在EntityA的OneToMany关联上添加了@NotAudited,进一步导致关联字段的审计逻辑缺失。
解决方案
方案一:显式维护EntityB的关联ID字段(无需改动ID结构)
修改EntityB,新增一个可插入的entityAId字段,手动同步关联的EntityAID,确保Envers能审计到该字段:
@Entity @Audited @Table(name = "entity_b") public class EntityB { @Id @Column(name = "entity_b_uuid", columnDefinition = "char", updatable = false) @Type(type = "uuid-char") private UUID uuid = UUID.randomUUID(); // 新增显式存储的entity_a_id,允许插入 @Column(name = "entity_a_id", nullable = false) private Integer entityAId; @ManyToOne(optional = false, fetch = FetchType.LAZY) @JoinColumn(name = "entity_a_id", insertable = false, updatable = false) private EntityA entityA; // 关联EntityA时同步设置ID public void setEntityA(EntityA entityA) { this.entityA = entityA; this.entityAId = entityA.getId(); } }
同时在EntityA中添加关联方法,确保同步设置:
public void addEntityB(EntityB entityB) { entityBs.add(entityB); entityB.setEntityA(this); }
这样保存EntityB时,显式的entityAId会被Envers审计,存入entity_b_audit表。
方案二:改用UUID作为EntityA的主键(推荐,符合代码设计意图)
既然EntityA的自增ID已标记为@Deprecated,直接将UUID设为主键,避免ID延迟生成的问题:
修改EntityA
@Entity @Table(name = "entity_a") @Audited public class EntityA { @Id // 将UUID设为主键 @Column(name = "entity_a_uuid", columnDefinition = "char", updatable = false) @Type(type = "uuid-char") private UUID uuid = UUID.randomUUID(); /** * @deprecated in favor of uuid */ @Deprecated @GeneratedValue @Column(name = "entity_a_id", updatable = false) private Integer id; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "entity_a_uuid", nullable = false) // 关联改为UUID字段 @BatchSize(size = 100) @NotAudited private List<EntityB> entityBs = new ArrayList<>(); }
修改EntityB
@Entity @Audited @Table(name = "entity_b") public class EntityB { @Id @Column(name = "entity_b_uuid", columnDefinition = "char", updatable = false) @Type(type = "uuid-char") private UUID uuid = UUID.randomUUID(); @Column(name = "entity_a_uuid", nullable = false) private UUID entityAUuid; @ManyToOne(optional = false, fetch = FetchType.LAZY) @JoinColumn(name = "entity_a_uuid", insertable = false, updatable = false) private EntityA entityA; public void setEntityA(EntityA entityA) { this.entityA = entityA; this.entityAUuid = entityA.getUuid(); } }
此方案从根源解决了ID延迟生成的问题,同时符合代码中弃用自增ID的设计,审计表中会正确存储entity_a_uuid字段。
方案三:自定义Envers审计监听器(无实体/表结构改动)
如果无法修改实体或表结构,可自定义审计监听器,手动填充关联字段:
public class CustomEntityBAuditListener extends AuditEventListener { @Override public void onPostInsert(PostInsertEvent event) { super.onPostInsert(event); if (event.getEntity() instanceof EntityB) { EntityB entityB = (EntityB) event.getEntity(); EntityA entityA = entityB.getEntityA(); if (entityA.getId() != null) { AuditReader auditReader = AuditReaderFactory.get(event.getSession()); List<Number> revisions = auditReader.getRevisions(EntityB.class, entityB.getUuid()); if (!revisions.isEmpty()) { Number latestRevision = revisions.get(revisions.size() - 1); EntityB auditedEntityB = auditReader.find(EntityB.class, entityB.getUuid(), latestRevision); auditedEntityB.setEntityAId(entityA.getId()); event.getSession().saveOrUpdate(auditedEntityB); } } } } }
在Hibernate配置中注册监听器:
<property name="hibernate.ejb.event.post-insert" value="com.yourpackage.CustomEntityBAuditListener"/>
此方案实现复杂,需处理事务同步,仅推荐在无法修改实体时使用。
内容的提问来源于stack exchange,提问作者braab
相关产品推荐
相关产品推荐

