第三种做法就是使用继承关系来表示外键关系类型。每一个子类都声明持久化属性-甚至包括抽象类和接口,都有自己的表。
与每张表一个具体的类不同,这里表中的列支敦那些非继承过来的属性有效,主键也是父类对应的表的外键。这种方法可以在图3.9中看到。
如果CreditCard类的对象被持久化的话,那么继承自父类BillingDetails的属性被持久化为表BILLING_DETAILS的行。而那些非继承的属性才会被持久化为CREDIT_CARD表的行。这两张表通过其共享的主键值联系在一起。而子类实例的获取可能通过和父类对应表格的连接来完成。
这种策略的最主要优势就是关系模型被格式化了。Schema的变更和完整性约束更加直观。子类的多态关联也可以被表示为外键关系。
在hibernate中,我们使用<joined-subclass>元素来指定这种映射关系,参见列表3.9。
(1)基类BillingDetails仍然被映射到BILLING_DETAILS表。注意这种策略不再需要discriminator。
(2)新的<joined-subclass>被用来映射到新表对应的子类。在连接的子类中所有的属性都将被映射到表中。注意我们并没有列举BankAccount的例子,因为它和CreditCard很相似。
(3)CREDIT_CARD表需要一个主键;同时和BILLING_DETAILS表之间存在外键约束。查询CreditCard对象需要在表之间进行连接。
<joined-subclass>可以包含其他的<joined-subclass>元素,但是不能包含<subclass>元素。Hibernate不支持这种类型的混合策略。
Hibernate将会使用外关联来查询BillingDetails类:
SQL的case表达式用来表示具体的子类,是CREDIT_CARD还是BANK_ACCOUNT。
至于子类的查询,Hibernate使用内连接的方式来查询:
你可以看到,这种映射策略手工实现非常复杂。所以在你将Hibernate用于手工书写SQL/JDBC的时候,你必须要慎重考虑。
而且,即使这种策略看起来简单,但是经验告诉我们负责的类体系会带来性能的不可接受性。查询通常都需要进行连接。现在我们的问题回到了如果选择一种合适的映射策略。一种典型的业务模型设计就混合了接口和抽象类。