JAVAWEB开发之Hibernate详解(四)——持久化类的继承映射关系

来源:互联网 发布:上海市网络作家协会 编辑:程序博客网 时间:2024/05/16 23:40


目录(?)
[+]

简介说明:

 在域模型中,类与类之间除了关联关系和聚集关系,还可以存在继承关系,在如图所示的域模型中,Company类和Employee类之间为一对多的双向关联关系(假定不允许雇员同时在多个公司兼职),Employee类为抽象类,因此它不能被实例化,它有两个具体的子类:HourlyEmployee类和SalaiedEmployee类。由于Java只允许一个类最多有一个直接的父类,因此Employee类、HourlyEmployee类和SalariedEmployee类构成了一棵继承关系树。
 
在面向对象的范畴中,还存在多态的概念,多态建立在继承关系的基础上。简单地理解,多态是指当一个Java应用变量被声明为Employee类时,这个变量实际上既可以引用HourlyEmployee类的实例,也可以引用SalariedEmployee类的实例。以下这段程序代码就体现了多态:
List employees=businessService.findAllEmployees();
Iterator it=employees.iterator();
while(it.hasNext()){
    Employee e=(Employee)it.next();
    if(e instanceof  HourlyEmployee){
           System.out.println(e.getName()+"  "+((HourlyEmployee)e).getRate());
    }else{
           System.out.println(e.getName()+"  "+((SalariedEmployee)e).getRate());
    }
}
    BusinessService类的findAllEmployees()方法通过Hibernate API从数据库中检索出所有Employee对象。findAllEmployees()方法返回的集合既包含HourlyEmployee类的实例,也包含SalariedEmployee类的实例,这种查询被称为多态查询。以上程序中变量e被声明为Employee类型,它实际上既可能引用HourlyEmployee类的实例,也可
能引用SalariedEmployee类的实例。
    此外,从Company类到Employee类为多态关联,因为Company类的employees集合中可以包含HourlyEmployee类和SalariedEmployee类的实例。从Employee类到Company类不是多态关联,因为Employee类的company属性只会引用Company类本身的实例。
数据库表之间不存在继承关系,那么如何把域模型的继承关系映射到关系数据模型中,有以下三种方式:
  • 继承关系树的每个具体类对应一个表: 关系数据模型完全不支持域模型中的继承关系和多态。
  • 继承关系树的根类对应一个表:对关系数据模型进行非常规设计,在数据库表中加入额外的区分子类型的字段。通过这种方式,可以使关系数据模型支持继承关系和多态。
  • 继承关系树的每个类对应一个表:在关系数据模型中用外键参照关系来表示继承关系。
  具体类是指非抽象的类,具体类可以被实例化。HourlyEmpolyee和SalariedEmployee类就是具体类。
 以上三种方式都有利也有弊。

继承关系树的每个具体类对应一个表

     把每个具体类映射到一张表是最简单的映射方式。如图12-2所示,在关系数据模型中只需定义COMPANIES、HOURLY_EMPLOYEES和SALARIED_EMPLOYEES表。为了叙述的方便,下文把HOURLY_EMPLOYEES表简称为HE表,把SALARIED_EMPLOYEES表简称为SE表。HourlyEmployee类和HHE表对应HourlyEmployee类本身的rate属性,以及从Employee类中继承的id属性和name属性,在HE表中都有对应的字段。此外,HourlyEmployee类继承了Employee类与Company类的关联关系,与此对应,在HE表中定义了参照COMPANIES表的COMPANY_ID外键。
     SalariedEmployee类和SE表对应,SalariedEmployee类本身的salary属性,以及从Employee类中继承的id属性和name属性,在SE表中都有对应的字段。此外SalariedEmployee类继承了Employee类与Company类的关联关系,与此对应,在SE表中定义了参照COMPANIES表的COMPANY_ID外键。
    Company类、HourlyEmployee类和SalariedEmployee类都有相应的映射文件,而Employee类没有相应的映射文件。如下图显示了持久化类、映射文件和数据库表之间的对应关系。

图12-2 每个具体类对应一个表                        

图12-3 持久化类、映射文件和数据表之间的对应关系
   如果Empolyee类不是抽象类,即Employee类本身也能被实例化,那么还需要为Empolyee类创建对应的EMPLOYEES表,此时HE表和SE表的结构仍然和图12-2中所示的一样。这意味着在EMPLOYEES表、HE表和SE表中都定义了相同的NAME字段及参照COMPANYS表的外键COMPANY_ID。另外,还需为EMPLOYEE类创建单独的Employee.hbm.xml文件。

创建映射文件

从Company类到Employee类是多态关联,但是由于关系数据模型没有描述Employee类和它的两个子类的继承关系,因此无法映射Company类的employees集合。例程12-1是Company.hbm.xml文件的代码,该文件仅映射了Company类的id和name属性。
例程12-1 Company.hbm.xml
[html] view plain copy
  1. <hibernate-mapping>  
  2.     <class name="mypack.Company" table="COMPANIES">  
  3.         <id name="id" type="long" column="ID">  
  4.             <generator class="increment" />  
  5.         </id>  
  6.         <property name="name" type="string" column="NAME"/>  
  7.     </class>  
  8. </hibernate-mapping>   
     HourlyEmployee.hbm.xml文件用于把HourlyEmployee类映射到HE表,在这个映射文件中,除了需要映射HourlyEmployee类本身的rate属性,还需要映射从Employee类中继承的name属性,此外还要映射从Employee类中继承的与Company类的关联关系。例程12-2是HourlyEmployee.hbm.xml文件的代码。
例程 12-2  HourlyEmployee.hbm.xml
[html] view plain copy
  1. <hibernate-mapping>  
  2.     <class name="mypack.HourlyEmployee" table="HOURLY_EMPLOYEES">  
  3.         <id name="id" type="long" column="ID">  
  4.             <generator class="increment" />  
  5.         </id>  
  6.         <property name="name" type="string" column="NAME"/>  
  7.         <property name="rate" type="double" column="RATE"/>  
  8.         <many-to-one name="company" column="COMPANY_ID" class="mypack.Company"/>  
  9.     </class>  
  10. </hibernate-mapping>      
SalariedEmployee.hbm.xml文件用于把SalariedEmployee类映射到SE表,在这个映射文件中,除了需要映射SalariedEmployee类本身的salary属性,还需要映射从Employee类中继承的name属性,此外还要映射从Employee类中继承的与Company类的关联关系。例程12-3是SalariedEmployee.hbm.xml文件的代码。
 例程12-3 SalariedEmployee.hbm.xml
[html] view plain copy
  1. <hibernate-mapping>  
  2.     <class name="mypack.SalariedEmployee" table="SALARIED_EMPLOYEES">  
  3.         <id name="id" type="long" column="ID">  
  4.             <generator class="increment" />  
  5.         </id>  
  6.         <property name="name" type="string" column="NAME"/>  
  7.         <property name="salary" type="double" column="SALARY"/>  
  8.         <many-to-one name="company" column="COMPANY_ID" class="mypack.Company"/>  
  9.     </class>  
  10. </hibernate-mapping>      

操作持久化对象

这种映射方式不支持多态查询。对于以下查询语句。
  List employees=session.createQuery("from  Employee").list();
如果Employee类是抽象类,那么Hibernate会抛出异常。如果Employee类是具体类,那么Hibernate仅查询EMPLOYEES表,检索出Employee类本身的实例,但不会检索出它的两个子类的实例。
  • findAlIEmployees():检索数据库中所有的Employee对象。
  • loadCompany():加载一个Company对象。
  • saveEmployee():保存一个Employee对象。
(1)运行findAllEmployees0方法,它的代码如下
  List results=new ArrayList();
  tx=session.beginTransaction();
  List hourlyEmployees=session.createQuery(" from HourlyEmployee").list();
  results.addAll(hourlyEmployees);
  List salariedEmployees=session.createQuery(" from SalariedEmployee").list();
  results.addAll(salariedEmployees);
  tx.commit();
  return results;
为了检索所有的Employee对象,必须分别检索所有的HourlyEmployee实例和SalariedEmployee实例,然局把它们合并到同一个集合中。在运行第一个Query的list()方法时,Hibernate执行以下select语句:
     select * from HOURLY_EMPLOYEES;
在运行第二个Query的list()方法时 Hibernate会执行以下的select语句
     select * from SALARIED_EMPLOYEES;

 (2) 运行loadCompany() 方法,它的代码如下:
  tx=session.beginTransaction();
  Company company=(Company)session.get(Company.class,new Long(id));
  List hourlyEmployees=session.createQuery(" from HourlyEmployee h where h.company.id="+id).list();
  company.getEmployees().addAll(hourlyEmployees);  
  List salariedEmployees=session.createQuery(" from SalariedEmployee s where s.company.id="+id).list();
  company.getEmployees().addAll(salariedEmployees);
  tx.commit();
  return company;

 (3) 运行saveEmployee(Employee employee)方法,它的代码如下:
  tx=session.beginTransaction();
  session.save(employee);
  tx.commit();
 在test()方法中,创建了一个HourlyEmployee实例,然后调用saveEmployee()方法保存这个实例:
 Employee employee=new HourlyEmployee("Mary",300,company);
 saveEmployee(employee);
 Session的save()方法能判断employee变量实际引用的实例的类型,如果employee变量引用HourlyEmployee实例,就向HE表中插入一条记录,执行如下insert语句:
 insert into HOURLY_EMPLOYEES(ID,NAME,RATE,CUSTOMER_ID) values(3, 'Mary', 300,1);
如果employee变量引用SalariedEmployee实例,就向SE表插入一条记录。

继承关系树的根类对应一个表

这种映射方式只需为继承关系树的Employee根类创建一张表EMPLOYEES如图12-4所示,在EMPLOYEES表中不仅提供和Employee类的属性对应的字段,还要提供和它的两个子类的所有属型对应的字段,此外,EMPLOYEES表中需要额外加入一个字符串类型的EMPLOYEE_TYPE字段,用于区分Employee的具体类型。

 Company类和Employee类有相应的映射文件,而HourlyEmployee类和SalariedEmployee类没有相应的映射文件。图12-5显示了持久化类、映射文件和数据库表之间的对应关系。

创建映射文件

从Company类到Employee类是多态关联,由于关系数据模型可以反映出Employee类和它的两个子类的继承关系,因此可以映射Company类的employees集合。例程12-5是Company.hbm.xml文件的代码,该文件不仅映射了Company类的id和name属性,还映射了它的employees集合。
例程12-5 Company.hbm.xml
[html] view plain copy
  1. <hibernate-mapping>  
  2.     <class name="mypack.Company" table="COMPANIES">  
  3.         <id name="id" type="long" column="ID">  
  4.             <generator class="increment" />  
  5.         </id>  
  6.         <property name="name" type="string" column="NAME" />  
  7.         <set name="employees" inverse="true">  
  8.             <key column="COMPANY_ID" />  
  9.             <one-to-many class="mypack.Employee" />  
  10.         </set>  
  11.     </class>  
  12. </hibernate-mapping>  
Employee.hbm.xml文件用于把Employee类映射到EMPLOYEES表,在这个映射文件中,除了需要映射Employee类本身的属性,还需要在<subclass>元素中映射两个子类的属性。例程12-6是Employee.hbm.xml文件的代码:
例程 12-6 Employee.hbm.xml
[html] view plain copy
  1. <hibernate-mapping>  
  2.     <class name="mypack.Employee" table="EMPLOYEES">  
  3.         <id name="id" type="long" column="ID">  
  4.             <generator class="increment" />  
  5.         </id>  
  6.         <discriminator column="EMPLOYEE_TYPE" type="string" />  
  7.         <property name="name" type="string" column="NAME" />  
  8.         <many-to-one name="company" column="COMPANY_ID" class="mypack.Company" />  
  9.   
  10.         <subclass name="mypack.HourlyEmployee" discriminator-value="HE">  
  11.             <property name="rate" type="double" column="RATE" />  
  12.         </subclass>  
  13.   
  14.         <subclass name="mypack.SalariedEmployee" discriminator-value="SE">  
  15.             <property name="salary" type="double" column="SALARY" />  
  16.         </subclass>  
  17.     </class>  
  18. </hibernate-mapping>  

   在Employee.hbm.xml文件中,<discriminator>元素指定EMPLOYEES表中用于区分Employee类型的字段为EMPLOYEE_TYPE,两个<subclass>元素用于映射HourlyEmployee类和SalariedEmployee类,<subclass>元素的discriminator-value属性指定EMPLOYEE TYPE字段的取值。EMPLOYEES表中有以下记录:

   其中ID为1和2的记录的EMPLOYEE_TYPE字段的取值为“HE”,因此它们对应HourlyEmployee类的实例,其中ID为3和4的记录的EMPLOYEE_TYPE字段的取值为“SE”,因此它们对应SalariedEmployee类的实例这种映射方式要求EMPLOYEES表中和子类属性对应的字段允许为null,如ID为1和2的记录的SALARY字段为null,而ID为3和4的记录的RATE字段为null 如果业务需求规定SalariedEmployee对象的rate属性不允许为null,显然无法在EMPLOYEES表中为SALARY字段定义not null约束,可见这种映射方式无法保证关系数据模型的数据完整性。
   如果Employee类不是抽象类,即它本身也能被实例化,那么可以在<class>元素中定义它的discriminator值,形式如下:
    <class name="mypack.Employee"  table="EMPLOYEES" discriminator-value="EE">
   以上代码表明,如果EMPLOYEES表中一条记录的EMPLOYEE_TYPE字段的取值为"EE",那么它对应Employee类本身的实例。

操作持久化对象

这种映射方式支持多态查询,对于以下查询语句:
  List employees=session.createQuery("from Employee").list();
Hibemate会检索出所有的HourlyEmployee对象和SalariedEmployee对象。此外也可以单独查询Employee类的两个子类的实例,例如:
  List hourlyEmployees=session.createQuery("from  HourlyEmployee").list();
业务代码类中有以下方法:
  • findAllHourlyEmployees():检查数据库中所有的HourlyEmployee对象。
  • findAlIEmployees():检索数据库中所有的Employee对象。
  • loadCompany():加载一个Company对象。
  • saveEmployee():保存一个Employee对象。
(1) 运行findAllHourlyEmployees() 方法,它的代码如下:
  tx=session.beginTransaction();
  List results=session.createQuery("from HourlyEmployee").list();
  tx.commit();
  return results;
在运行Query的list()方法时,Hibernate执行以下的select语句:
  select * from EMPLOYEES where EMPLOYEE_TYPE='HE';

(2) 运行findAllEmployees()方法,它的代码如下:
  tx=session.beginTransaction();
  List results=session.createQuery("from Employee").list();
  tx.commit();
  return results;
在运行Query的list()方法时,Hibernate执行以下的select语句:
  select * from EMPLOYEES ;
在这种映射方式下,Hibernate支持多态查询,对于从EMPLOYEES表获得的查询结果,如果EMPLOYEE_TYPE字段取值为“HE”,就创建HoulyEmployee实例,如果EMPLOYEE_TYPE字段取值为“SE”,就创建SalariedEmployee实例。

(3)运行loadCompany()方法,它的代码如下: 
  tx=session.beginTransaction();
  Company company=(Company)session.get(Company.class,new Long(id));
  Hibernate.initialize(company.getEmployees());
  tx.commit();
这种映射方式支持多态关联。如果在Company.hbm.xml文件中对employees集合设置了立即检索策略,那么Session的get()方法加载的Company对象的employees集合中包含所有关联的Employee对象。由于提供的Company.hbm.xml文件对employees集合采用默认的延迟检索策略,因此以上程序代码通过Hibemate类的静态
initialize()方法来显式初始化employees集合。

(4)运行saveEmployee(Employee employee)方法,它的代码如下:
 tx=session.beginTransaction();
 session.save(employee);
 tx.commit();
在test方法中,创建了一个HourlyEmployee实例,然后调用saveEmployee()方法保存这个实例:
   Employee employee=new HourlyEmployee("Mary",300,company);
   saveEmployee(employee);
Session的save()方法能判断employee变量实际引用的实例的类型,如果employee变量引用HourlyEmployee实例,就执行如下insert语句:
  insert into EMPLOYEES(ID,NAME,RATE, EMPLOYEE_TYPE, CUSTOMER_ID) values(5, 'Mary', 300, 'HE',1);
以上insert语句没有为SalariedEmployee类的salary属性对应的SALARY字段赋值,因此这条记录的SALARY字段为null.

继承关系树的每个类对应一个表

在这种映射方式下,继承关系树的每个类及接口都对应一个表。在本例中需要创建EMPLOYEES、HE和SE表。如图12-6所示,EMPLOYEES表仅包含和Employee类的属性对应的字段,HE表仅包含和HourlyEmployee类的属性对应的
字段,SE表仅包含和SalariedEmployee类的属性对应的字段。此外,HE表和SE表都以EMPLOYEE_ID字段作为主键,该字段还同时作为外键参照EMPLOYEES表。

   Company类和Employee类有相应的映射文件,而HourlyEmployee类和SalariedEmployee类没有相应的映射文件。图12-7显示了持久化类、映射文件和数据库表之间的对应关系。

创建映射文件

     从Company类到Employee类是多态关联,由于关系数据模型反映出了Employee类和它的两个子类的继承关系,因此可以映射Company类的employees集合。例程12-7是Company.hbm.xml文件的代码,该文件不仅映射了Company类的id和name属性,还映射了它的employees集合。
[html] view plain copy
  1. <hibernate-mapping>  
  2.     <class name="mypack.Company" table="COMPANIES">  
  3.         <id name="id" type="long" column="ID">  
  4.             <generator class="increment" />  
  5.         </id>  
  6.         <property name="name" type="string" column="NAME" />  
  7.         <set name="employees" inverse="true">  
  8.             <key column="COMPANY_ID" />  
  9.             <one-to-many class="mypack.Employee" />  
  10.         </set>  
  11.     </class>  
  12. </hibernate-mapping>   
Employee.hbm.xml文件用于把Employee类映射到EMPLOYEES表,在这个映射文件中,除了需要映射Employee类本身的属性,还需要在<joined-subclass>元素中映射两个子类的属性。例程12-8是Employee.hbm.xml文件的代码。
例程 12-8 Employee.hbm.xml
[html] view plain copy
  1. <hibernate-mapping>  
  2.     <class name="mypack.Employee" table="EMPLOYEES">  
  3.         <id name="id" type="long" column="ID">  
  4.             <generator class="increment" />  
  5.         </id>  
  6.         <property name="name" type="string" column="NAME" />  
  7.   
  8.         <many-to-one name="company" column="COMPANY_ID" class="mypack.Company" />  
  9.   
  10.         <joined-subclass name="mypack.HourlyEmployee" table="HOURLY_EMPLOYEES">  
  11.             <key column="EMPLOYEES_ID" />  
  12.             <property name="rate" type="double" column="RATE" />  
  13.         </joined-subclass>  
  14.   
  15.         <joined-subclass name="mypack.SalariedEmployee" table="SALARIED_EMPLOYEES">  
  16.             <key column="EMPLOYEES_ID" />  
  17.             <property name="salary" type="double" column="SALARY" />  
  18.         </joined-subclass>  
  19.   
  20.     </class>  
  21. </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>子元素。

操纵持久化对象

这种映射方式支持多态查询,对于以下查询语句:
   List  employees=session.createQuery("from  Employee").list();
Hibernate会检索出所有的HourlyEmployee对象和SalariedEmployee对象。此外,也可以单独查询Employee类的两个子类 的实例,例如:
   List hourlyEmployees=session.createQuery("from HourlyEmployee").list();
业务代码类中有以下方法:
  • findAllHourlyEmployees():检查数据库中所有的HourlyEmployee对象。
  • findAlIEmployees():检索数据库中所有的Employee对象。
  • loadCompany():加载一个Company对象。
  • saveEmployee():保存一个Employee对象。    
(1)  运行findAllHourlyEmployees()非,它的代码如下:
  tx = session.beginTransaction();
  List results=session.createQuery("from HourlyEmployee").list();
  tx.commit();
  return results;
在运行Query的list()方法时,Hibernate执行以下的Select语句:
  select * from HOURLY_EMPLOYEES he inner join EMPLOYEES e on he.EMPLOYEE_ID=e.ID;
Hibernate通过HE表与EMPLOYEES表的内连接获得HourlyEmployee对象的所有属性值。

(2)  运行findAllEmployees() 方法,它的代码如下:
  tx=session.beginTransaction(); 
  List results=session.createQuery("from Employee").list();
  tx.commit();
  return results;
在运行Query的list()方法时,Hibernate执行以下Select语句:
 select * from EMPLOYEES e  
 left outer join HOURLY_EMPLOYEES he on e.ID = he.EMPLOYEE_ID
 left outer join SALARIED_EMPLOYEES se on e.ID = se.EMPLOYEE_ID;
 Hibernate把EMPLOYEES表与HE表及SE表进行左外连接,从而获得HourlyEmployee对象和SalariedEmployee对象的所有属性。在这种映射方式下,Hibernate支持多态查询,对于以上查询语句获得的查询结果,如果HE表的EMPLOYEE_ID字典不为null,就创建HourlyEmployee实例;如果SE表的EMPLOYEE_ID不为null,就创建SalariedEmployee实例。

(3) 运行loadCompany()方法,它的代码如下:
 tx=session.beginTransaction();
 Company company=(company)session.get(Company.class, new Long(id));
Hibernate.initialize(company.getEmployees());
 tx.commit();
这种映射方式支持多态查询。如果在Company.hbm.xml文件中对employees集合设置了立即检索策略,那么Session的 get()方法加载的Company对象的employees集合中包含了所有关联的Employees对象。由于以上Company.hbm.xml文件对employees集合使用了默认的延迟检索的策略,因此以上程序代码还通过Hibernate累的静态initialize()方法来显式初始化employees集合,初始化时执行以下的select语句:
  select e.COMPANY_ID, e.ID, e.NAME, he.RATE, se.SALARY, case when he.EMPLOYEE_ID is not null then 1  when se.EMPLOYEE_ID  is not null then 2 when e.ID is not null then 0 as clazz from EMPLOYEES e left outer join  HOURLY_EMPLOYEES he on e.ID =he.EMPLOYEE_ID left outer join SALARIED_EMPLOYEES se on e.ID=se.EMPLOYEE_ID where e.COMPANY_ID=1;

(4)运行saveEmployee(Employee employee)方法,它的代码如下:
 tx=session.beginTransaction();
 session.save(employee);
 tx.commit();
在Test方法中,创建了一个HourlyEmployee实例,然后调用saveEmployee()方法保存这个实例:
 Employee employee=new HourlyEmployee("Mary", 300, company);
 saveEmployee(employee);
Session的save()方法能判断employee变量实际引用的实例的类型,如果employee变量引用HourlyEmployee实例,就执行如下的insert语句:
 insert into EMPLOYEES (ID, NAME, COMPANY_ID) values (5, 'Mary', 1);
 insert into HOURLY_EMPLOYEES (EMPLOYEE_ID, RATE) values (5, 300);
可见,每保存一个HourlyEmployee对象,需要分别向EMPLOYEES表和HE表插入一条记录,EMPLOYEES表的记录和HE表的记录共享一个主键。

选择继承的映射方式

比较三种映射方式

   如果不需要支持多态查询和多态关联,可以采用每个具体类对应一个表的映射方式;如果需要支持多态查询和多态关联,并且子类包含的属性不多,可以采用根类对应一个表的映射方式;如果需要支持多态查询和多态关联,并且子类包含的属性很多,可以采用每个类对应一个表的映射方式。如果继承关系树中包含接口,可以将它当做抽象类来处理。
图12-9显示了一棵复杂的继承关系树,其中DOClass为抽象类,其它均为具体类。

可以将图12-9的继承关系树分解成3棵子树:
  • 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表。
    如图12-10所示,在关系数据模型中,只需创建TABLE_A、TABLE_B、TABLE_E和TABLE_F表,其中TABLE_A中包含了与DOClass、ClassA、ClassC、ClassD、ClassG和ClassH的属性对应的字段。TABLE_B中包含了与DOClass和ClassB的属性对应的字段,TABLE_E和TABLE_F的BID字段既是主键,又是参照TABLE_B表的外键.

示例如下:

只需要创建两个映射文件,ClassA.hbm.xml和ClassB.hbm.xml如下所示:
 例程12-9  ClassA.hbm.xml
[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC   
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.     "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping>  
  6.     <class name="cn.test.hibernate3.demo1.ClassA" table="TABLE_A"  
  7.         discriminator-value="A">  
  8.         <id name="id" type="long" column="ID">  
  9.             <generator class="increment" />  
  10.         </id>  
  11.         <discriminator column="A_TYPE" type="string" />  
  12.         <property name="a1" type="string" column="A1" />  
  13.   
  14.         <subclass name="cn.test.hibernate3.demo1.ClassC"  
  15.             discriminator-value="C">  
  16.             <property name="c1" column="C1" type="string" />  
  17.         </subclass>  
  18.   
  19.         <subclass name="cn.test.hibernate3.demo1.ClassD"  
  20.             discriminator-value="D">  
  21.             <property name="d1" column="D1" type="string" />  
  22.   
  23.             <subclass name="cn.test.hibernate3.demo1.ClassG"  
  24.                 discriminator-value="G">  
  25.                 <property name="g1" column="G1" type="string" />  
  26.             </subclass>  
  27.   
  28.             <subclass name="cn.test.hibernate3.demo1.ClassH"  
  29.                 discriminator-value="H">  
  30.                 <property name="h1" column="H1" type="string" />  
  31.             </subclass>  
  32.         </subclass>  
  33.     </class>  
  34.   
  35. </hibernate-mapping>  
例程 12-10  ClassB.hbm.xml
[html] view plain copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <!DOCTYPE hibernate-mapping PUBLIC   
  3.     "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
  4.     "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">  
  5. <hibernate-mapping>  
  6.     <class name="cn.test.hibernate3.demo1.ClassB" table="TABLE_B">  
  7.         <id name="id" type="long" column="ID">  
  8.             <generator class="increment" />  
  9.         </id>  
  10.         <property name="b1" type="string" column="B1" />  
  11.   
  12.         <joined-subclass name="cn.test.hibernate3.demo1.ClassE" table="TABLE_E">  
  13.             <key column="B_ID" />  
  14.             <property name="e1" column="E1" type="string" />  
  15.             <property name="e2" column="E2" type="string" />  
  16.             <property name="e3" column="E3" type="string" />  
  17.             <property name="e4" column="E4" type="string" />  
  18.             <property name="e5" column="E5" type="string" />  
  19.             <property name="e6" column="E6" type="string" />  
  20.         </joined-subclass>  
  21.   
  22.         <joined-subclass name="cn.test.hibernate3.demo1.ClassF" table="TABLE_F">  
  23.             <key column="B_ID" />  
  24.             <property name="f1" column="F1" type="string" />  
  25.             <property name="f2" column="F2" type="string" />  
  26.             <property name="f3" column="F3" type="string" />  
  27.             <property name="f4" column="F4" type="string" />  
  28.             <property name="f5" column="F5" type="string" />  
  29.             <property name="f6" column="F6" type="string" />  
  30.             <property name="f7" column="F7" type="string" />  
  31.         </joined-subclass>  
  32.   
  33.     </class>  
  34.   
  35. </hibernate-mapping>  


在 ClassA.hbm.xml文件中,在用于映射ClassD 的<subclass>元素中还嵌入了两个<subclass>元素,它们分别映射ClassG和ClassHH类。在<class>及所有的<subclass>元系中都设置了discriminator-value属性,Hibernate根据discriminator-value属性来判断TABLE_A表中的记录对应哪个类的实例,如果TABLE_A表的一条记录的A_TYPE
子段取值为“A”,表明它是ClassA类的实例;如果A_TYPE字段取值为“G”,表明它是ClassG类的实例,依次类推。
测试类如下:
TestHbmExtend
[java] view plain copy
  1. package cn.test.hibernate3.demo1;  
  2.   
  3. import org.hibernate.Session;  
  4. import org.hibernate.Transaction;  
  5. import org.junit.Test;  
  6.   
  7. import cn.test.hibernate3.utils.HibernateUtils;  
  8.   
  9. public class TestHbmExtend {  
  10.   
  11.     @Test  
  12.     // 测试保存操作  
  13.     public void demo1() {  
  14.         Session session = HibernateUtils.openSession();  
  15.         Transaction tx = session.beginTransaction();  
  16.         ClassA g = new ClassG("a1""d1""g1");  
  17.         ClassB f = new ClassF("b1""f1""f2""f3""f4""f5""f6""f7");  
  18.         session.save(g);  
  19.         session.save(f);  
  20.         tx.commit();  
  21.         session.close();  
  22.     }  
  23. }  
生成表的结构如下:


插入的数据如下所示:


阅读全文
0 0
原创粉丝点击