继承关系是关系数据与面向对象数据结构之间的主要差异之一,hibernate中支持3种类型的继承形式:
1。表与子类之间的独立一对一关系
如 B和C 继承A,那么就有两张表分别对应B C,他们都含有A的内容
2。每个子类对应一张子表,并与主类共享主表
现在就要有三张表,为父类单独设立一张表,子表中只包含子类所扩展的属性,子表和父表通过外键相关联。
3。表与类的一对多关系
现在只要一张表,同时包含个子类,只是添加一个字段来区分不同的子类。
下面通过例子看一下这几种形式的做法。
图书大厦中既有书籍又有音像(如DVD),他们都是货物,但又有不同的属性,书籍有页数的属性,DVD有区域码的属性。我们将共性抽象为一个基类,子类体现具体特性。
如基类为Item(id, name), Book(page), DVD(regionCode)
1。表与子类之间的独立一对一关系
我们建两张表 book(id, name, pageCount), dvd(id, name, regionCode)
由于book,dvd由item继承而来,自然继承了item中的id,name属性,每个子类都分别对应一个独立的表,这张表中包含了子类所需的所有字段。
//book.hbm.xml
<class
name="Book"
table="book"
>
<id
name="Id"
type="integer"
column="id"
>
<generator class="native"/>
</id>
<property
name="Name"
column="name"
type="string"
not-null="true"
length="45"
/>
<property
name="PageCount"
column="pageCount"
type="integer"
not-null="true"
length="10"
/>
</class>
//Dvd.hbm.xml
<class
name="Dvd"
table="dvd"
>
<id
name="Id"
type="integer"
column="id"
>
<generator class="native"/>
</id>
<property
name="Name"
column="name"
type="string"
not-null="true"
length="45"
/>
<property
name="RegionCode"
column="regionCode"
type="string"
not-null="true"
length="45"
/>
</class>
可以看到,其映射方式与普通单表映射并没有什么不同,那么,Book 与 DVD是不是没有一点关系呢?
Book,Dvd继承了Item,这种关系在hibernate中以多态(polymorphism)进行描述,上面的配置文件的class节点中并没有出现polymorphism属性设定,这也意味着Book Dvd均采用了默认的隐式多态(polymorphism="implicit").
当我们执行HQL:“from Item"时,hibernate会自动在当前环境中查找所有polymorphism="implicit"的子类,返回子类对应的所有库表记录。
执行代码:
List list = (List)session.createQuery("from Item").list();
Iterator it = list.iterator();
while(it.hasNext()){
Item item = (Item)it.next();
System.out.println(item);
}
得到输出:
Hibernate: select dvd0_.id as id2_, dvd0_.name as name2_, dvd0_.regionCode as regionCode2_ from dvd dvd0_
Hibernate: select book0_.id as id1_, book0_.name as name1_, book0_.pageCount as pageCount1_ from book book0_
可以看到hibernate同时查询了book dvd
这种模式是最简单的映射方式,但也有很大的局限性,如果父类有变化就得改每个子类的表,如果有很多子类,这时只想根据名字查出id,就得查询所有子类表后汇总,可见性能低下。
2。每个子类对应一张子表,并与主类共享主表
将父类Item单独映射到一张主表,Book,Dvd分别单独设立一张子表,子表中只包含子类的扩展的属性,同时,子表和父表通过外键相关联。
映射关系如下:
<class
name="Item"
table="item"
>
<meta attribute="sync-DAO">false</meta>
<id
name="Id"
type="integer"
column="id"
>
<generator class="native"/>
</id>
<property
name="Name"
column="name"
type="string"
not-null="true"
length="45"
/>
<joined-subclass name="Dvd" table="dvd">
<key column="id"/>
<property
name="RegionCode"
column="regioncode"
type="string"
not-null="true"
length="45"
/>
</joined-subclass>
<joined-subclass name="Book" table="book">
<key column="id"/>
<property
name="PageCount"
column="pagecount"
type="integer"
not-null="true"
length="10"
/>
</joined-subclass>
</class>
我们并没有单独为Book,Dvd编写单独的映射文件,而是通过 joined-subclass 节点在父类映射文件中配置。
joined-subclass节点格式与class节点类似,且joined-subclass可以嵌套,也就是说还可以为子类再指定子类。
执行代码:
Book book = new Book();
book.setName("abc");
book.setPageCount(100);
Dvd dvd = new Dvd();
dvd.setName("dd");
dvd.setRegionCode("1111111");
session.save(dvd);
session.save(book);
输出:
Hibernate: insert into item (name) values (?)
Hibernate: insert into dvd (regioncode, id) values (?, ?)
Hibernate: insert into item (name) values (?)
Hibernate: insert into book (pagecount, id) values (?, ?)
执行查询代码:
List list = (List)session.createQuery("from Item").list();
Iterator it = list.iterator();
while(it.hasNext()){
Item item = (Item)it.next();
System.out.println(item);
}
输出:
Hibernate: select item0_.id as id1_, item0_.name as name1_, item0_1_.regioncode as regioncode2_, item0_2_.pagecount as pagecount3_,
case when item0_1_.id is not null then 1 when item0_2_.id is not null then 2 when item0_.id is not null then 0 end as clazz_ from item item0_ left outer join dvd item0_1_ on item0_.id=item0_1_.id left outer join book item0_2_ on item0_.id=item0_2_.id
这种模式带来了更加清晰的数据逻辑划分,但看以看到,多表操作带来的性能消耗也是比较大的。
3。表与类的一对多关系
我们来修改item表的结构,现在他不但包括了原来的id,name字段,同时也包含了dvd,book的regioncode,pagecount字段,不同的类型通过类型(categroy)字段来区分,不论是dvd还是book类都可以保存在这张表中。
//Item.hbm.xml
<class
name="Item"
table="item"
>
<meta attribute="sync-DAO">false</meta>
<id
name="Id"
type="integer"
column="id"
>
<generator class="native"/>
</id>
<discriminator
column="category"
type="string"
/>
<property
name="Name"
column="name"
type="string"
not-null="true"
length="45"
/>
<subclass name="Dvd" discriminator-value="1">
<property
name="RegionCode"
column="regioncode"
type="string"
/>
</subclass>
<subclass name="Book" discriminator-value="2">
<property
name="PageCount"
column="pagecount"
type="integer"
/>
</subclass>
</class>
其中:通过discriminator节点,我们声明了用作子类辨别标识的字段名,指定当辨别字段值为1时,对应子类为Dvd,为2时,对应的子类为Book。运行期hibernate在读取item表时,会根据指定的辨别标示进行判断。
执行代码:
List list = (List)session.createQuery("from Book").list();
Iterator it = list.iterator();
while(it.hasNext()){
Book item = (Book)it.next();
System.out.println(item);
}
list = (List)session.createQuery("from Dvd").list();
it = list.iterator();
while(it.hasNext()){
Dvd item = (Dvd)it.next();
System.out.println(item);
输出:
Hibernate: select book0_.id as id1_, book0_.name as name1_, book0_.pagecount as pagecount1_ from item book0_ where book0_.category='2'
Hibernate: select dvd0_.id as id1_, dvd0_.name as name1_, dvd0_.regioncode as regioncode1_ from item dvd0_ where dvd0_.category='1'
可以看到,hibernate已经根据配置自动进行了类型识别。
