手记

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就好

0人推荐
随时随地看视频
慕课网APP