zoukankan      html  css  js  c++  java
  • Hibernate第九篇【组件映射、继承映射】

    前言

    到目前位置,我们已经学习了一对一、一对多、多对一、多对多映射了…既然Hibernate是ORM实现的框架,它还提供了组件映射和继承映射..本博文主要讲解组件映射和继承映射

    Java主要的类主要有两种方式

    • 组合关系,组合关系对应的就是组件映射
    • 继承关系,继承关系对应的就是继承映射

    组件映射

    组件映射实际上就是将组合关系的数据映射成一张表,组件类和被包含的组件类映射成一张表

    有的时候,两个类的关系明显不是继承关系,但两个类的亲密程度很高,在一个类里边需要用到另外一个类…那么就在类中定义一个变量来维护另一个类的关系,这种就叫组合关系!

    需求:汽车和轮子。汽车需要用到轮子,但是轮子的爸爸不可能是汽车吧?

    设计数据库

    这里写图片描述

    设计实体

    Wheel.java

    
    public class Wheel {
    
        private int  count;
        private int size;
    
        public int getCount() {
            return count;
        }
    
        public void setCount(int count) {
            this.count = count;
        }
    
        public int getSize() {
            return size;
        }
    
        public void setSize(int size) {
            this.size = size;
        }
    }
    
    

    Car.java,使用变量维护Wheel

    package zhongfucheng.aa;
    
    /**
     * Created by ozc on 2017/5/7.
     */
    public class Car {
    
        private int id;
        private String name;
        private Wheel wheel;
    
        public Wheel getWheel() {
            return wheel;
        }
    
        public void setWheel(Wheel wheel) {
            this.wheel = wheel;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    }
    

    映射表

    使用了一个新标签<component>,组件映射标签。

    
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
    <hibernate-mapping package="zhongfucheng.aa" >
    
        <class name="Car" table="Car" >
    
            <!--映射主键-->
            <id name="id" column="id">
                <generator class="native"></generator>
            </id>
    
            <!--映射普通字段-->
            <property name="name" column="name" ></property>
    
    
            <!--
                映射组件字段
            -->
            <component name="wheel">
                <property name="count"></property>
                <property name="size"></property>
            </component>
    
        </class>
    
    </hibernate-mapping>
    

    测试

    package zhongfucheng.aa;
    
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    
    /**
     * Created by ozc on 2017/5/6.
     */
    public class App5 {
        public static void main(String[] args) {
    
    
            //创建对象
            Wheel wheel = new Wheel();
            Car car = new Car();
    
            //设置属性
            wheel.setCount(43);
            wheel.setSize(22);
            car.setName("宝马");
    
            //维护关系
            car.setWheel(wheel);
            //获取加载配置管理类
            Configuration configuration = new Configuration();
    
    
            configuration.configure().addClass(Car.class);
    
            //创建Session工厂对象
            SessionFactory factory = configuration.buildSessionFactory();
    
            //得到Session对象
            Session session = factory.openSession();
    
            //使用Hibernate操作数据库,都要开启事务,得到事务对象
            Transaction transaction = session.getTransaction();
    
            //开启事务
            transaction.begin();
    
            session.save(car);
    
            //提交事务
            transaction.commit();
    
            //关闭Session
            session.close();
    
    
        }
    }

    这里写图片描述

    传统方式继承

    需求:动物、猫、猴子。猫继承着动物

    传统方式继承的特点就是:有多少个子类就写多少个配置文件.

    表结构

    我们的表应该是这样的:id和name从Animal中继承,catchMouse是子类的具体行为。

    这里写图片描述

    实体

    Animal.java

    
    package zhongfucheng.aa;
    
    // 动物类
    public abstract class Animal {
    
        private int id;
        private String name;
    
    
        public int getId() {
            return id;
        }
        public void setId(int id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    
    }
    

    Cat.java继承着Animail

    
    package zhongfucheng.aa;
    
    public class Cat extends Animal{
    
        // 抓老鼠
        private String catchMouse;
    
        public String getCatchMouse() {
            return catchMouse;
        }
    
        public void setCatchMouse(String catchMouse) {
            this.catchMouse = catchMouse;
        }
    }

    映射文件

    简单继承的映射文件很好写,在属性上,直接写父类的属性就可以了。

    但是也有致命的缺点:如果子类有很多,就需要写很多的配置文件

    
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
    <hibernate-mapping package="zhongfucheng.aa" >
    
        <class name="Cat" table="cat" >
    
            <!--映射主键-->
            <id name="id" column="id">
                <generator class="native"></generator>
            </id>
    
            <!--
                映射普通字段
                父类的属性直接引用就行了,比如name属性,直接写就行了!
            -->
            <property name="name" column="name" ></property>
            <property name="catchMouse" column="catchMouse" ></property>
    
    
        </class>
    
    </hibernate-mapping>
    

    测试

    
    package zhongfucheng.aa;
    
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    
    public class App5 {
        public static void main(String[] args) {
    
    
            //创建对象
            Cat cat = new Cat();
    
            //设置属性
    
            cat.setName("大花猫");
            cat.setCatchMouse("捉大老鼠");
    
            //获取加载配置管理类
            Configuration configuration = new Configuration();
    
    
            configuration.configure().addClass(Cat.class);
    
            //创建Session工厂对象
            SessionFactory factory = configuration.buildSessionFactory();
    
            //得到Session对象
            Session session = factory.openSession();
    
            //使用Hibernate操作数据库,都要开启事务,得到事务对象
            Transaction transaction = session.getTransaction();
    
            //开启事务
            transaction.begin();
    
            session.save(cat);
    
            //如果取数据时候Animal父类接收的话,需要给出Anmail的全名
    
    
            //提交事务
            transaction.commit();
    
            //关闭Session
            session.close();
    
    
        }
    }

    这里写图片描述


    把所有子类映射成一张表

    前面我们采用的是:每个子类都需要写成一个配置文件,映射成一张表

    如果子类的结构很简单,只比父类多几个属性。就像上面的例子…我们可以将所有的子类都映射成一张表中

    但是呢,这样是不符合数据库设计规范的…..因为表中的数据可能是猫,可能是猴子…这明显是不合适的…

    由于表中可能存在猫,存在猴子,为了区分是什么类型的。我们需要使用鉴别器

    我们了解一下…

    数据表

    这里写图片描述

    实体

    实体和上面雷同,只多了一个猴子的实体表

    Monkey.java

    
    public class Monkey extends Animal {
    
        // 吃香蕉
        private String eatBanana;
    
        public String getEatBanana() {
            return eatBanana;
        }
    
        public void setEatBanana(String eatBanana) {
            this.eatBanana = eatBanana;
        }
    
    }
    
    

    映射文件

    使用了subClass这个节点和鉴别器

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
    <!-- 
        继承映射, 所有的子类都映射到一张表
     -->
    <hibernate-mapping package="cn.itcast.e_extends2">
    
        <class name="Animal" table="t_animal">
            <id name="id">
                <generator class="native"></generator>
            </id>
            <!-- 指定鉴别器字段(区分不同的子类) -->
            <discriminator column="type_"></discriminator>
    
            <property name="name"></property>
    
            <!-- 
                子类:猫
                    每个子类都用subclass节点映射
                    注意:一定要指定鉴别器字段,否则报错!
                    鉴别器字段:作用是在数据库中区别每一个子类的信息, 就是一个列
                discriminator-value="cat_"
                    指定鉴别器字段,即type_字段的值
                    如果不指定,默认为当前子类的全名
             -->
             <subclass name="Cat" discriminator-value="cat_">
                <property name="catchMouse"></property>
             </subclass>
    
             <!-- 
                子类:猴子
              -->
              <subclass name="Monkey" discriminator-value="monkey_">
                <property name="eatBanana"></property>
              </subclass>
    
        </class>
    
    
    </hibernate-mapping>
    

    测试

    加载的是Animal父类的映射文件。保存的是cat和monkey。

    
    package zhongfucheng.aa;
    
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    
    public class App5 {
        public static void main(String[] args) {
    
    
            //创建对象
            Cat cat = new Cat();
            Monkey monkey = new Monkey();
    
            //设置属性
            cat.setName("大花猫");
            cat.setCatchMouse("小老鼠");
            monkey.setEatBanana("吃香蕉");
            monkey.setName("大猴子");
    
            //获取加载配置管理类
            Configuration configuration = new Configuration();
    
            //加载Animal的映射文件!
            configuration.configure().addClass(Animal.class);
    
            //创建Session工厂对象
            SessionFactory factory = configuration.buildSessionFactory();
    
            //得到Session对象
            Session session = factory.openSession();
    
            //使用Hibernate操作数据库,都要开启事务,得到事务对象
            Transaction transaction = session.getTransaction();
    
            //开启事务
            transaction.begin();
    
            session.save(cat);
            session.save(monkey);
    
            //提交事务
            transaction.commit();
    
            //关闭Session
            session.close();
    
    
        }
    }

    这里写图片描述


    每个类映射一张表(3张表)

    父类和子类都各对应一张表。那么就有三张表了

    这种结构看起来是完全面向对象,但是表之间的结构会很复杂,插入一条子类的信息,需要两条SQL

    数据表设计

    这里写图片描述

    映射文件

    使用到了<joined-subclass >这个节点

    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
    
    <hibernate-mapping package="zhongfucheng.aa">
    
        <class name="Animal" table="t_animal">
            <id name="id">
                <generator class="native"></generator>
            </id>
    
            <property name="name"></property>
    
    
            <!--
                Animal下的子类映射成一张表
                    指定子类的类型,对应的表
                    指定子类的外键字段【需要对应Animal】
                    指定子类的普通属性
            -->
            <joined-subclass name="Cat" table="cat_">
                <!--key对应的是外键字段-->
                <key column="animal_id"></key>
                <property name="catchMouse"></property>
            </joined-subclass>
    
            <joined-subclass name="Monkey" table="monkey_">
                <!--key对应的是外键字段-->
                <key column="animal_id"></key>
                <property name="eatBanana"></property>
            </joined-subclass>
    
    
        </class>
    
    
    </hibernate-mapping>
    
    
    

    测试

    package zhongfucheng.aa;
    
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    
    public class App5 {
        public static void main(String[] args) {
    
    
            //创建对象
            Cat cat = new Cat();
            Monkey monkey = new Monkey();
    
            //设置属性
            cat.setName("大花猫");
            cat.setCatchMouse("小老鼠");
            monkey.setEatBanana("吃香蕉");
            monkey.setName("大猴子");
    
            //获取加载配置管理类
            Configuration configuration = new Configuration();
    
            //加载类对应的映射文件!
            configuration.configure().addClass(Animal.class);
    
            //创建Session工厂对象
            SessionFactory factory = configuration.buildSessionFactory();
    
            //得到Session对象
            Session session = factory.openSession();
    
            //使用Hibernate操作数据库,都要开启事务,得到事务对象
            Transaction transaction = session.getTransaction();
    
            //开启事务
            transaction.begin();
    
            session.save(cat);
            session.save(monkey);
    
            //提交事务
            transaction.commit();
    
            //关闭Session
            session.close();
    
    
        }
    }

    每保存一个子类对象需要两条SQL语句!

    这里写图片描述


    (推荐)每个子类映射一张表, 父类不对应表(2张表)

    • 使用过了一张表保存所有子类的数据,这不符合数据库设计规范
    • 每个子类、父类都拥有一张表..表结构太过于繁琐..添加信息时,过多的SQL

    我们即将使用的是:每个子类映射成一张表,父类不对应表…这和我们传统方式继承是一样的。只不过在hbm.xml文件中使用了<union-subclass>这个节点,由于有了这个节点,我们就不需要每个子类都写一个配置文件了。

    数据库表设计

    这里写图片描述

    映射文件

    • 想要父类不映射成数据库表,只要在class中配置为abstract即可
    • 使用了union-subclass节点,主键就不能采用自动增长策略了。我们改成UUID即可。当然啦,对应的实体id类型要改成String
    
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    
    
    <hibernate-mapping package="zhongfucheng.aa">
    
    
        <!--
            想要父类不映射成表,设置成abstract
        -->
        <class name="Animal" abstract="true">
    
            <!--
                如果使用了union-subclass节点,那么主键生成策略不能是自增长,我们改成uuid即可
            -->
            <id name="id">
                <generator class="uuid"></generator>
            </id>
    
            <property name="name"></property>
    
    
            <!--
                将子类的信息都映射成一张表
                    给出属性的名称
                    属性对应的数据库表
                    普通字段
            -->
            <union-subclass name="Cat" table="cat_">
                <property name="catchMouse"></property>
            </union-subclass>
    
            <union-subclass name="Monkey" table="monkey_">
                <property name="eatBanana"></property>
            </union-subclass>
    
    
        </class>
    
    
    </hibernate-mapping>
    

    测试

    
    package zhongfucheng.aa;
    
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.classic.Session;
    
    public class App5 {
        public static void main(String[] args) {
    
    
            //创建对象
            Cat cat = new Cat();
            Monkey monkey = new Monkey();
    
            //设置属性
            cat.setName("大花猫");
            cat.setCatchMouse("小老鼠");
            monkey.setEatBanana("吃香蕉");
            monkey.setName("大猴子");
    
            //获取加载配置管理类
            Configuration configuration = new Configuration();
    
            //加载类对应的映射文件!
            configuration.configure().addClass(Animal.class);
    
            //创建Session工厂对象
            SessionFactory factory = configuration.buildSessionFactory();
    
            //得到Session对象
            Session session = factory.openSession();
    
            //使用Hibernate操作数据库,都要开启事务,得到事务对象
            Transaction transaction = session.getTransaction();
    
            //开启事务
            transaction.begin();
    
            session.save(cat);
            session.save(monkey);
    
            //提交事务
            transaction.commit();
    
            //关闭Session
            session.close();
    
    
        }
    }

    这里写图片描述

    总结

    由于我们的传统继承映射每个子类都对应一个配置文件,这样十分麻烦。因此.hbm.xml就给出了几个节点供我们使用,分别有以下的情况:

    • 子类父类共有一张表subclass
      • 不符合数据库设计规范
      • 需要使用鉴别器
    • 子类、父类都有自己的表joined-subclass,那么就是三张表
      • 表的结构太过繁琐
      • 插入数据时要生成SQL至少就要两条
    • 子类拥有自己的表、父类不对应表【推荐】union-subclass
      • 父类不对应表要使用abstract来修饰
      • 主键的id不能使用自增长策略,修改成UUID就好了。对应的JavaBean的id设置成String就好

  • 相关阅读:
    BZOJ 3506 机械排序臂 splay
    BZOJ 2843 LCT
    BZOJ 3669 魔法森林
    BZOJ 2049 LCT
    BZOJ 3223 文艺平衡树 splay
    BZOJ 1433 假期的宿舍 二分图匹配
    BZOJ 1051 受欢迎的牛 强连通块
    BZOJ 1503 郁闷的出纳员 treap
    BZOJ 1096 ZJOI2007 仓库设计 斜率优化dp
    BZOJ 1396: 识别子串( 后缀数组 + 线段树 )
  • 原文地址:https://www.cnblogs.com/zhong-fucheng/p/7202961.html
Copyright © 2011-2022 走看看