對於面向對象的程序設計語言而言,繼承和多態是兩個最基本的概念。Hibernate 的繼承映射可以理解持久化類之間的繼承關係。
Hibernate支持三種繼承映射策略:
- 使用 subclass 進行映射:將域模型中的每一個實體對象映射到一個獨立的表中,也就是說不用在關係數據模型中考慮域模型中的繼承關係和多態。
- 使用 joined-subclass 進行映射: 對於繼承關係中的子類使用同一個表,這就需要在數據庫表中增加額外的區分子類類型的字段。
- 使用 union-subclass 進行映射:域模型中的每個類映射到一個表,通過關係數據模型中的外鍵來描述表之間的繼承關係。這也就相當於按照域模型的結構來建立數據庫中的表,並通過外鍵來建立表之間的繼承關係。
一、subclass
採用 subclass 的繼承映射可以實現對於繼承關係中父類和子類使用同一張表
因爲父類和子類的實例全部保存在同一個表中,因此需要在該表內增加一列,使用該列來區分每行記錄到低是哪個類的實例—-這個列被稱爲辨別者列(discriminator).
在這種映射策略下,使用 subclass 來映射子類,使用 class 或 subclass 的 discriminator-value 屬性指定辨別者列的值
所有子類定義的字段都不能有非空約束。如果爲那些字段添加非空約束,那麼父類的實例在那些列其實並沒有值,這將引起數據庫完整性衝突,導致父類的實例無法保存到數據庫中
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entities.subclass">
<class name="Person" table="PERSON" discriminator-value="PERSON">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<!-- 配置辨別者列 -->
<discriminator column="TYPE" type="string"></discriminator>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<property name="age" type="java.lang.Integer">
<column name="AGE" />
</property>
<!-- 映射 Student -->
<subclass name="Student" discriminator-value="STUDENT">
<property name="school" type="string" column="SCHOOL" length="30"></property>
</subclass>
</class>
</hibernate-mapping>
HibernateTest.java
package com.hibernate.entities.subclass;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class HibernateTest {
SessionFactory sessionFactory;
Session session;
Transaction transaction;
@Before
public void init() {
Configuration configuration = new Configuration().configure();
@SuppressWarnings("deprecation")
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
.applySettings(configuration.getProperties()).buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
transaction = session.beginTransaction();
}
@After
public void destory() {
transaction.commit();
session.close();
sessionFactory.close();
}
/**
* 1.查詢父類記錄,只需要查詢一張數據表
* 2.查詢子類記錄,只需要查詢一張數據表
*
* 缺點:
* 1.使用了辨別者列
* 2.子類獨有的字段不能添加非空約束
* 3.若繼承層次比較深,則數據表的字段也會比較多
*/
@Test
public void testQuery() {
List<Person> persons = session.createQuery("FROM Person").list();
System.out.println(persons.size());
List<Student> students = session.createQuery("FROM Student").list();
System.out.println(students.size());
}
/**
* 插入操作
* 1. 對於子類對象只需把記錄插入到一張數據表中。
* 2. 辨別者列由 Hibernate 自動維護
*/
@Test
public void testSave() {
Person person = new Person();
person.setAge(12);
person.setName("AA");
session.save(person);
Student student = new Student();
student.setAge(23);
student.setName("BB");
student.setSchool("china");
session.save(student);
}
}
二、joined-subclass
採用 joined-subclass 元素的繼承映射可以實現每個子類一張表
採用這種映射策略時,父類實例保存在父類表中,子類實例由父類表和子類表共同存儲。因爲子類實例也是一個特殊的父類實例,因此必然也包含了父類實例的屬性。於是將子類和父類共有的屬性保存在父類表中,子類增加的屬性,則保存在子類表中。
在這種映射策略下,無須使用鑑別者列,但需要爲每個子類使用 key 元素映射共有主鍵。
子類增加的屬性可以添加非空約束。因爲子類的屬性和父類的屬性沒有保存在同一個表中
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entities.joined.subclass">
<class name="Person" table="PERSON">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<property name="age" type="java.lang.Integer">
<column name="AGE" />
</property>
<!-- 映射 Student -->
<joined-subclass name="Student" table="STUDENT">
<key>
<column name="STUDENT_ID"></column>
</key>
<property name="school" type="string" column="SCHOOL" length="30"></property>
</joined-subclass>
</class>
</hibernate-mapping>
三、union-subclass
採用 union-subclass 元素可以實現將每一個實體對象映射到一個獨立的表中。
子類增加的屬性可以有非空約束 — 即父類實例的數據保存在父表中,而子類實例的數據保存在子類表中。
子類實例的數據僅保存在子類表中, 而在父類表中沒有任何記錄
在這種映射策略下,子類表的字段會比父類表的映射字段要多,因爲子類表的字段等於父類表的字段、加子類增加屬性的總和
在這種映射策略下,既不需要使用鑑別者列,也無須使用 key 元素來映射共有主鍵.
使用 union-subclass 映射策略是不可使用 identity 的主鍵生成策略, 因爲同一類繼承層次中所有實體類都需要使用同一個主鍵種子, 即多個持久化實體對應的記錄的主鍵應該是連續的. 受此影響, 也不該使用 native 主鍵生成策略, 因爲 native 會根據數據庫來選擇使用 identity 或 sequence.
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.hibernate.entities.union.subclass">
<class name="Person" table="PERSON">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="hilo" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<property name="age" type="java.lang.Integer">
<column name="AGE" />
</property>
<union-subclass name="Student" table="STUDENT">
<property name="school" column="SCHOOL" type="string"></property>
</union-subclass>
</class>
</hibernate-mapping>