将Hibernate混合继承从hbm.xml迁移至orm.xml的问题
Java类层级

需求
将整个类层级存储在名为file的单表中,但SpecialOutFile需要存储在专属的special_file表中。
原HBM.xml实现(可满足需求)
通过hbm.xml映射可以完美实现该需求:
<?xml version="1.0" encoding="UTF-8"?> <hibernate-mapping xmlns="http://www.hibernate.org/xsd/hibernate-mapping" default-access="field" default-cascade="all" default-lazy="true"> <class name="com.example.demo.File"> <id name="id"/> <discriminator column="file_type"/> <subclass name="com.example.demo.InFile" discriminator-value="InFile"/> <subclass name="com.example.demo.TypedOutFile1" discriminator-value="TypedOutFile1"/> <subclass name="com.example.demo.TypedOutFile2" discriminator-value="TypedOutFile2"/> <subclass name="com.example.demo.OutFile" discriminator-value="OutFile"/> </class> <class name="com.example.demo.SpecialOutFile" table="special_file"> <id name="id"/> <property name="moreData"/> </class> </hibernate-mapping>
- 持久化
OutFile时,仅在file表插入一条记录; - 持久化
SpecialOutFile时,仅在special_file表插入一条记录。
Hibernate 6迁移问题
由于Hibernate 6中hbm.xml已被废弃,需迁移至orm.xml,但无法复现相同行为:
- 当前采用secondary table方式配置时,持久化
SpecialOutFile会同时在file和special_file表各插入一条记录:
Hibernate: insert into file (some_data, type, id) values (?, 'SpecialOutFile', ?) Hibernate: insert into special_file (more_data, id) values (?, ?)
当前orm.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?> <entity-mappings version="2.0" xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_2_0.xsd"> <access>FIELD</access> <entity class="com.example.demo.File"> <discriminator-column name="type"/> <attributes> <id name="id"/> </attributes> </entity> <entity class="com.example.demo.InFile"> <discriminator-value>InFile</discriminator-value> </entity> <entity class="com.example.demo.TypedOutFile1"> <discriminator-value>TypedOutFile1</discriminator-value> </entity> <entity class="com.example.demo.TypedOutFile2"> <discriminator-value>TypedOutFile2</discriminator-value> </entity> <entity class="com.example.demo.OutFile"> <discriminator-value>OutFile</discriminator-value> </entity> <entity class="com.example.demo.SpecialOutFile"> <secondary-table name="special_file"/> <attribute-override name="id"> <column table="special_file"/> </attribute-override> <attributes> <basic name="moreData"> <column table="special_file"/> </basic> </attributes> </entity> </entity-mappings>
请问能否将SpecialOutFile的所有继承属性移至secondary table,实现与hbm.xml一致的仅在special_file表存储的效果?
不能直接通过secondary table实现该效果,因为JPA的secondary table机制本质是将实体的属性拆分存储到主表和副表,主表必须存在记录,这和你需要的“完全脱离主表、仅存副表”的逻辑冲突。
要在Hibernate 6中复现原hbm.xml的行为,可采用以下方案:
将
SpecialOutFile配置为独立实体
如果业务允许,直接切断与File的继承关系,把SpecialOutFile定义为独立JPA实体,映射到special_file表,自行维护原继承属性(如id、someData等)。使用Hibernate扩展注解实现子类独立表
若必须保留继承关系,可通过注解而非orm.xml实现:- 父类
File使用@Inheritance(strategy = InheritanceType.SINGLE_TABLE),指定主表和鉴别器列; - 除
SpecialOutFile外的子类用@DiscriminatorValue指定鉴别值; SpecialOutFile使用Hibernate扩展的@Subclass注解,配合@Table(name = "special_file")指定单独表,跳过主表写入逻辑。
对应代码示例:
@Entity @Table(name = "file") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "type") public class File { @Id private Long id; private String someData; // getter/setter } @Entity @DiscriminatorValue("InFile") public class InFile extends File {} @Entity @DiscriminatorValue("TypedOutFile1") public class TypedOutFile1 extends File {} @Entity @DiscriminatorValue("TypedOutFile2") public class TypedOutFile2 extends File {} @Entity @DiscriminatorValue("OutFile") public class OutFile extends File {} @Entity @Table(name = "special_file") @Subclass public class SpecialOutFile extends File { private String moreData; // getter/setter }- 父类
ORM.xml替代方案(需调整继承逻辑)
JPA标准中没有hbm.xml那种“子类完全脱离主表”的映射方式,若必须使用orm.xml,可将File设为@MappedSuperclass,所有子类(包括SpecialOutFile)作为独立实体分别映射到对应表,但会丢失单表继承的鉴别器逻辑,需自行处理类型判断。
总结:最贴近原hbm.xml效果的方案是使用Hibernate扩展的@Subclass注解配合@Table,让SpecialOutFile作为子类但仅写入special_file表。若必须用orm.xml,只能通过将SpecialOutFile设为独立实体来实现需求。
内容的提问来源于stack exchange,提问作者Bastien Jansen

