您的位置:首页 > 其它

hibernate映射学习(三)

2015-06-20 08:37 337 查看
在前面几篇文章中,分别学习了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关联映射的学习就到这里了,希望大家能够喜欢。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: