對於面向對象的程序設計語言,繼承、多態是兩個最基本的概念。Hibernate的繼承映射可以理解兩個持久化類之間的繼承關系。
Hibernate支持幾種繼承映射策略,不管哪種繼承映射策略,Hibernate的多態查詢都可以很好的運行。
假設有四個對象:Department、Employee、Skiller、Sales。他們四個對象存在如下關系:
從上面的關系可知:Employee與Department之間存在N-1的關聯關系。Skiller和Sales都是Employee的子類。下面是這四個類的代碼:
Department
[java]
public class Department {
private Integer id;
private String name;
private Set<Employee> employees; //關聯關系
//省略setter和getter方法
}
Employee
[java]
public class Employee {
private Integer id;
private String name;
private Department department;
//省略setter和getter方法
}
Skiller
[java]
public class Skiller extends Employee {
private String skiller;
//省略setter和getter方法
}
Sales
[java]
public class Sales extends Employee {
private int sell;
//省略setter和getter方法
}
一、采用subclass元素的繼承映射
在這種繼承映射策略下,整個繼承樹的所有實例都將保存在同一張表中。因為是將父類、子類的實例全部保存在同一張表中,所以需要在該表中添加列,通過該列可以區分每行記錄到底是屬於哪個類的實例—這個列被稱之為辨別者。
在這種繼承映射策略下,我們需要使用<subclass…/>元素來映射子類的持久化類,使用<discrimainator…/>元素來映射辨別者,同時還需要給<subclass…/>元素增加discriminator-value屬性來指定每個類的辨別者的值。
映射文件如下:
[html]
<hibernate-mapping package="com.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="employeeID">
<generator class="hilo" />
</id>
<!– 映射辨別者 –>
<discriminator column="type" type="string" />
<property name="name" column="employeeName" />
<!– 用於映射N-1關聯實體,指定關聯實體類為 :Department,指定外鍵為:departmentID–>
<many-to-one name="department" column="departmentID" />
<!– 使用subclass元素映射Employee的子類Skiller –>
<subclass name="Skiller" discriminator-value="skiller">
<property name="skiller" />
</subclass>
<!– 使用subclass元素映射Employee的子類Sales –>
<subclass name="Sales" discriminator-value="sales">
<property name="sell" />
</subclass>
</class>
</hibernate-mapping>
在這裡我們隻需要給父類進行映射就可以瞭。在這個配置文件中,指定瞭一個辨別者列:type,該列其本省是沒有任何意義的,隻是起到一個區分每條記錄時對應哪個持久化類的作用。其中Skiller類的辨別者列type的值為skiller,Sales類的辨別者列type的值為sales。通過下面的程序段來進行操作。
[html]
static void add(){
Session session = HibernateUtil.getSession();
Transaction tx = session.beginTransaction();
Department department = new Department();
department.setName("department name1");
Employee emp1 = new Employee();
emp1.setName("employy name1");
emp1.setId(1);
Skiller emp2 = new Skiller();
emp2.setSkiller("電工");
emp2.setName("employee name2");
emp2.setDepartment(department); //建立對象之間的關聯關系
Sales emp3= new Sales();
emp3.setSell(50);
emp3.setName("employee name3");
emp3.setDepartment(department); //建立對象之間的關聯關系
session.save(department);
session.save(emp1);
session.save(emp2);
session.save(emp3);
tx.commit();
session.close();
}
上面的程序段,hibernate將會產生如下的幾條SQL語句:
[sql] view plaincopyprint?
Hibernate: insert into department (departmentName) values (?)
Hibernate: insert into employee (employeeName, departmentID, type) values (?, ?, 'com.hibernate.domain.Employee')
Hibernate: insert into employee (employeeName, departmentID, skiller, type) values (?, ?, ?, 'skiller')
Hibernate: insert into employee (employeeName, departmentID, sell, type) values (?, ?, ?, 'sales')
在第二條sql語句,type的值為com.hibernate.domain.Employee。對於這個值可以理解為空。在第三條sql語句中,type的值為skiller,第四天sql語句中type值為sales。同時要註意第三條sql語句中的sell列是沒有值的,第四條的skiller列同樣也沒有值。所以在這裡一定要註意:使用subclass繼承映射策略時,其子類中增加的屬性映射的字段是一定不能有非空約束的。
表結構如下:
通過下面的程序段,來進行查詢操作:
[java]
static void query(int empid){
Session session = HibernateUtil.getSession();
Employee emp = (Employee) session.get(Employee.class, empid);
}
得到如下SQL語句:
[sql]
Hibernate: select employee0_.employeeID as employeeID2_0_, employee0_.employeeName as employee3_2_0_, employee0_.departmentID as departme4_2_0_, employee0_.skiller as skiller2_0_, employee0_.sell as sell2_0_, employee0_.type as type2_0_
from employee employee0_ where employee0_.employeeID=?
從上面的sql語句可以看到它是從一張表中獲取所有的記錄。這正是這種繼承策略的一個非常大的好處:在這種映射策略下,整棵繼承樹的所有數據都保存在一張表中,因此不管進行怎樣的查詢,不管查詢繼承樹中的那一層的實體,底層數據庫都隻需要在一張表中查詢即可,非常方便、高效。
我們在這裡給上面的查詢代碼增加一句:System.out.println(emp.getClass());
依次給empid賦值為:1、2得到的結果如下:
1、class com.hibernate.domain.Employee
2、class com.hibernate.domain.Skiller
有上面的輸出結果可知:hibernate能夠非常好處理多態查詢。
一、采用joined-subclass元素的繼承映射
采用這種策略時,父類實例保存在父類表裡,而子類實例則有父類表和子類表共同存儲。在這種策略下,是將父類與子類共有的屬性保存在父類表中,而子類特有的屬性,則保存在子類表中,就是說,父類一張表,子類一張表,同時子類表中需要增加父類表的外鍵。
采用joined-subclass來映射這種策略,並且需要為每個子類使用<key…/>元素映射共有主鍵–這個主鍵類還將參照父表的主鍵列。
映射文件如下:
[html]
<hibernate-mapping package="com.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="employeeID">
<generator class="hilo" />
</id>
<property name="name" column="employeeName" />
<!– 用於映射N-1關聯實體,指定關聯實體類為 :Department,指定外鍵為:departmentID–>
<many-to-one name="department" column="departmentID" />
<!– 使用join-class元素映射Employee類的Skill而子類 –>
<joined-subclass name="Skiller">
<!– 必須使用key元素映射父子類的共有主鍵 –>
<key column="employee_id" />
<property name="skiller" />
</joined-subclass>
<!– 使用join-class元素映射Employee類的Sales而子類 –>
<joined-subclass name="Sales">
<!– 必須使用key元素映射父子類的共有主鍵 –>
<key column="employee_id" />
<property name="sell" />
</joined-subclass>
</class>
</hibernate-mapping>
通過上面的增加代碼,執行添加操作。得到如下結果:
SQL語句如下:
[sql]
Hibernate: insert into department (departmentName) values (?)
Hibernate: insert into employee (employeeName, departmentID) values (?, ?)
Hibernate: insert into employee (employeeName, departmentID) values (?, ?)
Hibernate: insert into Skiller (skiller, employee_id) values (?, ?)
Hibernate: insert into employee (employeeName, departmentID) values (?, ?)
Hibernate: insert into Sales (sell, employee_id) values (?, ?)
從上面的SQL語句中也可以看出hibernate對與這種繼承映射策略的處理。父類一張表,子類一張表同時子類引用父類外鍵建立關聯關系。
對與這樣繼承映射策略的查詢,hibernate采用表連接方式來獲取子類表的信息:
[sql]
Hibernate: select employee0_.employeeID as employeeID2_0_, employee0_.employeeName as employee2_2_0_, employee0_.departmentID as departme3_2_0_, employee0_1_.skiller as skiller3_0_, employee0_2_.sell as sell4_0_, case
when employee0_1_.employee_id is not null then 1
when employee0_2_.employee_id is not null then 2
when employee0_.employeeID is not null then 0 end as clazz_0_
from employee employee0_ left
outer join Skiller employee0_1_
on employee0_.employeeID=employee0_1_.employee_id
left outer join Sales employee0_2_
on employee0_.employeeID=employee0_2_.employee_id
where employee0_.employeeID=?
所以當使用joined-subclass繼承映射策略,當程序查詢子類實例時,需要跨越多個字表查詢,其復雜度取決於該子類有多少層父類。這樣勢必會對查詢的性能有些影響。
一、采用union-subclass元素的繼承映射
采用這樣繼承映射策略,父類實例的數據保存在父表中,子類實例的數據僅僅隻保存在字表中,沒有在附表中有任何記錄。在這種繼承映射策略下,子表的字段會比父表的字段多。
同時在這種映射策略,既不需要使用辨別者,也不需要使用<key…/>元素來映射共有主鍵。
映射文件如下:
[html]
<hibernate-mapping package="com.hibernate.domain">
<class name="Employee" table="employee">
<id name="id" column="employeeID">
<generator class="hilo" />
</id>
<property name="name" column="employeeName" />
<!– 用於映射N-1關聯實體,指定關聯實體類為 :Department,指定外鍵為:departmentID–>
<many-to-one name="department" column="departmentID" />
<!– 使用union-subclass元素映射Employee類的子類:Skiller –>
<union-subclass name="Skiller" table="skiller">
<property name="skiller" />
</union-subclass>
<!– 使用union-subclass元素映射Employee類的子類:Sales –>
<union-subclass name="Sales" table="sales">
<property name="sell" />
</union-subclass>
</class>
</hibernate-mapping> www.aiwalls.com
註:在這種映射策略下,映射持久化類是不能使用identity主鍵生成策略。
通過添加操作得到如下的兩個子類表:
采用這中映射策略,底層數據庫的數據看起來更加符合正常情況下的數據庫設計,不同實體保存在不同的數據表中。
作者:chenssy