首先介绍一下一对多是什么,一对多关系是关系数据库中两个表之间的一种关系,该关系中第一个表中的单个行可以与第二个表中的一个或多个行相关,但第二个表中的一个行只可以与第一个表中的一个行相关。

例如:我有一个Customer类表示客户,一个Linkman类表示联系人。在这个关系中,我定义一个客户可以对应多个联系人,而一个联系人只能对应一个客户,那么这里的客户就是“一”,联系人就是“多”。

一对多关系的建表原则:在“多”的一方创建字段作为外键,指向“一”的一方的主键。

例如上图的例子,在“多”的一方创建一个外键cid,指向“一”的一方。例子中的客户张三就有两个联系人。

首先创建两个实体类Customer类和Linkman类,注意在这两个类中要相互关联

我的Customer类中采用Set集合的方式关联Linkman类来表示一个客户可以有多个联系人

package com.hibernate.entity;import java.util.HashSet;
import java.util.Set;public class Customer {//客户idprivate Integer cid;//客户姓名private String cName;//客户等级private String cLevel;//客户来源private String cSource;//客户联系电话private String cPhone;//客户邮箱private String cMail;//联系人private Set<Linkman> linkmans = new HashSet<Linkman>();public Set<Linkman> getLinkmans() {return linkmans;}public void setLinkmans(Set<Linkman> linkmans) {this.linkmans = linkmans;}public Integer getCid() {return cid;}public String getcName() {return cName;}public String getcLevel() {return cLevel;}public String getcSource() {return cSource;}public String getcPhone() {return cPhone;}public String getcMail() {return cMail;}public void setCid(Integer cid) {this.cid = cid;}public void setcName(String cName) {this.cName = cName;}public void setcLevel(String cLevel) {this.cLevel = cLevel;}public void setcSource(String cSource) {this.cSource = cSource;}public void setcPhone(String cPhone) {this.cPhone = cPhone;}public void setcMail(String cMail) {this.cMail = cMail;}@Overridepublic String toString() {return "Customer [cid=" + cid + ", cName=" + cName + ", cLevel=" + cLevel + ", cSource=" + cSource + ", cPhone="+ cPhone + ", cMail=" + cMail + ", linkmans=" + linkmans + "]";}
}
package com.hibernate.entity;public class Linkman {//联系人idprivate Integer lkm_id;//联系人姓名private String lkm_name;//联系人性别private String lkm_gender;//联系人电话private String lkm_phone;//所属客户private Customer lkm_customer;public Customer getLkm_customer() {return lkm_customer;}public void setLkm_customer(Customer lkm_customer) {this.lkm_customer = lkm_customer;}public Integer getLkm_id() {return lkm_id;}public String getLkm_name() {return lkm_name;}public String getLkm_gender() {return lkm_gender;}public String getLkm_phone() {return lkm_phone;}public void setLkm_id(Integer lkm_id) {this.lkm_id = lkm_id;}public void setLkm_name(String lkm_name) {this.lkm_name = lkm_name;}public void setLkm_gender(String lkm_gender) {this.lkm_gender = lkm_gender;}public void setLkm_phone(String lkm_phone) {this.lkm_phone = lkm_phone;}@Overridepublic String toString() {return "Linkman [lkm_id=" + lkm_id + ", lkm_name=" + lkm_name + ", lkm_gender=" + lkm_gender + ", lkm_phone="+ lkm_phone + ", lkm_customer=" + lkm_customer + "]";}
}

实体类有了,我们可以开始配置hibernate的对象映射文件了,推荐采用一个实体类创建一个对象映射文件的方式,注意在这里的配置中我们就要进行级联的相关配置了

Customer.hbm.xml文件的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping><class name="com.hibernate.entity.Customer" table="hibernate_Customer"><id name="cid" column="cid"><generator class="native"></generator></id><property name="cName" column="cName"></property><property name="cLevel" column="cLevel"></property><property name="cSource" column="cSource"></property><property name="cPhone" column="cPhone"></property><property name="cMail" column="cMail"></property><!-- 在客户映射文件中表示所有联系人 --><!-- set标签表示集合,name属性写客户实体类中set集合的名称 --><set name="linkmans" cascade="save-update,delete"><!-- 一对多建表,有外键hibernate机制:双向维护外键,即在“多”和“一”双方都要配置外键,命名外键为clid--><key column="clid"></key><!-- 一对多 :class中写实体类全路径 --><one-to-many class="com.hibernate.entity.Linkman"/></set></class>
</hibernate-mapping>

Linkman.hbm.xml文件的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping>
<!-- 配置类和表对应classname:实体类全路径名table:数据库表名--><class name="com.hibernate.entity.Linkman" table="hibernate_Linkman"><!-- 配置实体类id和表id对应hibernate要求实体类要有一个属性为唯一值hibernate要求表中要有字段作为唯一值--><!-- id标签name属性:实体类中id的属性名column:生成的表字段名称--><id name="lkm_id" column="lkm_id"><!-- native:生成表id为主键自动增长 --><generator class="native"></generator></id><!-- property标签配置实体类其他属性 --><property name="lkm_name" column="lkm_name"></property><property name="lkm_gender" column="lkm_gender"></property><property name="lkm_phone" column="lkm_phone"></property><!-- 表示联系人所属客户name属性:表示多对一中的“一”,即此处为客户,写lkm_customer名称class属性:customer全路径column属性:外键名称--><many-to-one name="lkm_customer" class="com.hibernate.entity.Customer" column="clid"></many-to-one></class>
</hibernate-mapping>

•    <many-to-one> 元素来映射组成关系

–  name: 设定待映射的持久化类的属性的名字

–  column: 设定和持久化类的属性对应的表的外键

–  class:设定待映射的持久化类的属性的类型

•    <set> 元素来映射持久化类的 set 类型的属性

–  name: 设定待映射的持久化类的属性的

•    <key> 元素设定与所关联的持久化类对应的表的外键

–  column: 指定关联表的外键名

•    <one-to-many> 元素设定集合属性中所关联的持久化类

class: 指定关联的持久化类的类名

配置了对象映射文件后就是hibernate的核心配置文件hibernate.cfg.xml了

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" >
<hibernate-configuration><session-factory><!-- 1.配置数据库信息 --><property name="connection.driver_class">com.mysql.jdbc.Driver</property><property name="connection.url">jdbc:mysql://localhost:3306/javadatabase</property><property name="connection.username">root</property><property name="connection.password">*******</property><!-- 2.配置hibernate信息(可选) --><!-- thread:Session对象的生命周期与本地线程绑定jta:Session对象的生命周期与JTA事务绑定managed:Hibernate委托程序来管理Session对象的生命周期 --><property name="hibernate.current_session_context_class">thread</property><!-- 输出底层sql语句 --><property name="show_sql">true</property><!-- 输出底层sql语句格式 --><property name="format_sql">true</property><!-- hibernate帮创建表,需要配置之后update:如果已经表存在,则更新,不存在,则创建--><property name="hbm2ddl.auto">update</property><!-- 配置数据库方言在MySQL里面实现分页关键字limit,只能使用MySQL里在oracle数据库,实现发页用rownum让hibernate框架识别不同数据库的自己特有的语句--><property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property><!-- 3.把映射文件放到核心配置文件中 --><mapping resource="Customer.hbm.xml"/><mapping resource="Linkman.hbm.xml"/></session-factory>
</hibernate-configuration>

级联操作:

1.级联保存:

•在对象–关系映射文件中,用于映射持久化类之间关联关系的元素,<set>, <many-to-one> 和 <one-to-one>都有一个cascade属性,它用于指定如何操纵与当前对象关联的其他对象. 

这里我配置了<set>标签的cascade属性进行简化,即在“一”的一方的对象映射文件中进行配置

对应上文Customer.hbm.xml中的save-update

<set name="linkmans" cascade="save-update,delete">

测试代码如下:

//简化级联保存,需在配置“一”一方的对象映射文件中为<set>标签配置cascade属性为save-update@Testpublic void testSave(){Session session = null;Transaction tx = null;try {session = HibernateUtil.getSession();tx = session.beginTransaction();//创建客户Customer customer = new Customer();customer.setcName("张三");customer.setcLevel("vip");customer.setcSource("网站");customer.setcPhone("13622222222");customer.setcMail("546216@qq.com");//创建联系人Linkman linkman = new Linkman();linkman.setLkm_name("王舞");linkman.setLkm_gender("女");linkman.setLkm_phone("13678944321");//关联客户和联系人customer.getLinkmans().add(linkman);//保存session.save(customer);tx.commit();} catch (Exception e) {tx.rollback();}finally {
            session.close();
        }}

执行完成后,可以发现数据库中创建了对应的两张表

表中信息如下:

接下来我们看看控制台中的sql语句的输出

首先创建了两张表

然后为hibernate_Linkman添加了外键指向hibernate_Customer的cid

再为两张表添加数据

最后为hibernate_Linkman中的外键赋值

以上就是创建并保存的全过程。

级联修改

这里我们首先看看数据库中修改前的数据

接下来我们将李四的联系人赵柳,改到张三的联系人中

测试代码如下

//级联修改
    @Test
    public void testUpdate(){
        Session session = null;
        Transaction tx = null;
        try {
            session = HibernateUtil.getSession();
            tx = session.beginTransaction();
            //根据id获取客户
            Customer customer = session.get(Customer.class, 1);
            //获取联系人
            Linkman linkman = session.get(Linkman.class, 2);
            //修改持久态对象实现数据库自动更新
            customer.getLinkmans().add(linkman);
            linkman.setLkm_customer(customer);
            tx.commit();
        } catch (Exception e) {
            tx.rollback();
        }finally {
            session.close();
        }
    }

此时我们发现数据库中数据改变了

但此时我们看到控制台中的update语句执行了两次

Hibernate: selectcustomer0_.cid as cid1_0_0_,customer0_.cName as cName2_0_0_,customer0_.cLevel as cLevel3_0_0_,customer0_.cSource as cSource4_0_0_,customer0_.cPhone as cPhone5_0_0_,customer0_.cMail as cMail6_0_0_ fromhibernate_Customer customer0_ wherecustomer0_.cid=?
Hibernate: selectlinkman0_.lkm_id as lkm_id1_1_0_,linkman0_.lkm_name as lkm_name2_1_0_,linkman0_.lkm_gender as lkm_gend3_1_0_,linkman0_.lkm_phone as lkm_phon4_1_0_,linkman0_.clid as clid5_1_0_ fromhibernate_Linkman linkman0_ wherelinkman0_.lkm_id=?
Hibernate: selectlinkmans0_.clid as clid5_1_0_,linkmans0_.lkm_id as lkm_id1_1_0_,linkmans0_.lkm_id as lkm_id1_1_1_,linkmans0_.lkm_name as lkm_name2_1_1_,linkmans0_.lkm_gender as lkm_gend3_1_1_,linkmans0_.lkm_phone as lkm_phon4_1_1_,linkmans0_.clid as clid5_1_1_ fromhibernate_Linkman linkmans0_ wherelinkmans0_.clid=?
Hibernate: updatehibernate_Linkman setlkm_name=?,lkm_gender=?,lkm_phone=?,clid=? wherelkm_id=?
Hibernate: updatehibernate_Linkman setclid=? wherelkm_id=?

这就说明效率并不高,同样的数据进行了两次update。

这里就要介绍一下<set>的inverse属性了

•在hibernate中通过对inverse属性的来决定是由双向关联的哪一方来维护表和表之间的关系.
inverse = false 的为主动方,inverse= true 的为被动方, 由主动方负责维护关联关系。
即inverse = true 表示放弃关系维护,false为不放弃关系维护。
•在没有设置 inverse=true的情况下,父子两边都维护父子

关系

在 一对多 关系中,将“多”的一方设为主控方将有助于性能改善

接下来我们进行配置,将Customer.hbm.xml中的<set>标签添加inverse属性值为true,即Customer放弃关系维护,而交由Linkman来进行关系的维护(因为不设置inverse属性则默认为false)

<set name="linkmans" cascade="save-update,delete" inverse="true">

然后我们再把刚刚做的数据库修改再改回去。

@Testpublic void testUpdate(){Session session = null;Transaction tx = null;try {session = HibernateUtil.getSession();tx = session.beginTransaction();//根据id获取客户Customer customer = session.get(Customer.class, 2);//获取联系人Linkman linkman = session.get(Linkman.class, 2);//修改持久态对象实现数据库自动更新customer.getLinkmans().add(linkman);linkman.setLkm_customer(customer);tx.commit();} catch (Exception e) {tx.rollback();}finally {session.close();}}

可以看到数据已经修改回原来的值了

现在控制台中的输出如下:

Hibernate: selectcustomer0_.cid as cid1_0_0_,customer0_.cName as cName2_0_0_,customer0_.cLevel as cLevel3_0_0_,customer0_.cSource as cSource4_0_0_,customer0_.cPhone as cPhone5_0_0_,customer0_.cMail as cMail6_0_0_ fromhibernate_Customer customer0_ wherecustomer0_.cid=?
Hibernate: selectlinkman0_.lkm_id as lkm_id1_1_0_,linkman0_.lkm_name as lkm_name2_1_0_,linkman0_.lkm_gender as lkm_gend3_1_0_,linkman0_.lkm_phone as lkm_phon4_1_0_,linkman0_.clid as clid5_1_0_ fromhibernate_Linkman linkman0_ wherelinkman0_.lkm_id=?
Hibernate: selectlinkmans0_.clid as clid5_1_0_,linkmans0_.lkm_id as lkm_id1_1_0_,linkmans0_.lkm_id as lkm_id1_1_1_,linkmans0_.lkm_name as lkm_name2_1_1_,linkmans0_.lkm_gender as lkm_gend3_1_1_,linkmans0_.lkm_phone as lkm_phon4_1_1_,linkmans0_.clid as clid5_1_1_ fromhibernate_Linkman linkmans0_ wherelinkmans0_.clid=?
Hibernate: updatehibernate_Linkman setlkm_name=?,lkm_gender=?,lkm_phone=?,clid=? wherelkm_id=?

可以看到update语句只有一句了,则提高了效率。

级联删除

先看目前数据库中数据的情况

接下来我们删除第二个客户李四,由于第二个客户李四的联系人是赵柳,那么删除李四的同时也会删除赵柳。

现在控制台中的输出如下:

//级联删除,在“一”的一方<set>标签中配置cascade属性为delete(多个值用英文的“,”隔开)@Testpublic void testDelete(){Session session = null;Transaction tx = null;try {session = HibernateUtil.getSession();tx = session.beginTransaction();//根据id获取客户Customer customer = session.get(Customer.class, 2);//删除session.delete(customer);tx.commit();} catch (Exception e) {tx.rollback();}finally {session.close();}}

执行之后数据库的变化

发现两张表的第二个记录都被删除了。

接着我们看看控制台中的sql语句的输出

Hibernate: selectcustomer0_.cid as cid1_0_0_,customer0_.cName as cName2_0_0_,customer0_.cLevel as cLevel3_0_0_,customer0_.cSource as cSource4_0_0_,customer0_.cPhone as cPhone5_0_0_,customer0_.cMail as cMail6_0_0_ fromhibernate_Customer customer0_ wherecustomer0_.cid=?
Hibernate: selectlinkmans0_.clid as clid5_1_0_,linkmans0_.lkm_id as lkm_id1_1_0_,linkmans0_.lkm_id as lkm_id1_1_1_,linkmans0_.lkm_name as lkm_name2_1_1_,linkmans0_.lkm_gender as lkm_gend3_1_1_,linkmans0_.lkm_phone as lkm_phon4_1_1_,linkmans0_.clid as clid5_1_1_ fromhibernate_Linkman linkmans0_ wherelinkmans0_.clid=?
Hibernate: delete fromhibernate_Linkman wherelkm_id=?
Hibernate: delete fromhibernate_Customer wherecid=?

我们发现一个细节,hibernate是先删除了linkman再删除的customer。这是因为linkman中含有一个外键,若先删除customer,则有可能linkman中原来所指向的customer就没有了,那么逻辑上的对应关系就不存在了,所以数据库不允许我们这样操作。

Hibernate学习总结(5)——一对多的级联操作相关推荐

  1. 关于hibernate注解方法中一对多的级联更新

    最近做的项目中遇到很多各种表间关系是一对多对应的问题,开始自己弄的时候就去网上查,结果发现网上有关这方面的东西给的不是自己想要的,于是经过几天不停的更改测试终于算是勉强完成了级联更新的问题.在此把它记 ...

  2. Hibernate学习系列————注解一对多单向实例

    2019独角兽企业重金招聘Python工程师标准>>> 开发环境:Mysql+Eclipse 一对多单向的列子原理:一个班级,多个学生,学生端为多的一端,他们拥有一个外键指向相同的班 ...

  3. Hibernate一对多关联映射及cascade级联操作

    我们以客户(Customer)与订单(Order)为例 实体类创建 Order订单实体类 //订单-----多的一方 public class Order {private Integer id;pr ...

  4. Hibernate学习-14:实体之间的关系及其配置,级联操作

    1.实体之间有三种关系: * 一对多: * 一个用户,生成多个订单,每一个订单只能属于一个用户. * 建表原则: * 在多的一方创建一个字段,作为外键,指向一的一方的主键. * 多对多: * 一个学生 ...

  5. 【SSH】Hibernate学习(三)一对多、多对一、多对多

    一.一对多,多对一 1.表及实体的表达 2.映射文件中的配置: 3.操作关联属性 public class Demo {@Test//保存客户以及客户下的联系人public void fun1() { ...

  6. Hibernate学习总结【比较与Mybatis框架之间的区别】

    经过一周的Hibernate学习,我理解了作为以JPA为核心的持久层查询标准.JPA所涉及的思想为ORM(object relational mapping),它解释了为什么我们的实体关系要和数据库一 ...

  7. hibernate学习和各种关系总结

    2019独角兽企业重金招聘Python工程师标准>>> 原文地址 http://yifanxiang.blog.163.com/blog/static/500008342010527 ...

  8. Hibernate(二)——一对多查询

    1. 前言 本章节我们讨论Hibernate一对多查询的处理. 在上一章节中(Hibernate(一)--入门),我们探讨了Hibernate执行最基本的增删改查操作.现在我们将情况复杂化:加入我们在 ...

  9. Hibernate学习记录

     Hibernate是一个关于java方面的ORM(Object/Relation Mapping,对象关系映射)框架. ORM的思想:主要实现的功能就是将关系数据库中表的记录应映射成对象,以对象的形 ...

最新文章

  1. WIN10python3.7配置MaskRCNN环境及demo测试(tensorflow-gpu1.x)
  2. Java 常用类库 之 Random 随机数类实例
  3. 《Pro ASP.NET MVC 3 Framework》学习笔记之十三【示例项目SportsStore】
  4. html怎么插入外部js,如何插入js,引用外部js,js在页面中的位置
  5. dotnet Core学习之旅(二):安装IDE
  6. leetcode258. 各位相加(简单题,但是你不看答案想不出来)
  7. java拖动图片拼图_求教,我的这个拼图程序中的移动图片的改怎么做
  8. OJ1035: 分段函数求值
  9. 字符串匹配之KMP算法
  10. 第三章 垃圾回收的一些概念
  11. C1083: 无法打开包括文件:“corecrt.h”
  12. 魔兽 怎么查服务器在线人数,网易魔兽世界人口普查查看
  13. 民生通讯面向全国招募民生通讯形象授权营业厅
  14. (干货)电源方案合集
  15. oracle 删除数据
  16. React 项目性能分析及优化
  17. c语言程序编程线性方程,C语言编程求解线性方程.doc
  18. 可以下載HEVC(H265)的網址
  19. IIS7+PHP安装教程
  20. java常用类库——lang包

热门文章

  1. sqlserver到Oracle,SQLSERVER到ORACLE的数据库迁移
  2. 开关电源设计之LLC变换器的工作原理
  3. sbm预测matlab,DEA ---关于SBM模型的选择
  4. labelimg闪退解决方法(多人检测有效,根本原因)
  5. Windows XP 的最高版本 .net framework 安装
  6. 摩王软件OA+系统功能简介
  7. 压缩感知重构算法综述-学习笔记
  8. 有没有大佬知道怎么把dcm格式核磁图像,进行3D分割
  9. eXtremeDB内存预警机制
  10. 计算机组成原理第五版第五章课件,计算机组成原理第五章课件