
hibernate 默认采用 single_table 策略,导致子类 worddoc 被错误插入 basedoc 表;需显式配置 @inheritance 或改用 @mappedsuperclass 才能按预期分表存储。
在 JPA/Hibernate 中,继承关系的持久化行为并非由类结构自动推断,而是严格依赖显式的继承映射策略(Inheritance Strategy)。你当前的实体定义未声明任何策略,因此 Hibernate 默认启用 SINGLE_TABLE 模式——即所有继承体系中的实体共享一个表(以最顶层非抽象类或显式指定的根表为准)。这正是错误的根本原因:
- BaseDoc 是 @Entity 且为抽象类,但仍是实体(非 @MappedSuperclass),Hibernate 将其视为 SINGLE_TABLE 的根表;
- Doc 和 WordDoc 作为其子类,字段(如 version)被强制“扁平化”到 BASEDOC 表中;
- 但你的数据库实际只有 WORD_DOC 表含 version 字段,而 BASEDOC 表缺失该列,故抛出 column "version" does not exist 异常。
✅ 正确解决方案
方案一:使用 JOINED 策略(推荐用于已有分表结构)
适用于你已存在 WORD_DOC 和 BASEDOC 物理表、且希望保持表间一对一继承关系的场景:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "BASEDOC")
public abstract class BaseDoc {
@Id
private Long id;
// 公共字段...
}
@Entity
@Table(name = "DOC")
public class Doc extends BaseDoc {
// Doc 特有字段...
}
@Entity
@Table(name = "WORD_DOC")
public class WordDoc extends Doc {
private String contentType;
// 其他 WordDoc 特有字段...
}? 说明:JOINED 会为每个 @Entity 类生成独立表,通过主键外键关联(如 WORD_DOC.id → DOC.id → BASEDOC.id)。查询时自动 JOIN,插入时分表写入——完全匹配你现有的数据库设计。
方案二:将 BaseDoc 改为 @MappedSuperclass
若 BaseDoc 仅提供公共字段和逻辑,本身不对应任何数据库表(即纯粹是代码复用层),应移除 @Entity,改为:
@MappedSuperclass
public abstract class BaseDoc {
@Id
protected Long id;
@Version
protected Long version; // 现在 version 属于子表
}此时 Doc 和 WordDoc 各自映射到 DOC 和 WORD_DOC 表,BaseDoc 的字段(含 id, version)会被自动继承到各子表中,无需额外配置。
⚠️ 注意:@MappedSuperclass 类不能被查询、不能作为 @ManyToOne 目标,且无独立生命周期。
❌ 不推荐:SINGLE_TABLE(除非重构表结构)
若坚持用 SINGLE_TABLE,则必须合并所有字段到一张表(如全存 BASEDOC),并添加 @DiscriminatorColumn 区分类型,但这与你现有双表结构冲突,会引发严重维护问题。
总结建议
- 优先检查领域语义:BaseDoc 是否真需作为可实例化的实体?若只是模板,用 @MappedSuperclass 最简洁;
- 若需多态查询且表已分离,务必显式声明 @Inheritance(strategy = InheritanceType.JOINED);
- 永远避免隐式策略:在根继承类上明确标注 @Inheritance,杜绝默认行为带来的歧义;
- 配合 @PrimaryKeyJoinColumn 可进一步控制 JOIN 键名(如 name = "doc_id"),提升可读性。
正确配置后,WordDoc 将只插入 WORD_DOC 表,version 等字段自然落库,彻底解决跨表插入异常。









