继承映射

继承映射会打破对象的封装性,所以少用继承映射,多用关联映射

1、继承实现的三种策略

  1. 单表继承。每棵类继承树使用一个表(table per class hierarchy) ,推荐使用
  2. 具体表继承。每个类一个表(table per subclass)
  3. 类表继承。每个子类一个表(table per concrete class)(有一些限制)

2、 理解如何映射

因为类继承树肯定是对应多个类,要把多个类的信息存放在一张表中,必须有某种机制来区分哪些记录是属于哪个类的。这种机制就是,在表中添加一个字段,用这个字段的值来进行区分。
用hibernate实现这种策略的时候,有如下步骤:

  1. 父类用普通的标签定义
    在父类中定义一个discriminator(类鉴别器),即指定这个区分的字段的名称和类型
    如:
  2. 子类使用标签定义,在定义subclass的时候,需要注意如下几点:
    Subclass标签的name属性是子类的全路径名
    在Subclass标签中,用discriminator-value属性来标明本子类的discriminator字段(用来区分不同类的字段)的值
    Subclass标签,既可以被class标签所包含(这种包含关系正是表明了类之间的继承关系),也可以与class标签平行。 当subclass标签的定义与class标签平行的时候,需要在subclass标签中,添加extends属性,里面的值是父类的全路径名称。
    子类的其它属性,像普通类一样,定义在subclass标签的内部。

3、继承类-----每个类继承树映射成一张表

继承映射_第1张图片
image.png
Animal类
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映射文件





  把string类型变成varchar
   把boolean类型变成bit
  //往鉴别器字段加鉴别值






含义:
加个鉴别器字段,这个string类型是hibernate为我们字符串定义的类型,因为在java中找不到

注意:
鉴别器字段要放在主键之后,property属性之前,里加鉴别值

理解如何存储

存储的时候hibernate会自动将鉴别字段值插入到数据库中。
在加载数据的时候,hibernate能根据这个鉴别值正确的加载对象。
多态查询:在hibernate加载数据的时候能鉴别出正真的类型(instanceOf)
get支持多态查询
load只有在lazy=false,才支持多态查询
Hql 支持多态查询
缺点:数据冗余

理解如何映射

这种策略是使用union-subclass标签来定义子类的。每个子类对应一张表,而且这个表的信息是完备的,即包含了所有从父类继承下来的属性映射的字段(这就是它跟joined-subclass的不同之处,joined-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查询,将标签上的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、继承映射-----每个类对应一张表

继承映射_第2张图片
image.png
关联映射
 t_animal表存放基本信息





  t_pig表存放Pig子类增加的信息
 pid即作为t_pig表的主键,也作为外键 参照t_animal表


 t_bird表存放Bird子类增加信息
 bid即作为t_bird表的主键,也作为外键  参照t_animal表



生成的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、每个子类映射成一张表

继承映射_第3张图片
image.png
理解如何映射

这种策略是使用union-subclass标签来定义子类的。每个子类对应一张表,而且这个表的信息是完备的,即包含了所有从父类继承下来的属性映射的字段(这就是它跟joined-subclass的不同之处, joined-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,因为会被子类继承,会造成主键重复
关联映射


不能使用自增生成主键










缺点:不能使用自增生成主键

三种继承映射的比较

1、单表继承(只有一个表):表结构简单,数据出现冗余
2、每个类一个表:结构清晰,由于有主外键,造成效率低,维护困难
3、一个子类一个表,结构清晰,查找和存储效率高。最大的缺点是主键生成策略只能是assign

你可能感兴趣的:(继承映射)