You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 08:05:14