Hibernate学习(2)

ORM中,对象和关系数据是业务中的两种表现形式,业务实体是在内存中表现为对象,而在数据库中表现未关系数据.但是内存中的对象之间存在关联和继承关系,而在数据库中,关系数据无法直接表达多对多关联和继承关系.
这是Hibernate学习系列的第二篇blog,准备写写关于Hibernate中反应对象之间的关联关系.

1. 集合 映射

在上一篇blog中我创建了book实体类,这一节中,我将创建User实体类,而每个User类可能有多个phone number,这个时候我们就可以单独创建phone number 表,并在User中使用集合映射,实现每个用户有多个phone number.
此时可更改如下:

package entities;

import java.util.Set;

/**
 * Created by tbxsx on 17-5-26.
 */
public class User {
    private long id;
    private String username;
    private String email;
    private Set<String> phoneSet;

    public User() {
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Set<String> getPhoneSet() {
        return phoneSet;
    }

    public void setPhoneSet(Set<String> phoneSet) {
        this.phoneSet = phoneSet;
    }
}

在User中添加Set phoneSet 属性,在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">
<!-- Generated 2013-11-11 23:19:21 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="entities">
    <class name="entities.User" table="`user`">
        <id name="id" type="long">
            <generator class="native"/>
        </id>
        <property name="email" column="`email`"/>
        <property name="username" column="`username`"/>
        <!-- 
            Set类型的名字是:phoneSet,与之对应的表格是phone,集合中的数据在获得User的时候,立即加载.
        -->
        <set name="phoneSet" table="`phone`" lazy="false">
            <key column="userid"/><!-- 指定 phone表格中的外键-->
            <element column="`phone`" type="string" not-null="true"/><!-- 指定与集合元素对应的字段名字未 phone ,映射类型未string,并且不能为空-->
        </set>
    </class>
</hibernate-mapping>

测试:

    @Test
    public void testSet(){
        SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        User user = new User();
        Phone phone = new Phone();
        phone.setPhone("15821859797");
        phone.setUser(user);
        user.setUsername("lls");
        user.setEmail("email@qq.com");
        Set<String> phoneSet = new HashSet<String>();
        phoneSet.add(phone.getPhone());
        user.setPhoneSet(phoneSet);
        session.save(user);
        session.getTransaction().commit();
        System.out.println("Test!");
    }

结果:
这里写图片描述
这里写图片描述
说明:在User实体类中添加SetphoneSet属性,然后在user的hbm.xml文件中添加集合对象配置.
在数据库中数据库表如下:
user表格:

字段名类型说明
idlong主键,自增
usernamevarchar
emailvarchar

Phone表格:

字段名类型说明
useridlong外键,用户编号,引用user表格中的id列
phonevarchar电话号码

值得注意的是,这里我们并没有创建Phone.hbm.xml文件,Phone表格是在User.hbm.xml文件set属性创建的.

我们分析一下hibernate怎样根据需要找到phone的.

我们可以把他类比为 到NPC处领任务.那么我们需要知道地点,然后是该地点的哪一个NPC,最后NPC可能有多个任务,我们需要确定接受哪一个任务.
在POJO中集合属性

private Set<String> phoneSet;

中的每个元素是怎样找到的,首先根据set(hbm.xml中)的name属性

<set name="phoneSet" table="`phone`" lazy="false">
            <key column="userid"/><!-- 指定 phone表格中的外键-->
            <element column="`phone`" type="string" not-null="true"/><!-- 指定与集合元素对应的字段名字未 phone ,映射类型未string,并且不能为空-->
        </set>

那么这是与之对应的映射,然后有这几个问题:
1. 去哪张table中寻找?

set中的table属性回答了这个问题
```
 table="`phone`"
```
去phone表中找.

这是地点
2. 这张表格中有那么多元组,该找哪一个元组呢?

hibernate中的key属性回答了这个问题:
```
<key column="userid"/>
```
去寻找userid与User表格中id相等(如果制定property-key = XXX,则与

这是地点之下的人物(NPC)
可参考:blog
3. 找到了元组,我该取走那一个元素?

这就用到了最后一个属性
```
<element column="`phone`" type="string" not-null="true"/><!-- 指定与集合元素对应的字段名字未 phone ,映射类型未string,并且不能为空-->
```
取走类型为String的phone.
**这是任务**
至此我们完成了去NPC处取走人物的全部流程,也明确了hibernate查询的方式

集合属性中当然还有List和Map属性,这就相当于从NPC处取走的任务在我们的任务栏中还有一定的顺序.
因此需要加一个属性来确定任务在我们任务栏的位置:
List:

        <list name="phoneSet" table="`phone`" lazy="false">
            <key column="`userid`"/>
            <!--确定在任务栏中的位置-->
            <list-index column="idx"/>
            <element type="string" column="`phone`" not-null="true"/>
        </list>

数据库phone表中会多加上一个属性 idx,用来确定位置.
Map:

        <map name="phoneSet">
            <key column="`userid`"/>
            <index column="key" type="string"/>
            <element column="`phone`" type="string" not-null="true"/>
        </map>   

数据库表中会多加上一个属性key varchar类型(通过 添加的)来确定在任务栏中的位置.
2. 实体对象之间的关系映射
接下来,有了上面值集合的经验,我们来接触一下实体对象之间的关系映射.
首先我们讲实体对象之间的关系映射分一下类,了解一下一共有哪些类型的关联关系:

方向关联侧单向一对多单向多对一双向多对一单向多对多双向多对多
set,one-to-manyset,one-to-many
many-to-oneone-to-manyset,many-to-manyset,many-to-many
set,many-to-many

注:如果注明单向X对Y,那么X知道Y而Y不知道X.
对与一对一关系,分为两大类:
1.基于主键的一对一关系
也就是说,一个表格的主键也是它的外键.
比如说,我们假设每个Husband 只有一个wife,而每个wife也只有一个Husband

    package entities;

/**
 * Created by tbxsx on 17-5-28.
 */
public class Husband {
    private long id;
    private String name;
    private Wife wife;

    public Husband() {
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Wife getWife() {
        return wife;
    }

    public void setWife(Wife wife) {
        this.wife = wife;
    }
}

Wife:

package entities;

/**
 * Created by tbxsx on 17-5-28.
 */
public class Wife {
    private long id;
    private String name;
    private Husband husband;

    public Wife() {
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Husband getHusband() {
        return husband;
    }

    public void setHusband(Husband husband) {
        this.husband = husband;
    }
}

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">
<!-- Generated 2013-11-11 23:19:21 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="entities">
    <class name="entities.Husband" table="`husband`">
        <id name="id" type="long">
            <generator class="native"/>
        </id>
        <property name="name" column="`name`"/>
        <one-to-one name="wife" class="entities.Wife"></one-to-one>
    </class>

    <class name="entities.Wife" table="`wife`">
        <id name="id" type="long">
            <generator class="foreign">
                <param name="property">husband</param>
            </generator>
        </id>
        <property name="name" column="`name`"/>
        <one-to-one name="husband" class="entities.Husband" constrained="true"></one-to-one>
    </class>
</hibernate-mapping>

注意:保存时,若只保存一个实体类,当保存没有外键的实体类的时候,会保存两个类,否则只会保存一个实体类.

2.基于外键的一对一关系
在多对一关系中,多方加上unique属性,即外键唯一.

双向多对多关系

以User和Address之间的多对多关系为例,一个User可能有多个Address,而一个Address而有可能有多个User,因此在数据库中将会存在一个关系表user_address:

字段名类型说明
aidlong外键,依赖于Address的id
uidlong外键,依赖于User的id

此时User.java类如下:

package entities;

import java.util.Set;

/**
 * Created by tbxsx on 17-5-26.
 */
public class User {
    private long id;
    private String username;
    private String email;
    private Set<String> phoneSet;
    private Set<Address> addressSet;

    public User() {
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Set<String> getPhoneSet() {
        return phoneSet;
    }

    public void setPhoneSet(Set<String> phoneSet) {
        this.phoneSet = phoneSet;
    }

    public Set<Address> getAddressSet() {
        return addressSet;
    }

    public void setAddressSet(Set<Address> addressSet) {
        this.addressSet = addressSet;
    }
}

可见多出了一个Set< Address >addressSet属性.
在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">
<!-- Generated 2013-11-11 23:19:21 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="entities">
    <class name="entities.User" table="`user`">
        <id name="id" type="long">
            <generator class="native"/>
        </id>
        <property name="email" column="`email`"/>
        <property name="username" column="`username`"/>
        <set name="phoneSet" table="`phone`" lazy="false">
            <key column="userid" foreign-key=""/>
            <element column="`phone`" type="string" not-null="true"/>
        </set>
        <!-- --> 
             <!-- name="addressSet" 告诉我们配置的是addressSet映射关系,数据库中表是user_address -->
        <set name="addressSet" table="`user_address`" cascade="save-update">
            <!-- 在user_address table中添加外键uid,默认依赖于user table中的主键 -->
            <key column="uid"></key>
            <!-- 
                由于是多对多关系,在找到user_address中uid与user table中id相等的元组时,元组会有多个,
                因此通过另一个外键 aid(user_address table中)到Address中去找id与aid相等的,
                即可找到相应的Address
            -->
            <many-to-many column="aid" class="entities.Address"/>
        </set>
    </class>
</hibernate-mapping>

同理,在Address.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">
<!-- Generated 2013-11-11 23:19:21 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="entities">
    <class name="entities.Address" table="`address`">
        <id name="id" type="long">
            <generator class="native"/>
        </id>
        <property name="name" column="`name`"/>
        <set name="userSet" table="`user_address`" cascade="save-update">
            <key column="aid"></key>
            <many-to-many column="uid" class="entities.User"/>
        </set>
    </class>
</hibernate-mapping>

由此就建立了双向多对多关系.

总结:
1. 1-n关系或者n-1关系:
数据库中在多的一方映射的表格中加入外键,所不同的是,在XXX.hbm.xml文件中,若是一方知道多方,那么是在一方加入

        <set name="userSet" table="one_many">
            <key column="many_id"></key>
            <one-to-many class="many_class"></one-to-many>
        </set>

也就是说,外键是通过在一方加入的.
若是多方知道一方,那么在多方加入:

    <many-to-one name="one_class"class="one_class"column="table_fk"></many-to-one>

双向多对一:
上面两个都要配置.

一对一关系:
分为基于外键的和基于主键的.
前者使用one-to-one,如上演示的Husband和Wife关系.
后者使用many-to-one和one-to-one关系,其中many-to-one侧加入unique属性.
多对多关系:
都是配置many-to-many,set.

源代码地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值