Hibernate4 关联关系的映射案例

Hibernate4 关联关系的映射案例

一对多、多对多、一对一的建表原则

一对多

如:一个用户,生成多个订单,每一个订单只能属于一个用户。
建表原则:
在多的一方创建一个字段,作为外键,指向一的一方的主键.

多对多

如:一个学生可以选择多门课程,一个课程可以被多个学生选择。
建表原则:
创建第三张表,中间表至少有两个字段,分别作为外键指向多对多双方主键.

一对一(特殊,应用很少)

一个公司只能有一个注册地址,一个注册地址,只能被一个公司使用(否则将两个表建到一个表)
建表原则:
* 唯一外键方式:一对一的双方,假设一方是多的关系,需要在多的一方创建一个字段作为外键,指向一的一方的主键。但是在外键添加一个unique
* 主键对应方式:一对一的双方,通过主键进行关联。

案例

1. 需求描述:

  1. 设计一个数据库表结构,可以存储满足记录班级学生选课的信息记录;
  2. 一个班级可以有多个学生,一个学生可以选择多门课程;
  3. 添加如下表数据,session.save方法只操作student对象即可完成相关联的其他表数据
学生姓名所属班级所选课程
张三三年二班高等数学、物理
李四三年二班物理、英语
小丽三年四班高等数学、物理、英语
James三年四班高等数学
David三年二班高等数学、英语

2. 需求分析

设计三张表格,分别存储学生、课程、班级信息,其中:
学生表,主键sid,关联外键班级表-classid
课程表,主键cid,关联外键学生表-sid
班级表,主键classid。

学生表与课程表是多对多关系;
班级表与学生表是一对多关系;
班级表与课程表没有关系。

学生表与课程表的关联:生成一张中间表,分别使用学生表sid和课程表cid作为外键关联这两张表。
学生表与班级表的关联,在学生表中(一对多的多方)维护外键,添加外键关联到班级表的classid。

表结构及对象结构如下图所示。
表结构.jpg

3. 持久化对象类的定义

student.java

public class Student {
    private Integer sid;
    private String sname;
    private Classnum sclass;
    private Set<Course> courses = new HashSet<Course>();

    public Integer getSid() {
        return sid;
    }
    public void setSid(Integer sid) {
        this.sid = sid;
    }
    public String getSname() {
        return sname;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    public Classnum getSclass() {
        return sclass;
    }
    public void setSclass(Classnum sclass) {
        this.sclass = sclass;
    }
    public Set<Course> getCourses() {
        return courses;
    }
    public void setCourses(Set<Course> courses) {
        this.courses = courses;
    }
}

Course.java

public class Course {
    private Integer cid;
    private String cname;
    private Set<Student> students = new HashSet<Student>();

    public Integer getCid() {
        return cid;
    }
    public void setCid(Integer cid) {
        this.cid = cid;
    }
    public String getCname() {
        return cname;
    }
    public void setCname(String cname) {
        this.cname = cname;
    }
    public Set<Student> getStudents() {
        return students;
    }
    public void setStudents(Set<Student> students) {
        this.students = students;
    }
}

classnum.java

public class Classnum {
    private Integer classid;
    private String classname;
    private Set<Student> students = new HashSet<Student>();

    public Integer getClassid() {
        return classid;
    }
    public void setClassid(Integer classid) {
        this.classid = classid;
    }
    public String getClassname() {
        return classname;
    }
    public void setClassname(String classname) {
        this.classname = classname;
    }
    public Set<Student> getStudents() {
        return students;
    }
    public void setStudents(Set<Student> students) {
        this.students = students;
    }
}

4. 映射配置文件

student.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.lim.user.domain">
    <class name="Student" table="student">
        <!-- 配置唯一标识 -->
        <id name="sid" column="sid">
            <!-- 主键生成策略 -->
            <!-- native: 本地策略,根据底层数据库不同,自动选择identity或sequence -->
            <generator class="native" />
        </id>

        <!-- 配置属性映射 -->
        <property name="sname" column="sname" type="java.lang.String" length="30" />

        <!-- 配置关联关系 -->
        <!-- 学生表和课程表是多对多的关系,生成的中间表名称是choose_course -->
        <set name="courses" table="choose_course" cascade="save-update">
            <!-- 当前表在中间表的外键列 -->
            <key column="sid" />
            <!-- class:另一方类的全路径. column:另一方在中间表中外键名称 -->
            <many-to-many class="cn.lim.user.domain.Course" column="cid" />
        </set>

        <!-- 班级表和学生表是一对多的关系,使用多方学生表维护外键 -->
        <!-- name:关联对象的java属性的名称.column:表中的外键名称.class:关联对象类的全路径-->
        <many-to-one name="sclass" column="classid" class="cn.lim.user.domain.Classnum" cascade="save-update"/>
    </class>
</hibernate-mapping>

course.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.lim.user.domain">
    <class name="Course" table="courses">
        <!-- 配置唯一标识 -->
        <id name="cid" column="cid">
            <!-- 主键生成策略 -->
            <!-- native: 本地策略,根据底层数据库不同,自动选择identity或sequence -->
            <generator class="native" />
        </id>

        <!-- 配置属性映射 -->
        <property name="cname" column="cname" type="java.lang.String" length="30" />

        <!-- 配置关联关系 -->
        <!-- 学生表和课程表是多对多的关系 -->
        <!-- name是本java对象保存对方表对象的属性,生成的中间表名称是choose_course,inverse:课程是被动方,放弃外键的维护权以避免产生多余sql -->
        <set name="students" table="choose_course" cascade="save-update" inverse="true">
            <!-- 当前表在中间表的外键列 -->
            <key column="cid" />
            <!-- class:另一方类的全路径. column:另一方在中间表中外键名称 -->
            <many-to-many class="cn.lim.user.domain.Student" column="sid" />
        </set>
    </class>
</hibernate-mapping>

classnum.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.lim.user.domain">
    <class name="Classnum" table="class">
        <!-- 配置唯一标识 -->
        <id name="classid" column="classid">
            <!-- 主键生成策略 -->
            <!-- native: 本地策略,根据底层数据库不同,自动选择identity或sequence -->
            <generator class="native" />
        </id>

        <!-- 配置属性映射 -->
        <property name="classname" column="classname" type="java.lang.String" length="30" />

        <!-- 配置多表级联 -->
        <!-- 班级表和学生表是一对多的关系 -->
        <!-- name是本java对象保存对方表对象的属性 -->
        <set name="students" cascade="save-update" inverse="true">
            <!-- 多方的外键的名称(学生表中的classid) -->
            <key column="classid" />
            <!-- 另一方类的全路径 -->
            <one-to-many class="cn.lim.user.domain.Student" />
        </set>
    </class>
</hibernate-mapping>

5. 几个属性选项

级联属性cascade的取值:
- none:不使用级联
- dave-update:保存或更新的时候级联
- delete:删除的时候级联
- all:除了孤儿删除以外的所有级联.
delete-orphan:孤儿删除(孤子删除).
* 仅限于一对多.只有一对多时候,才有父子存在.认为一的一方是父亲,多的一方是子方.
* 当一个客户与某个订单解除了关系.将外键置为null.订单没有了所属客户,相当于一个孩子没有了父亲.将这种记录就删除了。

  • all-delete-orphan:包含了孤儿删除的所有的级联.

具体在哪里配置级联属性?
如,只想session.save(Object1)时,Object2就被自动关联保存,那么在Object1的级联配置下使用cascate属性。

外键弃权inverse的取值
- true:在哪一端配置,那么哪一端放弃了外键的维护权。

多对多必须有一方放弃外键的维护权,否则会报错。一般情况下,被动的一方去放弃,如学生表和课程表,学生选课,因此学生主动,课程被动。
一对多最好有一方放弃外键的维护权,否则会产生多余的sql语句,影响性能。一般情况下一的一方放弃维护权。

lazy属性和fetch属性的取值
在一的一方关联多的一方:在<set>标签下配置。
fetch属性控制sql语句的类型,其取值有:
* join:发送迫切左外连接的SQL查询关联对象。如果fetch=“join”,那么lazy被忽略。
* select :默认值,发送多条SQL查询关联对象。
* subselect:发送子查询查询关联对象。(需要使用Query接口测试)

lazy属性控制关联对象的检索是否采用延迟,其取值有:
* true:默认值,查询关联对象的时候使用延迟检索
* false:查询关联对象的时候不使用延迟检索
* extra:及其懒惰

在多的一方关联一的一方:在<many-to-one>标签下配置。
fetch属性控制sql语句的类型,其取值有:
* join:发送迫切左外连接的SQL查询关联对象。如果fetch=“join”,那么lazy被忽略。
* select :发送多条SQL查询关联对象。

lazy属性控制关联对象的检索是否采用延迟,其取值有:
* false:不延迟
* proxy:使用代理.检索订单额时候,是否马上检索客户,由Customer对象的映射文件中class标签上的lazy属性来决定。
* no-proxy:不使用代理

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值