Hibernate Envers关联审计表缺失:如何正确定义该表?
Hibernate Envers关联审计表缺失问题解决
问题描述
我正在使用Hibernate Envers为实体添加历史审计功能,已定义Handbook和Chapter实体,对应HANDBOOK、HANDBOOK_AUD、CHAPTER、CHAPTER_AUD及REVINFO五张表。当两个实体无关联时一切正常,但为Handbook和Chapter添加OneToMany关联后,应用启动失败,提示缺失HANDBOOK_CHAPTER_AUD表。我知道需要该关联审计表,但不清楚如何正确定义。
现有代码与配置
Handbook实体
@Entity @Audited @Getter @Setter @NoArgsConstructor public class Handbook { @Id @SequenceGenerator(name = "HANDBOOK_ID_SEQUENCE", sequenceName = "HANDBOOK_ID_SEQUENCE", allocationSize = 1) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "HANDBOOK_ID_SEQUENCE") private Long id; private String title; @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "HANDBOOK_ID") @AuditJoinTable(name = "REV_HANDBOOK_CHAPTER") private Set<Chapter> chapters; }
Chapter实体
@Entity @Audited @Getter @Setter @NoArgsConstructor public class Chapter { @Id @SequenceGenerator(name = "CHAPTER_ID_SEQUENCE", sequenceName = "CHAPTER_ID_SEQUENCE", allocationSize = 1) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CHAPTER_ID_SEQUENCE") private Long id; private String name; @Column(name = "HANDBOOK_ID") private Long handbookId; }
表定义
<createTable tableName="REVINFO"> <column name="rev" type="integer"> <constraints primaryKey="true" nullable="false"/> </column> <column name="revtstmp" type="bigint"/> </createTable> <!-- Hibernate Envers需要此序列来递增版本化实体的修订号--> <createSequence sequenceName="HIBERNATE_SEQUENCE" startValue="1" incrementBy="1"/> <createTable tableName="HANDBOOK"> <column name="ID" type="bigint"> <constraints primaryKey="true" nullable="false"/> </column> <column name="TITLE" type="varchar2(128 char)"/> </createTable> <createTable tableName="REV_HANDBOOK"> <column name="ID" type="bigint"/> <column name="TITLE" type="varchar2(128 char)"/> <column name="REV_ID" type="integer"> <constraints foreignKeyName="FK_HANDBOOK_REV" references="REVINFO(REV)"/> </column> <column name="REV_TYPE" type="smallint"/> </createTable> <!-- CHAPTER 表 --> <createTable tableName="CHAPTER"> <column name="ID" type="bigint"> <constraints primaryKey="true" nullable="false"/> </column> <column name="NAME" type="varchar2(128 char)"/> <column name="HANDBOOK_ID" type="integer"> <constraints foreignKeyName="FK_CHAPTER_HANDBOOK_ID" references="HANDBOOK(ID)"/> </column> </createTable> <createTable tableName="REV_CHAPTER"> <column name="ID" type="bigint"/> <column name="NAME" type="varchar2(128 char)"/> <column name="HANDBOOK_ID" type="integer"> <constraints foreignKeyName="FK_CHAPTER_AUD_HANDBOOK_ID" references="HANDBOOK(ID)"/> </column> <column name="REV_ID" type="integer"> <constraints foreignKeyName="FK_CHAPTER_REV" references="REVINFO(REV)"/> </column> <column name="REV_TYPE" type="smallint"/> </createTable>
Envers配置
org: hibernate: envers: audit_table_prefix: REV_ audit_table_suffix: ~ # 无后缀 revision_field_name: REV_ID revision_type_field_name: REV_TYPE
解决方案
1. 修正实体关联映射(核心)
当前代码存在映射冲突:Handbook用@OneToMany+@JoinColumn定义单向关联,同时Chapter手动维护handbookId字段,导致Envers误判关联类型,预期生成中间关联表。
修改Chapter实体,用双向关联替代手动外键字段:
@Entity @Audited @Getter @Setter @NoArgsConstructor public class Chapter { @Id @SequenceGenerator(name = "CHAPTER_ID_SEQUENCE", sequenceName = "CHAPTER_ID_SEQUENCE", allocationSize = 1) @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CHAPTER_ID_SEQUENCE") private Long id; private String name; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "HANDBOOK_ID") // 可选:如果不需要在Chapter审计表中重复存储Handbook的审计信息,添加此注解 // @Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) private Handbook handbook; }
同步修改Handbook的关联定义:
@OneToMany(mappedBy = "handbook", cascade = CascadeType.ALL, orphanRemoval = true) // 移除@AuditJoinTable,因为双向OneToMany基于外键,无需中间审计表 private Set<Chapter> chapters;
2. 若需自定义关联审计表(可选)
如果坚持使用单向关联并自定义关联审计表,需创建对应表结构,并确保@AuditJoinTable名称与表名一致:
添加REV_HANDBOOK_CHAPTER表定义:
<createTable tableName="REV_HANDBOOK_CHAPTER"> <!-- 关联的Handbook主键 --> <column name="HANDBOOK_ID" type="bigint"> <constraints foreignKeyName="FK_REV_HANDBOOK_CHAPTER_HANDBOOK" references="HANDBOOK(ID)"/> </column> <!-- 关联的Chapter主键 --> <column name="CHAPTER_ID" type="bigint"> <constraints foreignKeyName="FK_REV_HANDBOOK_CHAPTER_CHAPTER" references="CHAPTER(ID)"/> </column> <!-- 审计版本号 --> <column name="REV_ID" type="integer"> <constraints foreignKeyName="FK_REV_HANDBOOK_CHAPTER_REVINFO" references="REVINFO(REV)"/> </column> <!-- 操作类型(新增/修改/删除) --> <column name="REV_TYPE" type="smallint"/> <!-- 复合主键 --> <constraints primaryKey="true" columnNames="HANDBOOK_ID, CHAPTER_ID, REV_ID"/> </createTable>
3. 验证配置匹配
Envers配置中audit_table_prefix: REV_和audit_table_suffix: ~决定了审计表命名规则:
- 若使用默认关联审计表,名称应为
HANDBOOK_CHAPTER_AUD - 若自定义
@AuditJoinTable(name = "REV_HANDBOOK_CHAPTER"),必须确保该表已创建,否则会触发缺失表错误
总结
- 优先采用双向关联,避免手动维护外键字段,让Envers自动处理关联审计
- 单向OneToMany基于外键时,无需
@AuditJoinTable,Envers会通过Chapter的审计表记录关联变化 - 自定义关联审计表时,需严格匹配表名与注解配置,并创建正确的表结构
内容的提问来源于stack exchange,提问作者stefan.stt
相关产品推荐
相关产品推荐

