一、继承映射
继承是面向对象很重要的特性,它实现了代码的服用,在关系模型中同样也有继承关系,这种继承关系其实可以看做是一种枚举关系,一种类型中可以枚举出很多子类型,这些子类型和父对象形成了继承关系,能够对其进行枚举的大部分都可以看做是一种继承映射,所以这种枚举关系可以看做是继承映射,例如动物就是一种抽象类,它是其它动物猪、猫等的父类,它们之间就是一种继承关系,如下图:
这种继承映射在转化为关系模型后会生成一张表,那么这张表是如何区分这两种类型的呢?用的是关系字段,需要在表中添加类型字段,使用关键字来标明对象的类型。所以上图中的对象模型对应的表结构如下:
在生成表结构时,需要添加对应的字段类型,所以需要在映射文件中添加对应的映射鉴别器,这里就需要使用discriminator-value属性。
1.类文件
类文件中没有需要注意的地方,在编写时注意之间的继承关系即可。
清单一:Animal类代码,只需要添加基本的属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package com.src.hibernate;
public class Animal {
//id号
private int id;
public int getId() {
return id;
}
public void setId( int id) {
this .id = id;
}
//名称
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this .name = name;
}
//性别
private boolean sex;
public boolean isSex() {
return sex;
}
public void setSex( boolean sex) {
this .sex = sex;
}
}
|
清单二:Bird和Pig类,添加基本的属性,并继承Animal类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package com.src.hibernate;
public class Bird extends Animal {
//高度
private int height;
public int getHeight() {
return height;
}
public void setHeight( int height) {
this .height = height;
}
}
package com.src.hibernate;
public class Pig extends Animal {
//重量
private int weight;
public int getWeight() {
return weight;
}
public void setWeight( int weight) {
this .weight = weight;
}
}
|
2.映射文件
映射文件中需要添加对应的映射,该模型中只需要添加一个映射文件,因为只生成一张表,在映射文件中添加对应的子类映射,使用<subclass>标签,标签中添加鉴别器discriminator-value,该鉴别器属性指明了在数据库中写入数据时指示写入的是何种类型,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
<? xml version = "1.0" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping >
< class name = "com.src.hibernate.Animal" table = "t_animal" >
< id name = "id" >
< generator class = "native" />
</ id >
<!-- 加入鉴别标签,且必须放在id后面 -->
< discriminator column = "type" />
< property name = "name" />
< property name = "sex" type = "boolean" />
< subclass name = "com.src.hibernate.Pig" discriminator-value = "P" >
< property name = "weight" />
</ subclass >
< subclass name = "com.src.hibernate.Bird" discriminator-value = "B" >
< property name = "height" />
</ subclass >
</ class >
</ hibernate-mapping >
|
3.分析结果
生成的MySQL数据库表中不仅会添加Animal的基本属性,而且会添加Pig和Bird的属性,因为在映射文件中使用<subclass>写出了所添加的属性,另外还添加了相应的鉴别器属性,所以在数据库中会添加对应的鉴别列,生成的表结构如下图:
二、数据操作
1.写入数据
在进行数据读取和写入操作时需要注意类中的操作使用了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
public void testSave(){
Session session= null ;
try {
//创建session对象
session=HibernateUtils.getSession();
//开启事务
session.beginTransaction();
Pig pig= new Pig();
pig.setName( "小猪猪" );
pig.setSex( true );
pig.setWeight( 200 );
session.save(pig);
Bird bird= new Bird();
bird.setName( "xiaoniaoniao" );
bird.setSex( true );
bird.setHeight( 100 );
session.save(bird);
session.getTransaction().commit();
} catch (Exception e){
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
}
|
2.多态查询--get和hql
基本的查询方法,只需要使用load和get方法即可,这里重点讨论多态查询。多态查询是指Hibernate在加载对象时能够采用instanceof鉴别出其真正的类型的对象即可为多态查询。
Note:多态查询不支持延迟加载,也就是说如果使用load方法,需要在映射文件中将延迟加载设置为false。
3.load延迟加载
load支持延迟加载,在加载对象时其实生成的是对象的代理,所以在使用多态查询时需要在映射文件中将延迟加载设置为false,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<? xml version = "1.0" ?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
< hibernate-mapping >
< class name = "com.src.hibernate.Animal" table = "t_animal" lazy = "false" >
< id name = "id" >
< generator class = "native" />
</ id >
<!-- 加入鉴别标签,且必须放在id后面 -->
< discriminator column = "type" />
< property name = "name" />
< property name = "sex" type = "boolean" />
< subclass name = "com.src.hibernate.Pig" discriminator-value = "P" >
< property name = "weight" />
</ subclass >
< subclass name = "com.src.hibernate.Bird" discriminator-value = "B" >
< property name = "height" />
</ subclass >
</ class >
</ hibernate-mapping >
|
load加载方法,使用load加载该示例中支持多态查询,在配置文件中将延迟加载设置为false,所以这里使用load方法能够加载获得相应的对象类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public void testLoad(){
Session session= null ;
try {
session=HibernateUtils.getSession();
session.beginTransaction();
Animal ani=(Animal)session.load(Animal. class , 1 );
System.out.println(ani.getName());
//因为load默认支持lazy,所以我们看到的是Animal的代理
//所以采用了instanceof无法鉴别出真正的类型Pig
//所以load在此情况下是不支持多态查询的
if (ani instanceof Pig){
System.out.println( "我是小猪猪!" );
} else {
System.out.println( "我不是小猪猪!" );
}
session.getTransaction().commit();
} catch (Exception e){
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
}
|
4.hql查询
hql支持多态查询,这主要由于查询出的是一个真正的对象,并不会返回一个代理,所以hql支持多态查询,另外在查询时需要注意查询语句中不要使用表名,而是要使用类名称,Hibernate会根据类名称将其映射为对应的表名称,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public void testLoad5(){
Session session= null ;
try {
session=HibernateUtils.getSession();
session.beginTransaction();
List<Animal> list=session.createQuery( "from Animal" ).list();
for (Iterator iter=list.iterator();iter.hasNext();){
Animal a=(Animal)iter.next();
if (a instanceof Pig){
System.out.println( "我是小猪猪!" );
} else {
System.out.println( "我不是小猪猪!" );
}
}
session.getTransaction().commit();
} catch (Exception e){
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
}
|
查询结果:
1
2
3
4
5
|
Hibernate: select animal0_.id as id0_, animal0_.name as name0_, animal0_.sex as sex0_, animal0_.weight as weight0_, animal0_.height as height0_, animal0_.type as type0_ from t_animal animal0_
我是小猪猪!
我不是小猪猪!
我是小猪猪!
我不是小猪猪!
|
三、继承映射三种策略
1. 每个类分层结构一张表(Table per class hierarchy)
假设我们有接口Payment和它的几个实现类: CreditCardPayment, CashPayment, 和ChequePayment。则“每个类分层结构一张表”(Table per class hierarchy)的映射代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
< class name = "Payment" table = "PAYMENT" >
< id name = "id" type = "long" column = "PAYMENT_ID" >
< generator class = "native" />
</ id >
< discriminator column = "PAYMENT_TYPE" type = "string" />
< property name = "amount" column = "AMOUNT" />
...
< subclass name = "CreditCardPayment" discriminator-value = "CREDIT" >
< property name = "creditCardType" column = "CCTYPE" />
...
</ subclass >
< subclass name = "CashPayment" discriminator-value = "CASH" >
...
</ subclass >
< subclass name = "ChequePayment" discriminator-value = "CHEQUE" >
...
</ subclass >
</ class >
|
采用这种策略只需要一张表即可。它有一个很大的限制:要求那些由子类定义的字段, 如CCTYPE,不能有非空(NOT NULL)约束。
2. 每个子类一张表(Table per subclass)
对于上例中的几个类而言,采用“每个子类一张表”的映射策略,代码如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
< class name = "Payment" table = "PAYMENT" >
< id name = "id" type = "long" column = "PAYMENT_ID" >
< generator class = "native" />
</ id >
< property name = "amount" column = "AMOUNT" />
...
< joined-subclass name = "CreditCardPayment" table = "CREDIT_PAYMENT" >
< key column = "PAYMENT_ID" />
...
</ joined-subclass >
< joined-subclass name = "CashPayment" table = "CASH_PAYMENT" >
< key column = "PAYMENT_ID" />
< property name = "creditCardType" column = "CCTYPE" />
...
</ joined-subclass >
< joined-subclass name = "ChequePayment" table = "CHEQUE_PAYMENT" >
< key column = "PAYMENT_ID" />
...
</ joined-subclass >
</ class >
|
需要四张表。三个子类表通过主键关联到超类表(因而关系模型实际上是一对一关联)。
3. 每个子类一张表(Table per subclass),使用辨别标志(Discriminator)
注意,对“每个子类一张表”的映射策略,Hibernate的实现不需要辨别字段,而其他 的对象/关系映射工具使用了一种不同于Hibernate的实现方法,该方法要求在超类 表中有一个类型辨别字段(type discriminator column)。Hibernate采用的方法更 难实现,但从关系(数据库)这点上来看,按理说它更正确。若你愿意使用带有辨别字 段的“每个子类一张表”的策略,你可以结合使用<subclass> 与<join>,如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
< class name = "Payment" table = "PAYMENT" >
< id name = "id" type = "long" column = "PAYMENT_ID" >
< generator class = "native" />
</ id >
< discriminator column = "PAYMENT_TYPE" type = "string" />
< property name = "amount" column = "AMOUNT" />
...
< subclass name = "CreditCardPayment" discriminator-value = "CREDIT" >
< join table = "CREDIT_PAYMENT" >
< property name = "creditCardType" column = "CCTYPE" />
...
</ join >
</ subclass >
< subclass name = "CashPayment" discriminator-value = "CASH" >
< join table = "CASH_PAYMENT" >
...
</ join >
</ subclass >
< subclass name = "ChequePayment" discriminator-value = "CHEQUE" >
< join table = "CHEQUE_PAYMENT" fetch = "select" >
...
</ join >
</ subclass >
</ class >
|
可选的声明fetch="select",是用来告诉Hibernate,在查询超类时, 不要使用外部连接(outer join)来抓取子类ChequePayment的数据。