Hibernate 作为ORM,面对的一个问题是:一个方面是关系型的数据库,另一面是Java Object。Java作为一种面向对象的语言支持继承关系。Hibernate本身提供了4种策略将继承关系映射到关系型的数据库中。他们分别是:
- Table Per Concrete Class with Implicit Polymorphism : 每个Concrete Class 对应一张Table。利用Hibernate内部的机制来实现多态型的查询。对于SuperClass Query是分成对Concrete Class的独立Query组合起来的。
- Table Per Concrete Class with Union : 每个Concrete Class 对应一张Table。对于SuperClass Query是通过Union的方式来实现的。
- Table Per Class Hierarchy : 将整个Class Hierachy的关系Mapping到一张Table中,通过discriminator来区分具体的Concrete Class。
- Table Per SubClass:对于Class Hierachy图中的每个类都对应有一张Table。
这些策略各有特点,应用场合不同。在具体实现的时候,应该仔细选择。
一、例子的背景:
假如我们有如下的类的关系,需要将他们mapping到数据库中
每个类的代码如下:
package org.example.hibernate.domain; //BillDetail SuperClass public class BillingDetail { private Long id; private String owner; //省略相应的get/set accessor }
package org.example.hibernate.domain; //Concrete Class BankAccount public class BankAccount extends BillingDetail{ private String account; private String bankName; private String swift; //省略相应的get/set accessor }
package org.example.hibernate.domain; //Concrete Class CreditCard public class CreditCard extends BillingDetail { private String number; private String expMonth; private String expYear; //省略相应的get/set accessor }
二、使用T/CCIP(Table Per Concrete Class with Implicit Polymorphism)策略来映射
1. 只对Concrete Class建立Table,SuperClass中的property,直接mapping到Concrete Class相应Table的column
2. 对于Concrete Class的mapping,就如同没有SuperClass的普通Class一样mapping。
Mapping File如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- CreditCard Class Mapping Section--> <class name="org.example.hibernate.domain.CreditCard" table="CREDITCARD" polymorphism="implicit"> <id name="id" column="IDNUM" type="long"> <generator class="native"/> </id> <property name="owner" type="string" column="OWNER" length="30"/> <property name="number" type="string" column="NUMBER_CODE" length="20"/> <property name="expMonth" type="string" column="EXP_MONTH" length="2"/> <property name="expYear" type="string" column="EXP_YEAR" length="4"/> </class> <!-- BankAccount Class Mapping Section--> <class name="org.example.hibernate.domain.BankAccount" table="BANK_ACCOUNT" polymorphism="implicit"> <id name="id" column="IDNUM" type="long"> <generator class="native"/> </id> <property name="owner" type="string" column="OWNER" length="30"/> <property name="account" type="string" column="ACCOUNT" length="20"/> <property name="bankName" type="string" column="BANK_NAME" length="255"/> <property name="swift" type="string" column="SWIFT" length="3"/> </class> </hibernate-mapping>
如Mapping File中看到的,CreditCard和BankAccount的Id都是采用Hibernate的native策略。当Session对CreditCard和BankAccount的instance做save的时候,insert到相应table中的idnum栏位的value可能是一样的。因为他们的Hibernate Id 产生策略独立的,彼此没有任何的关系。
//Transaction Commit以后,cc11的Id Value是1 CreditCard cc11 = new CreditCard(); cc11.setOwner("jessecia"); cc11.setNumber("123123123123123"); session.save(cc11); //Transaction Commit以后,ba11的Id Value是1 BankAccount ba11 = new BankAccount(); ba11.setOwner("jessecia"); ba11.setBankName("GBDFB"); session.save(ba11);
而对于BillingDetail的polymorphic query,则分成2个独立的对CreditCard和BankAccount表的查询SQL来获得。Java代码如下:
Criteria criteria = session.createCriteria(BillingDetail.class); List<BillingDetail> list = criteria.list();
产生的SQL如下:
select this_.IDNUM as IDNUM0_0_ , this_.NUMBER_CODE as NUMBER2_0_0_ , this_.EXP_MONTH as EXP3_0_0_ , this_.EXP_YEAR as EXP4_0_0_ , this_.OWNER as OWNER0_0_ from CREDITCARD this_ select this_.IDNUM as IDNUM1_0_ , this_.ACCOUNT as ACCOUNT1_0_ , this_.BANK_NAME as BANK3_1_0_ , this_.SWIFT as SWIFT1_0_ , this_.OWNER as OWNER1_0_ from BANK_ACCOUNT1 this_
T/CCIP方式的结论:
1. 应用场合:不适合于Polymorphic Association的应用场合。也就是说SupperClass和其他的Persistent Class发生关系。
2. 扩展性:当SuperClass增加新的Property的时候,每个Concrete Class相应的Table都需要新增Column