Company类和Employee类之间为一对多的双向关联关系,假设Employee有两个子类HourlyEmployee类和SalariedEmployee类, 此时Employee类为抽象类,不能被实例化。Employee类和HourlyEmployee类、SalariedEmployee类之间为继承关系, HourlyEmployee类和SalariedEmployee类继承自Employee类。
对于继承关系,以下介绍三种方式来将其映射到关系数据库。
1.继承关系树的每个具体类对应一个表
数据库表为:
create table COMPANIES (
ID bigint not null,
NAME varchar(15),
primary key (ID)
);
create table HOURLY_EMPLOYEES (
ID bigint not null,
NAME varchar(15),
RATE double precision,
COMPANY_ID bigint,
primary key (ID),
foreign key (COMPANY_ID) references COMPANIES (ID)
);
create table SALARIED_EMPLOYEES (
ID bigint not null,
NAME varchar(15),
SALARY double precision,
COMPANY_ID bigint,
primary key (ID),
foreign key (COMPANY_ID) references COMPANIES (ID)
);
HourlyEmployee类和HOURLY_EMPLOYEES表对应,SalariedEmployee类和SALARIED_EMPLOYEES表对应。
Company类、HourlyEmployee类和SalariedEmployee类都有相应的映射文件,而Employee类没有相应的映射文件。
由于关系数据模型没有描述Employee类和它的两个子类的继承关系,因此无法映射Company类的employees集合。
Company.java:
public class Company implements Serializable {
private Long id;
private String name;
private Set employees=new HashSet();
Constructor..; getter..; setter..;
}
Company.hbm.xml:
<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>
Employee.java:
abstract public class Employee implements Serializable {
private Long id;
private String name;
private Company company;
Constructor..; getter..; setter..;
}
HourlyEmployee.java:
public class HourlyEmployee extends Employee{
private double rate;
Constructor..; getter..; setter..;
}
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" column="RATE" type="double" />
<many-to-one
name="company"
column="COMPANY_ID"
class="mypack.Company"
/>
</class>
</hibernate-mapping>
SalariedEmployee.java:
public class SalariedEmployee extends Employee {
private double salary;
Constructor..; getter..; setter..;
}
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" column="SALARY" type="double" />
<many-to-one
name="company"
column="COMPANY_ID"
class="mypack.Company"
/>
</class>
</hibernate-mapping>
这种情况下,不能直接进行以下查询:
session.createQuery("from Employee").list();
2.继承关系树的根类对应一个表
只需为Employee类创建一张表EMPLOYEES,该表需要额外加入一个EMPLOYEE_TYPE字段,用于区分Employee的具体类型。
数据库表为:
create table COMPANIES (
ID bigint not null,
NAME varchar(15),
primary key (ID)
);
create table EMPLOYEES (
ID bigint not null,
NAME varchar(15),
EMPLOYEE_TYPE varchar(2),
RATE double precision,
SALARY double precision,
COMPANY_ID bigint,
primary key (ID) foreign key (COMPANY_ID) references COMPANIES (ID)
);
Company类和Employee类有相应的映射文件,而HourlyEmployee类和SalariedEmployee类没有相应的映射文件。
Company.java:
public class Company implements Serializable {
private Long id;
private String name;
private Set employees=new HashSet();
Constructor..; getter..; setter..;
}
Company.hbm.xml:
<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.java:
abstract public class Employee implements Serializable {
private Long id;
private String name;
private Company company;
Constructor..; getter..; setter..;
}
HourlyEmployee.java:
public class HourlyEmployee extends Employee{
private double rate;
Constructor..; getter..; setter..;
}
SalariedEmployee.java:
public class SalariedEmployee extends Employee {
private double salary;
Constructor..; getter..; setter..;
}
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" column="RATE" type="double" />
</subclass>
<subclass name="mypack.SalariedEmployee" discriminator-value="SE" >
<property name="salary" column="SALARY" type="double" />
</subclass>
</class>
</hibernate-mapping>
<discriminator>元素指定EMPLOYEES表中用于区分Employee类型的字段EMPLOYEE_TYPE,<subclass>元素用于映射HourlyEmployee类和SalariedEmployee类,discriminator-value属性指定EMPLOYEE_TYPE字段的取值。
如果Employee累不是抽象类,本身可实例化,可在<class>元素中定义其discriminator值:
<class name="mypack.Employee" table="EMPLOYEES"discriminator-value="EE">
当进行以下查询时:
session.createQuery("fromHourlyEmployee").list();
Hibernate执行的SQL语句为:
select * from EMPLOYEES whereEMPLOYEE_TYPE='HE';
3.继承关系树的每个类对应一个表
EMPLOYEES表仅包含和Employee类的属性对应的字段,HE表仅包含和HourlyEmployee类的属性对应的字段,SE表仅包含和SalariedEmployee类的属性对应的字段。HE表和SE表都以EMPLOYEE_ID作为主键,该字段同时作为外键参照EMPLOYEES表。
数据库表为:
create table COMPANIES (
ID bigint not null,
NAME varchar(15),
primary key (ID)
);
create table EMPLOYEES (
ID bigint not null,
NAME varchar(15),
COMPANY_ID bigint,
primary key (ID),
foreign key (COMPANY_ID) references COMPANIES (ID)
);
create table HOURLY_EMPLOYEES (
EMPLOYEE_ID bigint not null,
RATE double precision,
primary key (EMPLOYEE_ID),
foreign key (EMPLOYEE_ID) references EMPLOYEES (ID)
);
create table SALARIED_EMPLOYEES (
EMPLOYEE_ID bigint not null,
SALARY double precision,
primary key (EMPLOYEE_ID),
foreign key (EMPLOYEE_ID) references EMPLOYEES (ID)
);
Company类和Employee类有相应的映射文件,而HourlyEmployee类和SalariedEmployee类没有相应的映射文件。
Company.java:
public class Company implements Serializable {
private Long id;
private String name;
private Set employees=new HashSet();
Constructor..; getter..; setter..;
}
Company.hbm.xml:
<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.java:
abstract public class Employee implements Serializable {
private Long id;
private String name;
private Company company;
Constructor..; getter..; setter..;
}
HourlyEmployee.java:
public class HourlyEmployee extends Employee{
private double rate;
Constructor..; getter..; setter..;
}
SalariedEmployee.java:
public class SalariedEmployee extends Employee {
private double salary;
Constructor..; getter..; setter..;
}
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="EMPLOYEE_ID" />
<property name="rate" column="RATE" type="double" />
</joined-subclass>
<joined-subclass name="mypack.SalariedEmployee" table="SALARIED_EMPLOYEES" >
<key column="EMPLOYEE_ID" />
<property name="salary" column="SALARY" type="double" />
</joined-subclass>
</class>
</hibernate-mapping>
<joined-subclass>元素用于映射Employee类的两个子类的属性。
当进行以下查询时:
session.createQuery("fromHourlyEmployee").list();
Hibernate执行的SQL语句为:
select * fromHOURLY_EMPLOYEES he inner join EMPLOYEESe on he.EMPLOYEE_ID = e.ID;
当进行以下查询时:
session.createQuery("fromEmployee").list();
Hibernate执行的SQL语句为:
select * fromEMPLOYEES e
left outer joinHOURLY_EMPLOYEES he on he.EMPLOYEE_ID = e.ID
left outer join SALARIED_EMPLOYEES se on se.EMPLOYEE_ID = e.ID;
4.映射复杂继承关系
下图为一棵复杂的继承关系树,其中DOClass类为抽象类,其他均为具体类。
可将其分解为三棵子树:
DOClass、ClassA和ClassB为一棵子树,DOClass为抽象类,通常不会对其进行多态查询,因此可采用每个具体类对应一个表的映射方式,ClassA对应TABLE_A表,ClassB对应TABLE_B表。
ClassA、ClassC、ClassD、ClassG和ClassH为一棵子树,ClassA的所有子类均只包含少量属性,因此可采用根类对应一个表的映射方式,ClassA对应TABLE_A表。
ClassB、ClassE和ClassF为一棵子树,ClassB的两个子类均包含很多属性,因此可采用每个类对应一个表的映射方式, ClassB对应TABLE_B表,ClassE对应TABLE_E表,ClassF对应TABLE_F表。
因此,只需创建TABLE_A、TABLE_B、TABLE_E和TABLE_F四张表即可,其中TABLE_A包含DOClass、ClassA、ClassC、ClassD、ClassG和ClassH的属性所对应的字段,TABLE_B包含DOClass和ClassB的属性所对应的字段,TABLE_E和TABLE_F的B_ID字段即使主键,又是参照TABLE_B的外键。
数据库表为:
create table TABLE_A (
ID bigint not null,
A1 varchar(15),
C1 varchar(15),
D1 varchar(15),
G1 varchar(15),
H1 varchar(15),
A_TYPE char(1),
primary key (ID)
);
create table TABLE_B (
ID bigint not null,
B1 varchar(15),
primary key (ID)
);
create table TABLE_E (
B_ID bigint not null,
E1 varchar(15),
E2 varchar(15),
E3 varchar(15),
E4 varchar(15),
E5 varchar(15),
E6 varchar(15),
primary key (B_ID),
foreign key (B_ID) references TABLE_B(ID)
);
create table TABLE_F (
B_ID bigint not null,
F1 varchar(15),
F2 varchar(15),
F3 varchar(15),
F4 varchar(15),
F5 varchar(15),
F6 varchar(15),
F7 varchar(15),
primary key (B_ID),
foreign key (B_ID) references TABLE_B(ID)
);
DOClass.java、ClassA.java...ClassH.java均是普通的pojo,只包含其各自属性(如ClassA只包含a1属性)、Constructor以及getter、setter方法。
只需创建ClassA和ClassB的映射文件即可。
ClassA.hbm.xml:
<hibernate-mapping >
<class name="mypack.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="mypack.ClassC" discriminator-value="C" >
<property name="c1" column="C1" type="string" />
</subclass>
<subclass name="mypack.ClassD" discriminator-value="D" >
<property name="d1" column="D1" type="string" />
<subclass name="mypack.ClassG" discriminator-value="G" >
<property name="g1" column="G1" type="string" />
</subclass>
<subclass name="mypack.ClassH" discriminator-value="H" >
<property name="h1" column="H1" type="string" />
</subclass>
</subclass>
</class>
</hibernate-mapping>
ClassB.hbm.xml:
<hibernate-mapping >
<class name="mypack.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="mypack.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="mypack.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" />
</joined-subclass >
</class>
</hibernate-mapping>
注: 在<subclass>元素中只能嵌入 <subclass>子元素,而不能嵌入 <joined-subclass>子元素,而在 <joined-subclass>元素中只能嵌入 <joined-subclass>子元素,而不能嵌入 <subclass>子元素。