在这种映射方式下,继承关系树的每个类以及接口都对应一个表。在本例中,需要创建employees、he和se表。
如图14-6所示,employees表仅包含和employee类的属性对应的字段,he表仅包含和hourlyemployee类的属性对应的字段,se表仅包含和salariedemployee类的属性对应的字段。此外,he表和se表都以employee_id字段作为主键,该字段还同时作为外键参照employees表。


14.3.1 创建映射文件
从company类到employee类是多态关联,由于关系数据模型描述了employee类和它的两个子类的继承关系,因此可以映射company类的employees集合。例程14-7是company.hbm.xml文件的代码,该文件不仅映射了company类的id和name属性,还映射了它的employees集合。
例程14-7 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"
lazy="true" >
<key column="company_id" />
<one-to-many class="mypack.employee" />
</set>
</class>
</hibernate-mapping>
employee.hbm.xml文件用于把employee类映射到employees表,在这个映射文件中,除了需要映射employee类本身的属性,还需要在元素中映射两个子类的属性。例程14-8是employee.hbm.xml文件的代码。
例程14-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="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>
在employee.hbm.xml文件中,两个元素用于映射hourlyemployee类和salariedemployee类,元素的子元素指定he表和se表中既作为主键又作为外键的employee_id字段。
由于hourlyemployee类和salariedemployee类没有单独的映射文件,因此在初始化hibernate时,只需向configuration对象中加入company类和employee类:
configuration config = new configuration();
config.addclass(company.class)
.addclass(employee.class);
也可以在单独的映射文件中配置或元素,但此时必须显式设定它们的extends属性。例如可以在单独的hourlyemployee.hbm.xml文件中映射hourlyemployee类:
<hibernate-mapping >
<joined-subclass
name="mypack.hourlyemployee"
table="hourly_employees"
extends="mypack.employee" >
……
</joined-class>
<hibernate-mapping >
由于hourlyemployee类的映射代码不位于employee.hbm.xml文件中,因此在初始化hibernate时,不仅需要向configuration对象中加入company类和employee类,还需要加入hourlyemployee类,并且必须先加入employee父类,再加入hourlyemployee子类:
configuration config = new configuration();
config.addclass(company.class)
.addclass(employee.class)
.addclass(hourlyemployee.class);
如果颠倒加入employee类和hourlyemployee子类的顺序,hibernate在执行addclass()方法时会抛出hibernatemappingexception。
14.3.2 操纵持久化对象
这种映射方式支持多态查询,对于以下查询语句:
list employees=session.find("from employee");
hibernate会检索出所有的hourlyemployee对象和salariedemployee对象。此外,也可以单独查询employee类的两个子类的实例,例如:
list hourlyemployees=session.find("from hourlyemployee");
本节的范例程序位于配套光盘的sourcecode/chapter14/14.3目录下,运行该程序前,需要在sampledb数据库中手工创建companies表、employees表、he表和se表,然后加入测试数据,相关的sql脚本文件为/14.3/schema/sampledb.sql。
在dos命令行下进入chapter14根目录,然后输入命令:
ant -file build3.xml run
就会运行businessservice类。businessservice的main()方法调用test()方法,test()方法依次调用以下方法:
findallhourlyemployees():检索数据库中所有的hourlyemployee对象。
findallemployees():检索数据库中所有的employee对象。
loadcompany():加载一个company对象。
saveemployee():保存一个employee对象。
(1)运行findallhourlyemployees()方法,它的代码如下:
tx = session.begintransaction();
list results=session.find("from hourlyemployee");
tx.commit();
return results;
在运行session的find()方法时,hibernate执行以下select语句:
select * from hourly_employees he inner join employees e
on he.employee_id=e.id;
select * from companies where id=1;
hibernate通过he表与employees表的内连接获得hourlyemployee对象的所有属性值,此外,在加载hourlyemployee对象时,还会同时加载与它关联的company对象。
(2)运行findallemployees()方法,它的代码如下:
tx = session.begintransaction();
list results=session.find("from employee");
tx.commit();
return results;
在运行session的find()方法时,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;
select * from companies where id=1;
hibernate把employees表与he表以及se表进行左外连接,从而获得hourlyemployee对象和salariedemployee对象的所有属性值。在这种映射方式下,hibernate支持多态查询,对于以上查询语句获得的查询结果,如果he表的employee_id字段不为null,就创建houlyemployee实例,如果se表的employee_id字段不为null,就创建salariedemployee实例,这些实例所关联的company对象也被加载。(3)运行loadcompany()方法,它的代码如下:
tx = session.begintransaction();
company company=(company)session.load(company.class,new long(id));
hibernate.initialize(company.getemployees());
tx.commit();
这种映射方式支持多态关联。如果在company.hbm.xml文件中对employees集合设置了立即检索策略,那么session的load()方法加载的company对象的employees集合中包含所有关联的employee对象。由于本书提供的company.hbm.xml文件对employees集合设置了延迟检索策略,因此以上程序代码还通过hibernate类的静态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, company_id) values (5, 'mary', 1);
insert into hourly_employees (employee_id ,rate) values (5, 300);
可见,每保存一个hourlyemployee对象,需要分别向employees表和he表插入一条记录,employees表的记录和he表的记录共享同一个主键。
闽公网安备 35060202000074号