简介说明:
能引用SalariedEmployee类的实例。
- 继承关系树的每个具体类对应一个表: 关系数据模型完全不支持域模型中的继承关系和多态。
- 继承关系树的根类对应一个表:对关系数据模型进行非常规设计,在数据库表中加入额外的区分子类型的字段。通过这种方式,可以使关系数据模型支持继承关系和多态。
- 继承关系树的每个类对应一个表:在关系数据模型中用外键参照关系来表示继承关系。
继承关系树的每个具体类对应一个表
SalariedEmployee类和SE表对应,SalariedEmployee类本身的salary属性,以及从Employee类中继承的id属性和name属性,在SE表中都有对应的字段。此外SalariedEmployee类继承了Employee类与Company类的关联关系,与此对应,在SE表中定义了参照COMPANIES表的COMPANY_ID外键。
Company类、HourlyEmployee类和SalariedEmployee类都有相应的映射文件,而Employee类没有相应的映射文件。如下图显示了持久化类、映射文件和数据库表之间的对应关系。
创建映射文件
<hibernate-mapping>
<class name="mypack.Company" table="COMPANIES">
<id name="id" type="long" column="ID">
<generator class="increment" />
</id>
<property name="name" type="string" column="NAME"/>
</class>
</hibernate-mapping>
HourlyEmployee.hbm.xml文件用于把HourlyEmployee类映射到HE表,在这个映射文件中,除了需要映射HourlyEmployee类本身的rate属性,还需要映射从Employee类中继承的name属性,此外还要映射从Employee类中继承的与Company类的关联关系。例程12-2是HourlyEmployee.hbm.xml文件的代码。
<hibernate-mapping>
<class name="mypack.HourlyEmployee" table="HOURLY_EMPLOYEES">
<id name="id" type="long" column="ID">
<generator class="increment" />
</id>
<property name="name" type="string" column="NAME"/>
<property name="rate" type="double" column="RATE"/>
<many-to-one name="company" column="COMPANY_ID" class="mypack.Company"/>
</class>
</hibernate-mapping>
SalariedEmployee.hbm.xml文件用于把SalariedEmployee类映射到SE表,在这个映射文件中,除了需要映射SalariedEmployee类本身的salary属性,还需要映射从Employee类中继承的name属性,此外还要映射从Employee类中继承的与Company类的关联关系。例程12-3是SalariedEmployee.hbm.xml文件的代码。
<hibernate-mapping>
<class name="mypack.SalariedEmployee" table="SALARIED_EMPLOYEES">
<id name="id" type="long" column="ID">
<generator class="increment" />
</id>
<property name="name" type="string" column="NAME"/>
<property name="salary" type="double" column="SALARY"/>
<many-to-one name="company" column="COMPANY_ID" class="mypack.Company"/>
</class>
</hibernate-mapping>
操作持久化对象
- findAlIEmployees():检索数据库中所有的Employee对象。
- loadCompany():加载一个Company对象。
- saveEmployee():保存一个Employee对象。
继承关系树的根类对应一个表
创建映射文件
<hibernate-mapping>
<class name="mypack.Company" table="COMPANIES">
<id name="id" type="long" column="ID">
<generator class="increment" />
</id>
<property name="name" type="string" column="NAME" />
<set name="employees" inverse="true">
<key column="COMPANY_ID" />
<one-to-many class="mypack.Employee" />
</set>
</class>
</hibernate-mapping>
Employee.hbm.xml文件用于把Employee类映射到EMPLOYEES表,在这个映射文件中,除了需要映射Employee类本身的属性,还需要在<subclass>元素中映射两个子类的属性。例程12-6是Employee.hbm.xml文件的代码:
<hibernate-mapping>
<class name="mypack.Employee" table="EMPLOYEES">
<id name="id" type="long" column="ID">
<generator class="increment" />
</id>
<discriminator column="EMPLOYEE_TYPE" type="string" />
<property name="name" type="string" column="NAME" />
<many-to-one name="company" column="COMPANY_ID" class="mypack.Company" />
<subclass name="mypack.HourlyEmployee" discriminator-value="HE">
<property name="rate" type="double" column="RATE" />
</subclass>
<subclass name="mypack.SalariedEmployee" discriminator-value="SE">
<property name="salary" type="double" column="SALARY" />
</subclass>
</class>
</hibernate-mapping>
在Employee.hbm.xml文件中,<discriminator>元素指定EMPLOYEES表中用于区分Employee类型的字段为EMPLOYEE_TYPE,两个<subclass>元素用于映射HourlyEmployee类和SalariedEmployee类,<subclass>元素的discriminator-value属性指定EMPLOYEE TYPE字段的取值。EMPLOYEES表中有以下记录:
如果Employee类不是抽象类,即它本身也能被实例化,那么可以在<class>元素中定义它的discriminator值,形式如下:
操作持久化对象
Hibemate会检索出所有的HourlyEmployee对象和SalariedEmployee对象。此外也可以单独查询Employee类的两个子类的实例,例如:
- findAllHourlyEmployees():检查数据库中所有的HourlyEmployee对象。
- findAlIEmployees():检索数据库中所有的Employee对象。
- loadCompany():加载一个Company对象。
- saveEmployee():保存一个Employee对象。
(3)运行loadCompany()方法,它的代码如下:
initialize()方法来显式初始化employees集合。
(4)运行saveEmployee(Employee employee)方法,它的代码如下:
session.save(employee);
tx.commit();
Session的save()方法能判断employee变量实际引用的实例的类型,如果employee变量引用HourlyEmployee实例,就执行如下insert语句:
以上insert语句没有为SalariedEmployee类的salary属性对应的SALARY字段赋值,因此这条记录的SALARY字段为null.
继承关系树的每个类对应一个表
字段,SE表仅包含和SalariedEmployee类的属性对应的字段。此外,HE表和SE表都以EMPLOYEE_ID字段作为主键,该字段还同时作为外键参照EMPLOYEES表。
创建映射文件
<hibernate-mapping>
<class name="mypack.Company" table="COMPANIES">
<id name="id" type="long" column="ID">
<generator class="increment" />
</id>
<property name="name" type="string" column="NAME" />
<set name="employees" inverse="true">
<key column="COMPANY_ID" />
<one-to-many class="mypack.Employee" />
</set>
</class>
</hibernate-mapping>
Employee.hbm.xml文件用于把Employee类映射到EMPLOYEES表,在这个映射文件中,除了需要映射Employee类本身的属性,还需要在<joined-subclass>元素中映射两个子类的属性。例程12-8是Employee.hbm.xml文件的代码。
<hibernate-mapping>
<class name="mypack.Employee" table="EMPLOYEES">
<id name="id" type="long" column="ID">
<generator class="increment" />
</id>
<property name="name" type="string" column="NAME" />
<many-to-one name="company" column="COMPANY_ID" class="mypack.Company" />
<joined-subclass name="mypack.HourlyEmployee" table="HOURLY_EMPLOYEES">
<key column="EMPLOYEES_ID" />
<property name="rate" type="double" column="RATE" />
</joined-subclass>
<joined-subclass name="mypack.SalariedEmployee" table="SALARIED_EMPLOYEES">
<key column="EMPLOYEES_ID" />
<property name="salary" type="double" column="SALARY" />
</joined-subclass>
</class>
</hibernate-mapping>
在Employee.hbm.xml文件中,两个<joined-subclass>元素用于映射HourlyEmployee类和SalariedEmployee类,<joined-subclass>元素的<key>子元素指定HE表和SE表中即作为主键,又作为外键的EMPLOYEE_ID字段。图12-8显示了EMPLOYEES表、HE表和SE表中记录的参照关系。
也可以在单独的映射文件中配置<subclass>或<joined-subclass>元素,但此时必须显式设定它们的extends属性。例如,可以在单独的HourlyEmployee.hbm.xml文件中映射HourlyEmployee类:
<hibernate-mapping>
<joined-subclass name="mypack.HourlyEmployee" table="HOURLY_EMPLOYEES" extends="mypack.Employee">
......
</joined-subclass>
</hibernate-mapping>
使用注意事项:
值得注意的是,在<subclass>元素中只能嵌入<subclass>子元素,但不能嵌入<joined-subclass>子元素,而在<joined-subclass>元素中只能嵌入<joined-subclass>子元素,但不能嵌入<subclass>子元素。
操纵持久化对象
- findAllHourlyEmployees():检查数据库中所有的HourlyEmployee对象。
- findAlIEmployees():检索数据库中所有的Employee对象。
- loadCompany():加载一个Company对象。
- saveEmployee():保存一个Employee对象。
选择继承的映射方式
- DOClass类、ClassA类和ClassB类为一棵子树:DOClass类为抽象类,位于整个继承关系树的顶层,通常不会对它进行多态查询,因此可以采用每个具体类对应一个表的映射方式,ClassA类对应TABLE_A表,ClassB类对应TABLEB表。
- ClassA类、ClassC类、ClassD类、ClassG类和ClassH类为一棵子树:ClassA类的所有子类都只包含少量属性,因此可以采用根类对应一个表的映射方式,ClassA类对应TABLEA表
- ClassB类、ClassE类和ClassF为一棵子树:ClassB类的两个子类都包含很多属性,因此采用每个类对应一个表的映射方式,ClassB类对应TABLE_B表,ClassE类对应TABLE_E表,ClassF类对应TABLE_F表。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.test.hibernate3.demo1.ClassA" table="TABLE_A"
discriminator-value="A">
<id name="id" type="long" column="ID">
<generator class="increment" />
</id>
<discriminator column="A_TYPE" type="string" />
<property name="a1" type="string" column="A1" />
<subclass name="cn.test.hibernate3.demo1.ClassC"
discriminator-value="C">
<property name="c1" column="C1" type="string" />
</subclass>
<subclass name="cn.test.hibernate3.demo1.ClassD"
discriminator-value="D">
<property name="d1" column="D1" type="string" />
<subclass name="cn.test.hibernate3.demo1.ClassG"
discriminator-value="G">
<property name="g1" column="G1" type="string" />
</subclass>
<subclass name="cn.test.hibernate3.demo1.ClassH"
discriminator-value="H">
<property name="h1" column="H1" type="string" />
</subclass>
</subclass>
</class>
</hibernate-mapping>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="cn.test.hibernate3.demo1.ClassB" table="TABLE_B">
<id name="id" type="long" column="ID">
<generator class="increment" />
</id>
<property name="b1" type="string" column="B1" />
<joined-subclass name="cn.test.hibernate3.demo1.ClassE" table="TABLE_E">
<key column="B_ID" />
<property name="e1" column="E1" type="string" />
<property name="e2" column="E2" type="string" />
<property name="e3" column="E3" type="string" />
<property name="e4" column="E4" type="string" />
<property name="e5" column="E5" type="string" />
<property name="e6" column="E6" type="string" />
</joined-subclass>
<joined-subclass name="cn.test.hibernate3.demo1.ClassF" table="TABLE_F">
<key column="B_ID" />
<property name="f1" column="F1" type="string" />
<property name="f2" column="F2" type="string" />
<property name="f3" column="F3" type="string" />
<property name="f4" column="F4" type="string" />
<property name="f5" column="F5" type="string" />
<property name="f6" column="F6" type="string" />
<property name="f7" column="F7" type="string" />
</joined-subclass>
</class>
</hibernate-mapping>
子段取值为“A”,表明它是ClassA类的实例;如果A_TYPE字段取值为“G”,表明它是ClassG类的实例,依次类推。
package cn.test.hibernate3.demo1;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;
import cn.test.hibernate3.utils.HibernateUtils;
public class TestHbmExtend {
@Test
// 测试保存操作
public void demo1() {
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
ClassA g = new ClassG("a1", "d1", "g1");
ClassB f = new ClassF("b1", "f1", "f2", "f3", "f4", "f5", "f6", "f7");
session.save(g);
session.save(f);
tx.commit();
session.close();
}
}
生成表的结构如下: