Chapter 5:Inheritance and custom types<o:p></o:p>
5.1 Mapping class inheritance<o:p></o:p>
最简单的映射策略是“一个持久类对应一张表”,但在面向对象继承体系中,这种策略就不足够,SQL支持是‘has a’关系,而java支持‘has a’和‘is a’,SQL数据库并不支持类型继承,这就需要我们寻找新的策略来解决继承问题:<o:p></o:p>
一般有4中策略:<o:p></o:p>
■ Table per concrete class with implicit polymorphism—Use no explicit inheritance mapping, and default runtime polymorphic behavior.<o:p></o:p>
■ Table per concrete class—Discard polymorphism and inheritance relationships<o:p></o:p>
completely from the SQL schema.<o:p></o:p>
■ Table per class hierarchy—Enable polymorphism by denormalizing the SQL<o:p></o:p>
schema, and utilize a type discriminator column that holds type information.<o:p></o:p>
■ Table per subclass—Represent is a (inheritance) relationships as has a (foreign<o:p></o:p>
key) relationships.<o:p></o:p>
<o:p> </o:p>
<o:p> </o:p>
<st1:chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">5.1.1</st1:chsdate> Table per concrete class with implicit polymorphism<o:p></o:p>
Every concrete subclass is mapping to a table, the solution is so straightforward,<o:p></o:p>
But there are several problems with this approach.<o:p></o:p>
1, we can’t get a foreign key to super class to make sure the polymorphism in model domain, <o:p></o:p>
or when there is a “many to one” relation from another model to the Billing; for example ,:User n:1 Billing, we must deal with the both concrete subclasses tables.<o:p></o:p>
2, query problems:<o:p></o:p>
A SQL query against the super class must be executed several SQL query, one for each concrete subclass.<o:p></o:p>
3, Conceptual problem:<o:p></o:p>
Different columns of subclass tables share the same semantics: <o:p></o:p>
When a property of super class changed, we must update all the corresponding columns in different subclass tables, it’s such a terrible job…<o:p></o:p>
<o:p> </o:p>
With so many problems, so this approach is recommend only for the top lever of class hierarchy, where the polymorphism isn’t usually required and when the modification of the super class in future is unlikely<o:p></o:p>
<o:p> </o:p>
<st1:chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">5.1.2</st1:chsdate> Table per concrete class with unions<o:p></o:p>
In this situation, we still have two tables with duplicate column in bath.<o:p></o:p>
<hibernate-mapping><o:p></o:p></hibernate-mapping>
<class><o:p></o:p></class>
<<class<o:p></o:p>
name="BillingDetails"<o:p></o:p>
abstract="true"> // an abstract class (interface ) must be declared as “true”<o:p></o:p>
//otherwise there needs a single table for superclass<o:p></o:p>
<id<o:p></o:p>
name="id"<o:p></o:p>
column="BILLING_DETAILS_ID"<o:p></o:p>
type="long"> // the id property is shared by all subclass<o:p></o:p>
<generator class="native"/><o:p></o:p>
</id><o:p></o:p>
<property<o:p></o:p>
name="name"<o:p></o:p>
column="OWNER"<o:p></o:p>
type="string"/> // superclass’s properties are shared by all subclasses<o:p></o:p>
<o:p> </o:p>
...<o:p></o:p>
<union-subclass<o:p></o:p>
name="CreditCard" table="CREDIT_CARD"><o:p></o:p>
<property name="number" column=”NUMBER”/><o:p></o:p>
<property name="expMonth" column="EXP_MONTH"/><o:p></o:p>
<property name="expYear" column="EXP_YEAR"/><o:p></o:p>
</union-subclass><o:p></o:p>
<union-subclass<o:p></o:p>
name="BankAccount" table="BANK_ACCOUNT"><o:p></o:p>
...<o:p></o:p>
</class><o:p></o:p>
</hibernate-mapping>
<o:p></o:p>
With this strategy, we still need two tables and duplicate columns in tables, but the first advantage is that we don’t need to declare duplicate mapping for tables, <o:p></o:p>
<o:p> </o:p>
If the superclass is a concrete class, you should need an additional table for holding the instances of the class.<o:p></o:p>
Be sure that from the view of tables, we can’t see any relation between them. <o:p></o:p>
<o:p> </o:p>
The second advantage is that we can do a better query now:<o:p></o:p>
select<o:p></o:p>
BILLING_DETAILS_ID, OWNER,<o:p></o:p>
NUMBER, EXP_MONTH, EXP_YEAR,<o:p></o:p>
ACCOUNT, BANKNAME, SWIFT<o:p></o:p>
CLAZZ_<o:p></o:p>
from<o:p></o:p>
( select<o:p></o:p>
BILLING_DETAILS_ID, OWNER,<o:p></o:p>
NUMBER, EXP_MONTH, EXP_YEAR,<o:p></o:p>
null as ACCOUNT, null as BANKNAME, null as SWIFT,<o:p></o:p>
1 as CLAZZ_<o:p></o:p>
from<o:p></o:p>
CREDIT_CARD<o:p></o:p>
union<o:p></o:p>
select<o:p></o:p>
BILLING_DETAILS_ID, OWNER,<o:p></o:p>
null as NUMBER, null as EXP_MONTH, null as EXP_YEAR, ...<o:p></o:p>
ACCOUNT, BANKNAME, SWIFT,<o:p></o:p>
2 as CLAZZ_<o:p></o:p>
from<o:p></o:p>
BANK_ACCOUNT<o:p></o:p>
)<o:p></o:p>
将表中没有,但其他类中有的字段设为null,最后统一查询所有的字段,Hibernate本身能判断是到底要实例化为哪个具体的子类或父类。<o:p></o:p>
<o:p> </o:p>
The third advantage is that we can handle the domain polymorphism relations now, <o:p></o:p>
Just make relations to the superclass.<o:p></o:p>
<o:p> </o:p>
<st1:chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">5.1.3</st1:chsdate> Table per class hierarchy<o:p></o:p>
In this strategy, all the properties in the class hierarchy are mapping to a single table,<o:p></o:p>
The concrete subclass is represented by a column is identified by the value of the discriminator.<o:p></o:p>
<o:p></o:p>
This approach is so simple and straightforward, it supports the polymorphism very well.<o:p></o:p>
But to be noticed, the columns in the table must be declared as nullable, the second is that this approach may not satisfy the normalization, <o:p></o:p>
Mapping in Hibernate XML files:<o:p></o:p>
<hibernate-mapping><o:p></o:p>
<class<o:p></o:p>
name="BillingDetails"<o:p></o:p>
table="BILLING_DETAILS"> // the class hierarchy is mapped to a single table<o:p></o:p>
<id<o:p></o:p>
name="id"<o:p></o:p>
column="BILLING_DETAILS_ID"<o:p></o:p>
type="long"><o:p></o:p>
<generator class="native"/><o:p></o:p>
</id><o:p></o:p>
<discriminator<o:p></o:p>
column="BILLING_DETAILS_TYPE"<o:p></o:p>
type="string"/> // the column to distinguish the concrete subclasses, Using // by hibernate internally <o:p></o:p>
<property<o:p></o:p>
name="owner"<o:p></o:p>
column="OWNER"<o:p></o:p>
type="string"/><o:p></o:p>
...<o:p></o:p>
<subclass // subclass mapping, must declare the discriminator value<o:p></o:p>
name="CreditCard"<o:p></o:p>
discriminator-value="CC"><o:p></o:p>
<property name="number" column="CC_NUMBER"/><o:p></o:p>
<property name="expMonth" column="CC_EXP_MONTH"/><o:p></o:p>
<property name="expYear" column="CC_EXP_YEAR"/><o:p></o:p>
</subclass><o:p></o:p>
<subclass<o:p></o:p>
name=”BankAccount”<o:p></o:p>
discriminator-value=”BA”><o:p></o:p>
...<o:p></o:p>
</class>
Hibernate generates the following SQL when querying the BillingDetails<o:p></o:p>
class:<o:p></o:p>
select<o:p></o:p>
BILLING_DETAILS_ID, BILLING_DETAILS_TYPE, OWNER,<o:p></o:p>
CC_NUMBER, CC_EXP_MONTH, ..., BA_ACCOUNT, BA_BANKNAME, ...<o:p></o:p>
from BILLING_DETAILS<o:p></o:p>
To query the CreditCard subclass, Hibernate adds a restriction on the discriminator<o:p></o:p>
column:<o:p></o:p>
select BILLING_DETAILS_ID, OWNER, CC_NUMBER, CC_EXP_MONTH, ...<o:p></o:p>
from BILLING_DETAILS<o:p></o:p>
where BILLING_DETAILS_TYPE='CC'<o:p></o:p>
all the detail is managed by hibernate internally.<o:p></o:p>
<o:p> </o:p>
Note: this strategy may be too serious to your database schema design, because it doesn’t satisfy normalizations and guarantee the integrity of the data.<o:p></o:p>
<o:p> </o:p>
<o:p> </o:p>
<st1:chsdate w:st="on" year="1899" month="12" day="30" islunardate="False" isrocdate="False">5.1.4</st1:chsdate> Table per subclass<o:p></o:p>
Every class is mapped to a table in despite of abstract, concrete class or interface, the table only holds the properties in the corresponding class.<o:p></o:p>
When an object of subclass is persisted, the properties declared in superclass are persisted to a new row of the superclass_table, only the properties declared in subclass are persisted to a new row of the corresponding subclass table, and the two rows in different tables share the same identity.<o:p></o:p>
<o:p> </o:p>
<o:p></o:p>
Using hibernate <join-subclass> property:<o:p></o:p></join-subclass>
<o:p> </o:p>
<hibernate-mapping><o:p></o:p>
<class<o:p></o:p>
name="BillingDetails"<o:p></o:p>
table="BILLING_DETAILS"><o:p></o:p>
<id<o:p></o:p>
name="id"<o:p></o:p>
column="BILLING_DETAILS_ID"<o:p></o:p>
type="long"><o:p></o:p>
<generator class="native"/><o:p></o:p>
L.3 Hibernate <joined-subclass> mapping<o:p></o:p>
</id><o:p></o:p>
<property<o:p></o:p>
name="owner"<o:p></o:p>
column="OWNER"<o:p></o:p>
type="string"/><o:p></o:p>
...<o:p></o:p>
<joined-subclass<o:p></o:p>
name="CreditCard" <o:p></o:p>
table="CREDIT_CARD"><o:p></o:p>
<key column="CREDIT_CARD_ID"/> // share the same id with subclass table<o:p></o:p>
<o:p> </o:p>
<property name="number" column="NUMBER"/><o:p></o:p>
<property name="expMonth" column="EXP_MONTH"/><o:p></o:p>
<property name="expYear" column="EXP_YEAR"/><o:p></o:p>
</joined-subclass><o:p></o:p>
<joined-subclass<o:p></o:p>
name="BankAccount"<o:p></o:p>
table="BANK_ACCOUNT"><o:p></o:p>
...<o:p></o:p>
</class><o:p></o:p>
</hibernate-mapping><o:p></o:p>
This strategy can well support the polymorphism in domain models, but if there is a very complex classes’ hierarchy, this approach will be unacceptable, the cost of query will be a nightmare because every query requires a join cross several tables.
<st1:chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">5.1.5</st1:chsdate> Mixing inheritance strategies<o:p></o:p>
<o:p> </o:p>
Of course, we can mix the strategies discussed above, for the third strategy, we can add a <join> (the fourth strategy), </join>
<o:p> </o:p>
<hibernate-mapping><o:p></o:p>
<class name="BillingDetails"<o:p></o:p>
table="BILLING_DETAILS"><o:p></o:p>
<id>...</id><o:p></o:p>
<discriminator<o:p></o:p>
column="BILLING_DETAILS_TYPE"<o:p></o:p>
type="string"/><o:p></o:p>
...<o:p></o:p>
<subclass<o:p></o:p>
name="CreditCard"<o:p></o:p>
discriminator-value="CC"><o:p></o:p>
<join table="CREDIT_CARD"><o:p></o:p>
<key column="CREDIT_CARD_ID"/><o:p></o:p>
<property name="number" column="CC_NUMBER"/><o:p></o:p>
<property name="expMonth" column="CC_EXP_MONTH"/><o:p></o:p>
<property name="expYear" column="CC_EXP_YEAR"/><o:p></o:p>
...<o:p></o:p>
</join><o:p></o:p>
</subclass><o:p></o:p>
<subclass<o:p></o:p>
name="BankAccount"<o:p></o:p>
discriminator-value="BA"><o:p></o:p>
<property name=account" column="BA_ACCOUNT"/><o:p></o:p>
...<o:p></o:p>
</subclass><o:p></o:p>
...<o:p></o:p>
</class><o:p></o:p>
</hibernate-mapping><o:p></o:p>
<o:p></o:p>
<o:p></o:p>
...<o:p></o:p>
<o:p></o:p>
<o:p></o:p>
Also you can add more <join> in you hierarchy, but if the class hierarchy is wide, it will be a problem, some database limit the number of outer_join.</join><st1:chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">5.1.6</st1:chsdate> Choosing a strategy<o:p></o:p>
<o:p> </o:p>
Now we know 4 strategies for mapping class hierarchy to database tables, how to choose?
1 if you don’t required polymorphism queries and associations, the second strategy is better(per table concrete class),
2 if you required polymorphism queries and associations, but there are few properties declared in subclass, just use the third strategy(per table a class hierarchy).
3 if you required polymorphism queries and associations, but there are many properties declared in subclass, just use the fourth(table per subclass), or depending on the width and depth of the class hierarchy, considering the second strategy(table per concrete class).
<o:p> </o:p>
Per table a class hierarchy is only for simple problems, for more complex cases, you should consider Per table a subclass, but at the point, you should first ask yourself whether you should redesign your models.