hibernate映射学习(三)

在前面几篇文章中,分别学习了hibernate的关联映射,基本的一对一,一对多,多对
多等都有学习,今天我会给大家带来hibernate中关于"组合映射"和"继承映射"的学习。

## 组合映射##

为什么要学习组合映射,它和一般的映射有什么区别吗??我们先来看一下下面这种情况:

上图,可以看出,user包含了username,address包含了homeaddress,contact包含了qq,phone,这些字段,由于这些属性没有什么必然联系,我们可以将这些字段放入到一个Tuser表里,因为这样的话,对于数据库的维护,已经性能都是有很大的提升。我们可以将Address当做User的属性,将Contact当做Address的属性。这样将所有的属性,只映射到一个Tuser表里,就是组合映射。下面分别来看这三个实体类以及怎么根据这些实体类实现组合映射

Contact.java

public class Contact implements Serializable{
    private String qq;
    private String phone;
    public String getQq() {
        return qq;
    }
    public void setQq(String qq) {
        this.qq = qq;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public Contact(String qq, String phone) {
        super();
        this.qq = qq;
        this.phone = phone;
    }
    public Contact() {
        super();
    }
}

Address.java

public class Address implements Serializable {
    private String homeAddress;
    private Contact contact = null;

    public Contact getContact() {
        return contact;
    }

    public void setContact(Contact contact) {
        this.contact = contact;
    }

    public String getHomeAddress() {
        return homeAddress;
    }

    public void setHomeAddress(String homeAddress) {
        this.homeAddress = homeAddress;
    }

    public Address(String homeAddress, Contact contact) {
        super();
        this.homeAddress = homeAddress;
        this.contact = contact;
    }

    public Address() {
        super();
    }
}

UserInfo.java

package com.mydb.entity;

import java.io.Serializable;

public class UserInfo implements Serializable {
    private int userId;
    private String userName;
    private String userPass;
    private Address address = null;

    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getUserPass() {
        return userPass;
    }
    public void setUserPass(String userPass) {
        this.userPass = userPass;
    }
    @Override
    public String toString() {
        return "UserInfo [userId=" + userId + ", userName=" + userName
                + ", userPass=" + userPass + "]";
    }
}

可以看到这里,我是将contact变成Address的属性,再将address变成User的属性。此时,UserInfo对应的映射文件内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <!-- name="全类名" table="表名" -->
    <class name="com.mydb.entity.UserInfo" table="userinfo">
        <!-- name="属性名" column="主键"-->
        <id name="userId" column="uid">
            <!-- 主键的生成策略:native(主键自增),assigned(指派) -->
            <generator class="increment"></generator>
        </id>
        <component name="address">
            <property name="homeAddress"></property>
            <component name="contact">
                <property name="phone"></property>
                <property name="qq"></property>
            </component>
        </component>
        <!-- name="属性名" column="字段名" -->
        <property name="userName" column="uname"></property>
        <property name="userPass" column="upass"></property>
    </class>
</hibernate-mapping>    

可以看到映射到的userinfo表的主键是自增的,对于组合映射,将address映射到user中,将contact映射到address中,是这样做的:

<component name="address">
    <property name="homeAddress"></property>
    <component name="contact">
        <property name="phone"></property>
        <property name="qq"></property>
    </component>
</component>

我们通过component标签来实现组合映射。接下来编写测试类:

//解析hibernate.cfg.xml文件
        Configuration cfg = new Configuration().configure();
        //创建SessionFactory(创建连接池)
        SessionFactory factory = cfg.buildSessionFactory();
        //创建session
        Session session = factory.openSession();

        UserInfo userInfo = new UserInfo();
        userInfo.setUserName("gaga");
        userInfo.setUserPass("123456");
        userInfo.setAddress(new Address("深圳",new Contact("456754433","12367544554")));
        //创建以及开启事物对象
        Transaction transaction = null;
        try {
            transaction = session.beginTransaction();
            session.save(userInfo);
            transaction.commit();
        } catch (HibernateException e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }

这里,我存储了一个userinfo到映射的userinfo表里,此时看下userinfo表都有哪些字段:

此时我们就将address类和contact以及userinfo类中的属性都映射到一个表里边了。

继承映射

为什么要有继承映射,首先我们咋面向对象中,经常会用到继承这种方式来实现代码复用,那么如果是一个实体类,有继承关系,但是在数据库表中是没有这样的继承关系的,在数据库当中,只有关联,并且继承是有多态的,那么当我们从数据库当中,查找出来的数据,如何确保封装成正确的格式呢??由此继承映射就应运而生吧。

下面看下我们的实体类:

Animal.java

public class Animal implements Serializable {
    private int animalId;
    public int getAnimalId() {
        return animalId;
    }

    public void setAnimalId(int animalId) {
        this.animalId = animalId;
    }

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Pig.java

public class Pig extends Animal implements Serializable {
    private int weight;//重量

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }
}   

Dog.java

public class Dog extends Animal implements Serializable {
    private String dark;

    public String getDark() {
        return dark;
    }

    public void setDark(String dark) {
        this.dark = dark;
    }

}

可以看到dog和pig这两个类,有一个共同的属性name,又各自有自己的属性。映射文件如下:

<hibernate-mapping package="com.mydb.entity.jicheng">
    <!-- name="全类名" table="表名" -->
    <class name="Animal" table="t_animal">
        <!-- name="属性名" column="主键"-->
        <id name="animalId">
            <!-- 主键的生成策略:native(主键自增),assigned(指派) -->
            <generator class="increment"></generator>
        </id>
        <discriminator column="discriminator" type="string"></discriminator>
        <property name="name"></property>
        <subclass name="Dog" discriminator-value="D">
            <property name="dark"></property>
        </subclass>
        <subclass name="Pig" discriminator-value="P">
            <property name="weight"></property>
        </subclass>
    </class>
</hibernate-mapping>    

说明一下,subclass标签指定继承自animal的子类,discriminator是用来正确封装我们查询的数据到指定子类,discriminator是在animal表中增加一个列,用来存储每一行是pig类型还是dog类型的数据。可以看到如果是Dog类型的数据,则该列的值是”D”,如果是pig类型的数据,那么该列的值是”P”.

编写测试类:

//解析hibernate.cfg.xml文件
        Configuration cfg = new Configuration().configure();
        //创建SessionFactory(创建连接池)
        SessionFactory factory = cfg.buildSessionFactory();
        //创建session
        Session session = factory.openSession();

        Dog dog = new Dog();
        dog.setName("哈士奇");
        dog.setDark("wangwang");

        Pig pig = new Pig();
        pig.setName("胖猪");
        pig.setWeight(300);
        //创建以及开启事物对象
        Transaction transaction = null;
        try {
            transaction = session.beginTransaction();
            session.save(pig);
            session.save(dog);
            transaction.commit();
        } catch (HibernateException e) {
            if (transaction != null) {
                transaction.rollback();
            }
            e.printStackTrace();
        } finally {
            if (session != null) {
                session.close();
            }
        }

上面的测试方法分别给t_animal表中存储了一个Dog和Pig这两个类型的数据,下面看看数据库的内容:

可以看到在t_animal表格当中已经增加了一列discirminator,用来区分该类存储的类型的具体子类。

此时当我再次通过session查询的时候,就会查询出正确的类型,可以看到表格里边id=1的行存储了一个Pig类型的数据:

Pig pig = (Pig) session.get(Animal.class,1);
System.out.println(pig.getName());

此时类型将会正确的转换,但是如果此时强制将Pig转换成Dog类型,hibernate将会抛出下面的异常:

基于接口的隐式多态查询

hibernate同样提供查询实现某一个接口的所有具体的实体类,我这里新建一个接口:

public interface AnimalInterface {

}

然后让Pig和Dog这两个类同时实现该接口,接下来利用hql来实现基于接口的隐式多态查询:

Session session = factory.openSession();
        String hql = "from com.mydb.entity.jicheng.AnimalInterface";
        Query query =  session.createQuery(hql);
        List list = query.list();
        for (Object object : list) {
            if (object instanceof Dog) {
                System.out.println(((Dog) object).getName()+"叫声:"+((Dog) object).getDark());
            } else if (object instanceof Pig) {
                System.out.println(((Pig) object).getName()+"体重是:"+((Pig) object).getWeight());
            }
        }

继承映射(每个子类一张表)

接下来给大家介绍第二种继承映射,上面继承映射的实现方式是将所有的子类放入到一张表中来存储,然后通过discriminator标签来实现多台查询时候的正确转型,下面带大家实现第二种继承映射即:每一个子类映射一张表,同样是animal,dog,pig这三个类,只需要这样配置映射文件即可:

<hibernate-mapping package="com.mydb.entity.jicheng">
    <!-- name="全类名" table="表名" -->
    <class name="Animal" table="t_animal">
        <!-- name="属性名" column="主键" -->
        <id name="animalId">
            <!-- 主键的生成策略:native(主键自增),assigned(指派) -->
            <generator class="increment"></generator>
        </id>
        <joined-subclass name="Dog" table="t_dog">
            <key column="aid"></key>
            <property name="dark"></property>
        </joined-subclass>
        <joined-subclass name="Pig" table="t_pig">
            <key column="aid"></key>
            <property name="weight"></property>
        </joined-subclass>
    </class>
</hibernate-mapping>    

joined-subclass标签,name属性填写子类的类名,table填写子类映射到的表名称,<key column="aid"></key>表示关联animal表的外键。此时会在数据库中生成”t_animal”,”t_dog”,”t_pig”这三个表:

t_animal

t_dog

t_pig

hibernate中set集合映射

在之前一对一,和多对多的映射中,我们利用set集合来映射,但是如果是基本的类型,应该怎么办呢??我如我一个User有很多张图片:比如

我现在给userinfo中增加一个

private Set<String>photoes = new HashSet();

并且设置get和set方法,此时配置文件需要这么写:

<set name="photoes" table="t_photo">
<key column="photoid"></key>
    <element column="photoAddrs" type="string"></element>
</set>

table=”t_photo”表示photo将单独映射到一张表里,column=”photoid”表示所映射到的”t_photo”表所对应userinfo表的外键。

可以看到此时photo表中的photoid是userinfo表中的外键。

那么问题有来了,如果是对象类型的set集合呢??比如我在userinfo类中在增加这样一个set集合:

private Set<Contact>contacts = new HashSet<>();

并提供get和set方法。此时需要这样配置:

<set name="contacts" table="t_contacts">
    <key column="contact_id"></key>
    <composite-element class="com.mydb.entity.Contact">
        <property name="phone"></property>
        <property name="qq"></property>
    </composite-element>
</set>

ok,关于hibernate关联映射的学习就到这里了,希望大家能够喜欢。

时间: 06-19

hibernate映射学习(三)的相关文章

hibernate映射学习(一)

今天这篇博客将会主要学习hibernate关于实体类和表格中的映射详细学习.首先看下"常用主键的生成策略" hibernate常用主键生成策略 在hibernate中,每个主键必须定义相应的主键生成策略,它用来为持久化类实例生成唯一的标识. 1.assigned 在hibernate中,如果不想使用hibernate的主键生成策略,那么此时就需要自己指定主键,此时的主键生成策略,就需要使用assigned. 在使用assigned的时候,必须手动指定id,比如我连续两次执行如下代码:

浅析Hibernate映射(三)——继承映射

对象模型示例: 继承映射的实现方式有三种: (一)每棵类继承树一张表 关系模型: 映射文件: [html] view plaincopyprint? <hibernate-mapping package="com.jialin.hibernate"> <class name="Animal" table="t_animal" lazy="false"> <id name="id"

hibernate映射对象三种状态的分析

一,首先hibernate中对象的状态有 三种:瞬态.游离态和持久态,三种状态转化的方法都是通过session来调用,瞬态到持久态的方法有save().saveOrUpdate(). get().load():持久态到瞬态的方法有delete():游离态到持久态的方法有update().saveOrUpdate(). lock():持久态到游离态的方法有:session.close().session.evict().session.clear(). 二,Hibernate的状态 hiberna

【SSH系列】Hibernate映射 -- 一对多关联映射

    映射原理       一对多关联映射和多对一关联映射的映射原理是一样一样的,所以说嘛,知识都是相通的,一通百通,为什么说一对多关联映射和多对一关联映射是一样的呢?因为她们都是在多的一端加入一个外键,指向一的一段,关联关系都是在多的一端进行维护,只是我们在写映射的时候发生了变化.       一对多和多对一的映射原理是一样的,但是她们之间也存在着小小的区别,毕竟世界上没有两片完全相同的叶子,她们之间的区别就是维护的关系不同,我们先来看多对一,多端维护一端的关系,在加载多端的时候,可以将一端

u-boot学习(三):u-boot源码分析

建立域模型和关系数据模型有着不同的出发点: 域模型: 由程序代码组成, 通过细化持久化类的的粒度可提高代码的可重用性, 简化编程 在没有数据冗余的情况下, 应该尽可能减少表的数目, 简化表之间的参照关系, 以便提高数据的访问速度 Hibernate 把持久化类的属性分为两种: 值(value)类型: 没有 OID, 不能被单独持久化, 生命周期依赖于所属的持久化类的对象的生命周期 实体(entity)类型: 有 OID, 可以被单独持久化, 有独立的生命周期(如果实体类型包含值类型,这个值类型就

【SSH进阶之路】Hibernate映射——多对多关联映射(八)

上篇博文[SSH进阶之路]Hibernate映射--一对多关联映射(七),我们介绍了一对多关联映射,它是多对多关联映射的基础. 多对多映射是现实生活中最常见的映射,也是最容易理解的映射.废话少说,直接开始. 映射原理 不论是单向关联还是双向关联都是通过第三张表,将两个表中的主键放到第三张做一个关联.用第三张表来解决可能会造成数据冗余的问题. 举例 一个用户(User)对多个角色(Role),一个角色对多个用户. 分类 单向的多对多关联映射(单向User--->Role) 对象模型 关系模型 实例

Hibernate 映射文件详解

Hibernate 映射文件详解 2010-09-02 19:03:33|  分类: Hibernate学习|举报|字号 订阅 Hibernate的持久化类和关系数据库之间的映射通常是用一个XML文档来定义的.该文档通过一系列XML元素的配置,来将持久化类与数据库表之间建立起一一映射.这意味着映射文档是按照持久化类的定义来创建的,而不是表的定义. 一.根元素:<hibernate-mapping>,每一个hbm.xml文件都有唯一的一个根元素,包含一些可选的属性 1)package:指定一个包

hibernate 框架学习笔记---网上摘抄的一个非常好的例子

编写Spring+Hibernate框架下的应用,总是离不了编写一个通用的泛型GenericHibernateDao.查阅了网上不少的GenericHibernateDao实现,归纳整理为如下实现,供后续编码参考. 首先定义接口泛型DAO接口 GenericDao package com.th.huz; import java.io.Serializable;import java.util.Collection;import java.util.Iterator;import java.uti

Hibernate映射关系之多对多

1.用户表user和优惠券coupon存在多对多的关系,一个用户可以拥有多个优惠券,一个优惠券可以从属于多个用户. 2.user.java,创建了中间表tb_user_coupon (1)JoinTable表示中间表的 (2) /** * 一个用户可以拥有多个优惠券 但优惠券只有自己的属性,没有用户的引用 单边的一对多关系 */ @ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.PERSIST }) @JoinTable(n