整个类层次结构可以被映射到单张表。这张表把所有类的所有属性的列都包括在层次结构中。由特定行表示的具体子类通过一个类型辨别标志列的值进行识别。
这个映射策略在性能和简单性方面都胜出一筹。它是表示多态的最佳方法——多态和非多态的查询都执行得很好——并且更易于手工实现。不用复杂的联结或者联合也有可能生成特殊的报表。有一个重大的问题:子类声明的属性的列必须声明为可为空。如果每个子类都定义几个可为空的属性,从数据库完整性的角度来说,没有NOT NULL约束可能会是一个严重的问题。另一个重要的问题是标准化。我们已经创建了非键列之间的依赖,违背了第三范式。跟往常一样,性能的反规范化可能会令人误解,因为它为了也能通过SQL执行计划的适当优化而也可能实现的即时获取,却牺牲了数据的长期稳定性、可维护性和数据完整性。
在Hibernate中,用<subclass>元素给每个类层次结构映射创建一张表:
<hibernate-mapping package="cn.jbit.hibernate.entity3"> <class name="BillingDetails4" table="BILLING_DETAILS"> <id name="id" column="BILLING_DETAILS_ID" type="integer"> <generator class="native"/> </id> <discriminator column="BILLING_DETAILS_TYPE" type="string" /> <property name="owner"> <column name="OWNER" not-null="true"/> </property> <subclass name="CreditCard4" discriminator-value="CC"> <property name="number" column="CC_NUMBER" length="20" not-null="true"/> <property name="expMonth" column="EXP_MONTH" length="20" not-null="true"/> <property name="expYear" column="EXP_YEAR" length="20" not-null="true"/> </subclass> </class> </hibernate-mapping>JPA中也有这个映射策略,即SINGLE_TABLE:
@Entity @Table(name = "BILLING_DETAILS2") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn( name = "BILLING_DETAILS_TYPE", discriminatorType = DiscriminatorType.STRING ) public abstract class BillingDetails5 { @Id @GeneratedValue @Column(name = "BILLING_DETAILS_ID", nullable = false) private Integer id; @Column(name = "OWNER", nullable = false) private String owner; }如果没有在超类中指定辨别标志列,它的名称就默认为DTYPE,且它的类型默认为字符串。继承层次结构中所有具体的类都可以有辨别标志值。
@Entity @Table(name = "CREDIT_CARD2") @DiscriminatorValue("CC") public class CreditCard5 extends BillingDetails5 { @Column(name = "CC_NUMBER", nullable = false) private String number; @Column(name = "EXP_MONTH", nullable = false) private String expMonth; @Column(name = "EXP_YEAR", nullable = false) private String expYear; }没有显式的辨别标志值时,如果使用Hibernate XML文件,Hibernate就默认认为完全限定的类名;如果使用注解或者JPA XML文件,则默认为实体名称。
以下是JPA XML描述符中与之相当的映射:
<entity class="cn.jbit.hibernate.entity3.BillingDetails3" access="FIELD"> <inheritance strategy="SINGLE_TABLE"/> <discriminator-column name="BILLING_DETAILS_TYPE" discriminator-type="STRING"/> <attributes> <id name="id"> <column name="BILLING_DETAILS_ID" nullable="false"/> <generated-value strategy="AUTO"/> </id> <basic name="owner"> <column name="OWNER" nullable="false"/> </basic> </attributes> </entity> <entity class="cn.jbit.hibernate.entity3.CreditCard3" access="FIELD"> <discriminator-value>CC</discriminator-value> <attributes> <basic name="number"> <column name="CC_NUMBER" nullable="false"/> </basic> <basic name="expMonth"> <column name="EXP_MONTH" nullable="false"/> </basic> <basic name="expYear"> <column name="EXP_YEAR" nullable="false"/> </basic> </attributes> </entity>有时候,尤其在遗留的Schema中,你无权在实体表中包括一个额外的辨别标志列。此时,可以应用一个formula来计算每一行的辨别标志值。
<discriminator formula="case when CC_NUMBER is not null then ‘CC‘ else ‘BA‘ end" type="string" />用于辨别的公式不是JPA规范的一部分,但是可以应用Hibernate注解:
@org.hibernate.annotations.DiscriminatorFormula( "case when CC_NUMBER is not null then ‘CC‘ else ‘BA‘ end" )每个类层次结构一张表的策略的缺点对于你的设计来说可能太严重了——毕竟,反规范化的Schema长期而言会变成一个重大的负担。你的数据库管理员根本不可能喜欢它。