美文网首页
继承映射

继承映射

作者: Mango_lxh | 来源:发表于2018-09-10 15:03 被阅读0次

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

    1、继承实现的三种策略

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

    2、 理解如何映射

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

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

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

    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映射文件
    <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

    相关文章

      网友评论

          本文标题:继承映射

          本文链接:https://www.haomeiwen.com/subject/laesgftx.html