hibernate(2)(二)(一对一,组合映射,继承映射 )

一对一映射:两种情况:

1.外键作为普通列并且添加唯一约束(有外键和唯一约束)
2.外键作为主键(有外键和主键约束)

1.外键,唯一的情况:

实例:

User.java

public class User {
    private int userId;
    private String userName;
    // 用户与身份证信息, 一对一关系
    private IdCard idCard;
    
View Code

User.hbm.xml(一对一主键做外键有点奇怪,不用指定外键列名)

<?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.c_one2one">    
    <class name="User" table="t_user">
        <id name="userId">
            <generator class="native"></generator>
        </id>    
        <property name="userName" length="20"></property>
        <!-- 
            一对一映射: 没有外键方
         -->
         <one-to-one name="idCard" class="IdCard"></one-to-one>             
    </class>    
</hibernate-mapping>

IdCard.java

// 身份证
public class IdCard {
    // 身份证号(主键)
    private String cardNum;// 对象唯一表示(Object Identified, OID)
    private String place; //  身份证地址
    // 身份证与用户,一对一的关系
    private User user;
View Code

IdCard.hbm.xml(将一个属性映射成外键字段只能用many-to-one(再加上唯一约束,就是一对一)

<?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.c_one2one">
    
    <class name="IdCard" table="t_IdCard">
        <id name="cardNum">
            <generator class="assigned"></generator>
        </id>    
        <property name="place" length="20"></property>
        <!-- 
            一对一映射,有外键方
            unique="true"   给外键字段添加唯一约束
         -->
         <many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one>            
    </class>    
</hibernate-mapping>

测试类:

public class App {    
    private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(IdCard.class)   
            .addClass(User.class)   // 测试时候使用
            .buildSessionFactory();
    }
    @Test
    public void getSave() {        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 用户
        User user = new User();
        user.setUserName("Jack");
        // 身份证
        IdCard idCard = new IdCard();
        idCard.setCardNum("441202XXX");
        idCard.setPlace("广州XXX");
        // 关系(这样设置关联关系的原因是,配置了级联在IdCard这边)
        idCard.setUser(user);
        
        // ----保存----保存idCard的原因是级联配置在该类
        session.save(idCard);        
        session.getTransaction().commit();
        session.close();        
    }

2.外键做主键:

User.java

// 用户
public class User {
    private int userId;
    private String userName;
    // 用户与身份证信息, 一对一关系
    private IdCard idCard;
View Code

User.hbm.xml

<!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.c_one2one2">    
    <class name="User" table="t_user">
        <id name="userId">
            <generator class="native"></generator>
        </id>    
        <property name="userName" length="20"></property>
        <!-- 
            一对一映射: 没有外键方
         -->
         <one-to-one name="idCard" class="IdCard"></one-to-one>             
    </class>    
</hibernate-mapping>

IdCard.java

// 身份证
public class IdCard {
    private int user_id;
    // 身份证号
    private String cardNum;
    private String place; //  身份证地址
    // 身份证与用户,一对一的关系
    private User user;
View Code

IdCard.hbm.xml(主键和属性对应的对象是关联的)

<?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.c_one2one2">
    
    <class name="IdCard" table="t_IdCard">
        <id name="user_id">
            <!-- 
                id 节点指定的是主键映射, 即user_id是主键
                   主键生成方式: foreign  即把别的表的主键作为当前表的主键;
                   property (关键字不能修改)指定引用的对象,与下面user对应
对象的全名 cn..User、 对象映射 cn.User.hbm.xml、 table(id)
--> <generator class="foreign"> <param name="property">user</param> </generator> </id> <property name="cardNum" length="20"></property> <property name="place" length="20"></property> <!--
上面那个user和下面这个user属性值必须u是对应的 一对一映射,有外键方 (基于主键的映射) constrained="true" 指定在主键上添加外键约束
--> <one-to-one name="user" class="User" constrained="true" cascade="save-update"></one-to-one> </class> </hibernate-mapping>

测试:

private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(IdCard.class)   
            .addClass(User.class)   // 测试时候使用
            .buildSessionFactory();
    }
    @Test
    public void getSave() {        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 用户
        User user = new User();
        user.setUserName("Jack");
        // 身份证
        IdCard idCard = new IdCard();
        idCard.setCardNum("441202XXX");
        idCard.setPlace("广州XXX");
        // 关系
        idCard.setUser(user);
        
        // ----保存----
        session.save(idCard);        
        session.getTransaction().commit();
        session.close();

 

组件映射与继承映射

类的关系

         组合关系(更像是包含关系好不)
                   一个类中包含了另外一个类。这2个类中就是组合关系。
                   需求: 汽车与车轮
         继承关系
                   一个类继承另外一个类。这2个类中就是继承关系。
                   需求:动物
                         猫
                         猴

组件映射:

类组合关系的映射,也叫做组件映射!:组件类和被包含的组件类,共同映射到一张表!

需求: 汽车与车轮

数据库:

T_car:主键id   汽车名称  轮子大小  个数

有时候只整个对象的一部,可以通过组合对象的方式,拼凑一个对象映射到一张表里面:

实例:

Car.java(省略get/set)

public class Car {
    private int id;
    private String name;
    // 车轮
    private Wheel wheel;

Wheel.java(省略get/set)

// 车轮
public class Wheel {
    private int count;
    private int size;

Car.hbm.xml

<?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.d_component">    
    <class name="Car" table="t_car">
        <id name="id">
            <generator class="native"></generator>
        </id>    
        <property name="name" length="20"></property>        
        <!-- 组件映射 -->
        <component name="wheel">
            <property name="size"></property>
            <property name="count"></property>
        </component>                             
    </class>    
</hibernate-mapping>

测试类:

    private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Car.class)   
            .buildSessionFactory();
    }
    @Test
    public void getSave() {        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 轮子
        Wheel wheel = new Wheel();
        wheel.setSize(38);
        wheel.setCount(4);
        // 汽车
        Car car = new Car();
        car.setName("BMW");
        car.setWheel(wheel);
        
        // 保存
        session.save(car);        
        session.getTransaction().commit();
        session.close();        
    }

简单继承映射:

特点:简单继承映射,有多少个子类,写多少个映射文件!

Animal.java(省略get/set)

// 动物类
public abstract class Animal {
    private int id;
    private String name;

Cat.java(省略get/set)

public class Cat extends Animal{
    // 抓老鼠
    private String catchMouse;

Cat.hbm.xml

<?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_extends1">    
    <class name="Cat" table="t_Cat">
        <!-- 简单继承映射: 父类属性直接写 -->
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="name"></property>   
<property name="catchMouse"></property> </class> </hibernate-mapping>

测试类:

public class App {    
    private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Cat.class)   
            .buildSessionFactory();
    }
    @Test
    public void getSave() {        
        Session session = sf.openSession();
        session.beginTransaction();        
        // 保存
//        Cat cat = new Cat();
//        cat.setName("大花猫");
//        cat.setCatchMouse("抓小老鼠");
//        session.save(cat);
        
        // 获取时候注意:当写hql查询的使用,通过父类查询必须写上类的全名;如果是子类Cat可以不用全名
        Query q = session.createQuery("from cn.itcast.e_extends1.Animal");
        List<Animal> list = q.list();
        System.out.println(list);        
        session.getTransaction().commit();
        session.close();        
    }

数据库效果:

继承映射多种情况:

需求:猫、猴子、动物

1.所有子类映射到一张表 (1张表):

什么情况用?

    子类教多,且子类较为简单,即只有个别属性!
    好处:因为使用一个映射文件, 减少了映射文件的个数。
    缺点:(不符合数据库设计原则:每条记录都有空字段值)

数据库(通过type_字段来指明具体类型的数据)

T_animal (要存储所有的子类信息)                              “鉴别器”
        Id   name     catchMouse      eatBanana          type_(区别是哪个子类)
         1   大马猴       NULL        吃10个香蕉               猴子
         2   大花猫     不抓老鼠         NULL                   猫

实例:(都是省略get/set)

Animal.java

// 动物类
public abstract class Animal {
    private int id;
    private String name;
View Code

Cat.java

public class Cat extends Animal{
    // 抓老鼠
    private String catchMouse;
View Code

Monkey.java

public class Monkey extends Animal {
    
    // 吃香蕉
    private String eatBanana;
View Code

Animal.hbm.xml

<?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>

测试类:

public class App {    
    private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Animal.class)   
            .buildSessionFactory();
    }
    @Test
    public void getSave() {
        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 保存
        Cat cat = new Cat();
        cat.setName("大花猫");
        cat.setCatchMouse("抓小老鼠");
        
        Monkey m = new Monkey();
        m.setName("猴子");
        m.setEatBanana("吃10个香蕉");
        
        // 保存
        session.save(cat);
        session.save(m);
        
        session.getTransaction().commit();
        session.close();        

数据库效果:

总结:

  写法较为简单:所有子类用一个映射文件,且映射到一张表!
  但数据库设计不合理!
 (不推荐用。)

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

实例(省略get/set)

Animal.java

// 动物类
public abstract class Animal {
    private int id;
    private String name;
View Code

Cat.java

public class Cat extends Animal{
    // 抓老鼠
    private String catchMouse;
View Code

Monkey.java

public class Monkey extends Animal {    
    // 吃香蕉
    private String eatBanana;
View Code

Animal.hbm.xml

<?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_extends3">    
    <class name="Animal" table="t_animal">
        <id name="id">
            <generator class="native"></generator>
        </id>
        <property name="name"></property>
        
        <!-- 
            子类:猫  t_cat
            key 指定_cat表的外键字段
        -->
        <joined-subclass name="Cat" table="t_cat">
            <key column="t_animal_id"></key>
            <property name="catchMouse"></property>
        </joined-subclass>
        
        <!-- 子类:猴子  t_monkey -->
        <joined-subclass name="Monkey" table="t_monkey">
            <key column="t_animal_id"></key>
            <property name="eatBanana"></property>
        </joined-subclass>        
    </class>    
</hibernate-mapping>

测试类:

public class App {
private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Animal.class)   
            .buildSessionFactory();
    }
    @Test
    public void getSave() {        
        Session session = sf.openSession();
        session.beginTransaction();        
        // 保存
        Cat cat = new Cat();
        cat.setName("大花猫");
        cat.setCatchMouse("抓小老鼠");
        
        Monkey m = new Monkey();
        m.setName("猴子");
        m.setEatBanana("吃10个香蕉");
        
        // 保存
        session.save(cat);
        session.save(m);        
        session.getTransaction().commit();
        session.close();        
    }

数据库效果:对应三个表:T_anmal (存储父类信息),T_cat (引用父类的主键),T_monkey (引用父类的主键)

总结:

一个映射文件,配置所有的子类; 子类父类都对应表;

缺点:
表结构比较复杂,插入一条子类信息,需要用2条sql: 往父类插入、往子类插入!

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

1.同union-subclass 组合的配置映射表,特别注意表的主键不能是自增长,可以用uuid
2.默认父类也是生成表的,需要设置abstract=true

数据库:

T_cat :  Id    name   catchMounse
T_monkey:Id    name   eatBanana

实例:

Animal.java

public class Animal {
    private String id;
    private String name;
    
View Code

Cat.java

public class Cat extends Animal{
    // 抓老鼠
    private String catchMouse;
View Code

Monkey.java

public class Monkey extends Animal {    
    // 吃香蕉
    private String eatBanana;
View Code

Animal.hbm.xml

<?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_extends4">
    <!-- 
         abstract="true"  指定实体类对象不对应表,即在数据库段不生成表
     -->
    <class name="Animal" abstract="true">
        <!-- 如果用union-subclass节点,主键生成策略不能为自增长! -->
        <id name="id">
            <generator class="uuid"></generator>
        </id>
        <property name="name"></property>
        
        <!-- 
            子类:猫  t_cat
            union-subclass  
                table 指定为表名, 表的主键即为id列
        -->
        <union-subclass name="Cat" table="t_cat">
            <property name="catchMouse"></property>
        </union-subclass>
        
        <!-- 子类:猴子  t_monkey -->
        <union-subclass name="Monkey" table="t_monkey">
            <property name="eatBanana"></property>
        </union-subclass>        
    </class>    
</hibernate-mapping>

测试类:

private static SessionFactory sf;
    static {
        sf = new Configuration()
            .configure()
            .addClass(Animal.class)   
            .buildSessionFactory();
    }
    @Test
    public void getSave() {        
        Session session = sf.openSession();
        session.beginTransaction();
        
        // 保存
        Cat cat = new Cat();
        cat.setName("大花猫");
        cat.setCatchMouse("抓小老鼠");
        
        Monkey m = new Monkey();
        m.setName("猴子");
        m.setEatBanana("吃10个香蕉");
        
        // 保存
        session.save(cat);
        session.save(m);        
        session.getTransaction().commit();
        session.close();        
    }

总结:

所有的子类都写到一个映射文件;
父类不对应表; 每个子类对应一张表

 

posted @ 2017-03-26 18:26  假程序猿  阅读(187)  评论(0)    收藏  举报