继承映射会打破对象的封装性,所以少用继承映射,多用关联映射
1、继承实现的三种策略
- 单表继承。每棵类继承树使用一个表(table per class hierarchy) ,推荐使用
- 具体表继承。每个类一个表(table per subclass)
- 类表继承。每个子类一个表(table per concrete class)(有一些限制)
2、 理解如何映射
因为类继承树肯定是对应多个类,要把多个类的信息存放在一张表中,必须有某种机制来区分哪些记录是属于哪个类的。这种机制就是,在表中添加一个字段,用这个字段的值来进行区分。
用hibernate实现这种策略的时候,有如下步骤:
- 父类用普通的<class>标签定义
在父类中定义一个discriminator(类鉴别器),即指定这个区分的字段的名称和类型
如:<discriminator column=”XXX” type=”string”/>
- 子类使用<subclass>标签定义,在定义subclass的时候,需要注意如下几点:
Subclass标签的name属性是子类的全路径名
在Subclass标签中,用discriminator-value属性来标明本子类的discriminator字段(用来区分不同类的字段)的值
Subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),也可以与class标签平行。 当subclass标签的定义与class标签平行的时候,需要在subclass标签中,添加extends属性,里面的值是父类的全路径名称。
子类的其它属性,像普通类一样,定义在subclass标签的内部。
3、继承类-----每个类继承树映射成一张表
image.pngAnimal类
public class Animal {
private int id;
private String name;
private boolean sex;
Pig类
public class Pig extends Animal {
private int weight;
Bird类
public class Bird extends Animal {
private int height;
- 子类不能继承父类的私有属性
- 创建了子类对象并没有创建父类对象,但是子类对象为父类的私有属性开辟空间,可以通过get、set方法使用父类的私有属性,并且调用父类的构造方法为这些属性初始化
三个实体类对应一个hbm映射文件
<class name=“com.bjsxt.hibernate.Animal" table="t_animal" lazy="false">
<id name="id">
<generator class="native"/>
</id>
<discriminator column="type" type="string"/>
<property name="name"/> 把string类型变成varchar
<property name="sex"/> 把boolean类型变成bit
<subclass name="com.bjsxt.hibernate.Pig" discriminator-value="P"> //往鉴别器字段加鉴别值
<property name="weight"/>
</subclass>
<subclass name="com.bjsxt.hibernate.Bird" discriminator-value="B">
<property name="height"/>
</subclass>
</class>
<discriminator column="type" type="string"/>含义:
加个鉴别器字段,这个string类型是hibernate为我们字符串定义的类型,因为在java中找不到
注意:
鉴别器字段要放在主键之后,property属性之前,<subclass>里加鉴别值
理解如何存储
存储的时候hibernate会自动将鉴别字段值插入到数据库中。
在加载数据的时候,hibernate能根据这个鉴别值正确的加载对象。
多态查询:在hibernate加载数据的时候能鉴别出正真的类型(instanceOf)
get支持多态查询
load只有在lazy=false,才支持多态查询
Hql 支持多态查询
缺点:数据冗余
理解如何映射
这种策略是使用union-subclass标签来定义子类的。每个子类对应一张表,而且这个表的信息是完备的,即包含了所有从父类继承下来的属性映射的字段(这就是它跟joined-subclass的不同之处,joined-subclass定义的子类的表,只包含子类特有属性映射的字段)。实现这种策略的时候,有如下步骤:
父类用普通<class>标签定义即可
子类用<union-subclass>标签定义,在定义union-subclass的时候,需要注意如下几点:
Union-subclass标签不再需要包含key标签(与joined-subclass不同)
Union-subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),也可以与class标签平行。 当Union-subclass标签的定义与class标签平行的时候,需要在Union-subclass标签中,添加extends属性,里面的值是父类的全路径名称。
子类的其它属性,像普通类一样,定义在Union-subclass标签的内部。这个时候,虽然在union-subclass里面定义的只有子类的属性,但是因为它继承了父类,所以,不需要定义其它的属性,在映射到数据库表的时候,依然包含了父类的所有属性的映射字段。
5、存储
Pig pig = new Pig();
pig.setName("pig");//给父类的name属性赋值为“pig”
pig.setSex(true);//给父类的sex属性赋值为true
pig.setWeight(100);//给父类的weigh属性赋值为100
给pig对象对应的类所对应的表(t_animal表)存储数据。
id自动产生;
调用Pig的getName(),把父类的name属性的值存到t_animal表的name字段上;
调用Pig的getSex(),把父类的sex属性的值存到t_animal表的sex字段上;
调用Pig的getName(),把子类的weigh属性的值存到t_animal表的weight字段上;
自动把鉴别值P设到鉴别器字段上去session.save(pig);
Bird bird = new Bird();
bird.setName("bird");//给父类的name属性赋值为“bird”
bird.setSex(false);//给父类的sex属性赋值为false
bird.setHeight(50);//给父类的weigh属性赋值为50
给pig对象对应的类所对应的表(t_animal表)存储数据。
id自动产生;
调用Pig的getName(),把父类的name属性的值存到t_animal表的name字段上;
调用Pig的getSex(),把父类的sex属性的值存到t_animal表的sex字段上;
调用Pig的getName(),把子类的weigh属性的值存到t_animal表的weight字段上;
自动把鉴别值P设到鉴别器字段上去session.save(bird);
6、加载
(多态查询:可以根据鉴别器字段创建出对应的对象)
如果懒加载开启,load不支持多态查询
不管懒加载是否开启,get都支持多态查询
(1)采用load,通过Pig查询
Pig pig = (Pig)session.load(Pig.class, 1);---->(默认加type=P),通过Pig.class找到t_animal表,自动生成Pig对象
(2)采用load,通过Animal查询
Animal animal = (Animal)session.load(Animal.class, 1);
(3)采用load,通过Animal查询(开启懒加载)
Animal animal = (Animal)session.load(Animal.class, 1);
- 因为load默认支持lazy,因为我们看到的是Animal的代理对象,
- 不访问数据库,没发SQL语句(关闭懒加载就发SQL语句)
- 所以通过instanceof是反应不出真正的对象类型的
- 因此load在默认情况下是不支持多态查询的
if (animal instanceof Pig) {//animal所指对象的类型是Pig吗?-->此时所指类型是Cglib
System.out.println(animal.getName());
}else {
System.out.println("no");
}
(4)采用load,通过Animal查询,将<class>标签上的lazy=false(不开启懒加载)
Animal animal = (Animal)session.load(Animal.class, 1);
- 可以正确的判断出Pig的类型,因为lazy=false,返回的是具体的Pig类型
- 不开启懒加载时load支持多态查询,发SQL语句
if (animal instanceof Pig) {
System.out.println(animal.getName());
}else {
System.out.println("no");
}
(5)采用get,通过Animal查询,多态查询
- 可以正确的判断出Pig的类型,因为返回的是具体的Pig类型
- get支持多态查询
- 通过1查到id=1的记录,根据记录中的鉴别器的值来决定到底生成哪个子类对象
Animal animal = (Animal)session.get(Animal.class, 1);
if (animal instanceof Pig) {//animal所指对象的类型是Pig吗?
System.out.println(animal.getName());
}else {
System.out.println("no");
}
(6)采用Query
- 能够正确的鉴别出正真的类型,不管懒加载开启还是关闭,
- hql是支持多态查询的,发SQL语句
List animalList = session.createQuery("from Animal").list();
for (Iterator iter = animalList.iterator(); iter.hasNext();) {
Animal a = (Animal)iter.next();
if (a instanceof Pig) {
System.out.println("Pig");
}else if (a instanceof Bird) {
System.out.println("bird");
}
}
4、继承映射-----每个类对应一张表
image.png关联映射
<class name=“Animal” table=“t_animal”> t_animal表存放基本信息
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<property name="sex"/>
<joined-subclass name=“Pig” table=“t_pig”> t_pig表存放Pig子类增加的信息
<key column=“pid”/> pid即作为t_pig表的主键,也作为外键 参照t_animal表
<property name="weight"/>
</joined-subclass>
<joined-subclass name="Bird" table="t_bird"> t_bird表存放Bird子类增加信息
<key column=“bid”/> bid即作为t_bird表的主键,也作为外键 参照t_animal表
<property name="height"/>
</joined-subclass>
</class>
生成的SQL语句如下
create table t_animal (id integer not null auto_increment, name varchar(255), sex bit, primary key (id))
create table t_bird (bid integer not null, height integer, primary key (bid))
table t_bird add index FKCB5B05A48DB9DCE9 (bid), add constraint FKCB5B05A48DB9DCE9 foreign key (bid) references t_animal (id)
create table t_pig (pid integer not null, weight integer, primary key (pid))
table t_pig add index FK68F87438DBA1177 (pid), add constraint FK68F87438DBA1177 foreign key (pid) references t_animal (id)
缺点:效率低(需几个表关联查询)
5、每个子类映射成一张表
image.png理解如何映射
这种策略是使用union-subclass
标签来定义子类的。每个子类对应一张表,而且这个表的信息是完备的,即包含了所有从父类继承下来的属性映射的字段(这就是它跟joined-subclass的不同之处, joined-subclass定义的子类的表,只包含子类特有属性映射的字段)。
实现这种策略的时候,有如下步骤:
- 父类用普通<class>标签定义即可
- 子类用<union-subclass>标签定义,在定义union-subclass的时候,需要注意如下几点:
1、Union-subclass标签不再需要包含key标签(与joined-subclass不同)
2、Union-subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),也可以与class标签平行。 当Union-subclass标签的定义与class标签平行的时候,需要在Union-subclass标签中,添加extends属性,里面的值是父类的全路径名称。子类的其它属性,像普通类一样,定义在Union-subclass标签的内部。这个时候,虽然在union-subclass里面定义的只有子类的属性,但是因为它继承了父类,所以,不需要定义其它的属性,在映射到数据库表的时候,依然包含了父类的所有属性的映射字段。
注意:父类的主键生成策略不能是native和uuid,因为会被子类继承,会造成主键重复
关联映射
<class name="Animal" abstract="true">
<id name="id">
<generator class=“assigned”/>不能使用自增生成主键
</id>
<property name="name"/>
<property name="sex"/>
<union-subclass name="Pig" table="t_pig">
<property name="weight"/>
</union-subclass>
<union-subclass name="Bird" table="t_bird">
<property name="height"/>
</union-subclass>
</class>
缺点:不能使用自增生成主键
三种继承映射的比较
1、单表继承(只有一个表):表结构简单,数据出现冗余
2、每个类一个表:结构清晰,由于有主外键,造成效率低,维护困难
3、一个子类一个表,结构清晰,查找和存储效率高。最大的缺点是主键生成策略只能是assign
网友评论