精通Hibernate映射继承关系之六
2007-12-23 12:33:51 来源:WEB开发网核心提示:Company与Employee类之间为一对多多态关联关系,如果继承关系树的根类对应一个表,精通Hibernate映射继承关系之六,或者每个类对应一个表,那么就能映射Company类的employees集合,永远不会访问其他形式的构造方法,提供以上形式的完整构造方法,本节介绍如何映射多对一多态关联,如图14-11所示
Company与Employee类之间为一对多多态关联关系,如果继承关系树的根类对应一个表,或者每个类对应一个表,那么就能映射Company类的employees集合。本节介绍如何映射多对一多态关联。如图14-11所示,ClassD与ClassA为多对一多态关联关系。
ClassA、ClassB和ClassC构成了一棵继承关系树,如果继承关系树的根类对应一个表,或者每个类对应一个表,那么可以按以下方式映射ClassD的a属性:
<many-to-one name="a" class="ClassA" column="A_ID" cascade="save-update" />
假定与ClassD对应的表为TABLE_D,与ClassA对应的表为TABLE_A,在TABLE_D中定义了外键A_ID,它参照TABLE_A表的主键。
ClassD对象的a属性既可以引用ClassB对象,也可以引用ClassC对象,例如:
tx = session.beginTransaction(); ClassD d=(ClassD)session.get("ClassD",id); ClassA a=d.getA(); if(a instanceof ClassB) System.out.PRintln(((ClassB)a).getB1()); if(a instanceof ClassC) System.out.println(((ClassC)a).getC1()); tx.commit();
以下代码在映射ClassD类的a属性时使用了延迟检索策略:
<many-to-one name="a" class="ClassA" column="A_ID" lazy="true" cascade="save-update" />
当Hibernate加载ClassD对象时,它的属性a引用ClassA的代理类实例,在这种情况下,如果对ClassA的代理类实例进行类型转换,会抛出ClassCastException:
ClassA a=d.getA(); ClassB b=(ClassB)a; //抛出ClassCastException
解决以上问题的一种办法是使用Session.load()方法:
ClassA a=d.getA(); ClassB b=(ClassB)session.load(ClassB.class,a.getId()); System.out.println(b.getB1());
当执行Session的load()方法时,Hibernate并不会访问数据库,而是仅仅返回ClassB的代理类实例。这种解决办法的前提条件是必须事先知道ClassD对象实际上和ClassA的哪个子类的对象关联。
解决以上问题的另一种办法是显式使用迫切左外连接检索策略,避免Hibernate创建ClassA的代理类实例,而是直接创建ClassA的子类的实例:
tx = session.beginTransaction(); ClassD d=(ClassD)session.createCriteria(ClassD.class) .add(EXPression.eq("id",id)) .setFetchMode("a",FetchMode.EAGER) .uniqueResult(); ClassA a=d.getA(); if(a instanceof ClassB) System.out.println(((ClassB)a).getB1()); if(a instanceof ClassC) System.out.println(((ClassC)a).getC1()); tx.commit();
如果继承关系树的具体类对应一个表,为了表达ClassD与ClassA的多态关联,需要在TABLE_D中定义两个字段:A_ID和A_TYPE,A_TYPE字段表示子类的类型,A_ID参照在子类对应的表中的主键。图14-12显示了表TABLE_D、TABLE_B和TABLE_C的结构。
在ClassD.hbm.xml文件中,用元素来映射ClassD的a属性:
<any name="a" meta-type="string" id-type="long" cascade="save-update"> <meta-value value="B" class="ClassB" /> <meta-value value="C" class="ClassC" /> <column name="A_TYPE" /> <column name="A_ID" /> </any>
元素的meta-type属性指定TABLE_D中A_TYPE字段的类型,id-type属性指定TABLE_D中A_ID字段的类型,
小结
本章介绍了映射继承关系的三种方式:
继承关系树的每个具体类对应一个表:在具体类对应的表中,不仅包含和具体类的属性对应的字段,还包含和具体类的父类的属性对应的字段。这种映射方式不支持多态关联和多态查询。
继承关系树的根类对应一个表:在根类对应的表中,不仅包含和根类的属性对应的字段,还包含和所有子类的属性对应的字段。
这种映射方式支持多态关联和多态查询,并且能获得最佳查询性能,缺点是需要对关系数据模型进行非常规设计,在数据库表中加入额外的区分各个子类的字段,此外,不能为所有子类的属性对应的字段定义not null约束。
继承关系树的每个类对应一个表:在每个类对应的表中只需包含和这个类本身的属性对应的字段,子类对应的表参照父类对应的表。
这种映射方式支持多态关联和多态查询,而且符合关系数据模型的常规设计规则,缺点是它的查询性能不如第二种映射方式。在这种映射方式下,必须通过表的内连接或左外连接来实现多态查询和多态关联。
在默认情况下,对于简单的继承关系树可以采用根类对应一个表的映射方式。如果必须保证关系数据模型的数据完整性,可以采用每个类对应一个表的映射方式。对于复杂的继承关系树,可以将它分解为几棵子树,对每棵子树采用不同的映射方式。
当然,在设计域模型时,应该尽量避免设计过分复杂的继承关系,这不仅会增加把域模型映射到关系数据模型的难度,而且也会增加在java程序代码中操纵持久化对象的复杂度。
对于不同的映射方式,必须创建不同的关系数据模型和映射文件,但是域模型是一样的,域模型中的持久化类的实现也都一样。
只要具备Java编程基础知识,就能创建具有继承关系的持久化类,因此本章没有详细介绍这些持久化类的创建过程,在此仅提醒一点,子类的完整构造方法不仅负责初始化子类本身的属性,还应该负责初始化从父类中继承的属性,例如以下是HourlyEmployee类的构造方法:
public class HourlyEmployee extends Employee{ private double rate; /** 完整构造方法*/ public HourlyEmployee(String name, double rate,Company company) { super(name,company); this.rate=rate; } /** 默认构造方法*/ public HourlyEmployee() {} …… }
Hibernate只会访问持久化类的默认构造方法,永远不会访问其他形式的构造方法。提供以上形式的完整构造方法,主要是为Java应用的编程提供方便。
(出处:http://www.cncms.com)
- ››Hibernate高级应用:性能优化策略
- ››hibernate 多对多关系详解(包括中间表,一对多字表...
- ››Hibernate实现mysql数据库limit查询方法
- ››精通Photoshop之通道详解
- ››Hibernate 之父:是时候升级到Java EE 6了
- ››Hibernate查询
- ››Hibernate和iBATIS比较(摘自网络)
- ››Hibernate使用Projections进行聚合操作
- ››hibernate中java.util.Date类型映射
- ››hibernate中update与saveOrUpdate的区别
- ››精通 Grails: 使用 Grails 进行单元测试(单元测试...
- ››Hibernate各种映射关系总结
更多精彩
赞助商链接