14. Hibernate 一对多双向关联映射

1. 前言

本节课程和大家一起聊聊一对多关联映射。通过本节课程,你将了解到:

  • 如何实现一对多关联映射;

  • 如何实现双向一对多关联映射;

  • 关联映射中的级联操作。

2. 一对多关联映射

关系型数据库中表与表中的数据存在一对多(或多对一)关系。

如学生表、班级表。一个班级有多个学生,多个学生可以在同一个班级。

一对多或多对一本质上是一样的,如同一块硬币的正面和反面,只是看待事物的角度不同而已。

数据库中有学生表、班级表。使用 Hibernate 进行数据操作时, 程序中就应该有学生类、班级类。

同时学生类、班级类应该使用 OOP 语法描述出如同学生表和班级表一样的关系。并且还要让 Hibernate 看得懂。

有了前面的基础,直接上代码:

创建班级类:

@Entity
public class ClassRoom {private Integer classRoomId;private String classRoomName;private String classRoomDesc;@Id@GeneratedValue(strategy = GenerationType.IDENTITY)public Integer getClassRoomId() {return classRoomId;}

需求:查询学生时,得到学生所在班级信息。

进入学生类,添加如下代码,描述学生类班级类的关系:

private ClassRoom classRoom;

除此之外,还需要让 Hibernate 知道,这个对象属性的值来自于班级表中的对应数据,进一步修改代码:

private ClassRoom classRoom;
@ManyToOne(targetEntity=ClassRoom.class)
@JoinColumn(name="classRoomId")
public ClassRoom getClassRoom() {return classRoom;
}
  • @ManyToOne 告诉 Hibernate,从学生的角度来看,学生是多的一边,查询班级表可以得到学生所在班级信息。
  • @JoinColumn 告诉 Hibernate 需要带着指定的字段值到班级表中匹配数据。

修改 Hibernate 主配置文件中内容:

<property name="hbm2ddl.auto">create</property>
<mapping class="com.mk.po.Student" />
<mapping class="com.mk.po.ClassRoom" />

为了让事情变得简单明了,在主配置文件中只保留学生类班级类的映射关系。

学生类中的所有属性描述:

private Integer stuId;private String stuName;private String stuSex;private String stuPassword;private Blob stuPic;private ClassRoom classRoom;@ManyToOne(targetEntity=ClassRoom.class)@JoinColumn(name="classRoomId")public ClassRoom getClassRoom() {return classRoom;}

使用上一节课的模板对象跑一个空测试实例:

@Test
public void testGetByTemplate() {HibernateTemplate<Student> hibernateTemplate=new HibernateTemplate<Student>();	
}

目的是让 Hibernate 重新创建学生表、班级表。别忘记啦,自动创建表后,修改回:

<property name="hbm2ddl.auto">update</property>

进入 MySql,在学生表、班级表中手工添加几条测试数据:

到了完成需求的时候,测试下面实例:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();hibernateTemplate.template(new Notify<Student>() {@Overridepublic Student action(Session session) {Student stu=(Student)session.get(Student.class, new Integer(1));System.out.println("学生姓名:"+stu.getStuName());System.out.println("学生所在班级:"+stu.getClassRoom().getClassRoomName());return stu_;}});

控制台输出结果:

Hibernate: selectstudent0_.stuId as stuId1_1_1_,student0_.classRoomId as classRoo6_1_1_,student0_.stuName as stuName2_1_1_,student0_.stuPassword as stuPassw3_1_1_,student0_.stuPic as stuPic4_1_1_,student0_.stuSex as stuSex5_1_1_,classroom1_.classRoomId as classRoo1_0_0_,classroom1_.classRoomDesc as classRoo2_0_0_,classroom1_.classRoomName as classRoo3_0_0_ fromStudent student0_ left outer joinClassRoom classroom1_ on student0_.classRoomId=classroom1_.classRoomId wherestudent0_.stuId=?
学生姓名:Hibernate
学生所在班级:c1911

Hibernate 使用 left outer join 构建了一条多表查询语句!

3. 双向一对多关联映射

需求:查询班级时,想知道班上有多少名学生,又应该如何映射?

进入班级类,添加如下属性:

private Set<Student> students;

使用集合属性 students,描述了一个班有多名学生。

为什么使用 Set 集合?

因为一个班级内不可能出现两个完全相同的学生对象。

这还仅仅只是 OOP 层面上的关系,还需要告诉 Hibernate 应该如何填充数据。

添加下面代码:

private Set<Student> students;
@OneToMany(targetEntity=Student.class,mappedBy="classRoom")
public Set<Student> getStudents() {return students;
}
  • @OneToMany:很直白的说明了一个班级会有多名学生,指引 Hibernate 在填充数据时,要找到所有学生,别遗漏了;
  • 属性 mappedBy=“classRoom”: 告诉 Hibernate,班级和学生之间的关系在学生类中已经说的够明白了,应该不需要再废话了吧。

OK!把前面的测试实例改为查询班级信息:

HibernateTemplate<ClassRoom> hibernateTemplate = new HibernateTemplate<ClassRoom>();hibernateTemplate.template(new Notify<ClassRoom>() {@Overridepublic ClassRoom action(Session session) {ClassRoom classRoom=(ClassRoom)session.get(ClassRoom.class, new Integer(1));System.out.println("班级名称:"+classRoom.getClassRoomName());
System.out.println("------我是分隔线------------------------");System.out.println("班级学生人数:"+classRoom.getStudents().size());return classRoom;}});

查看控制台输出结果:

Hibernate: selectclassroom0_.classRoomId as classRoo1_0_0_,classroom0_.classRoomDesc as classRoo2_0_0_,classroom0_.classRoomName as classRoo3_0_0_ fromClassRoom classroom0_ whereclassroom0_.classRoomId=?
班级名称:c1911
------我是分隔线------------------------
Hibernate: selectstudents0_.classRoomId as classRoo6_0_1_,students0_.stuId as stuId1_1_1_,students0_.stuId as stuId1_1_0_,students0_.classRoomId as classRoo6_1_0_,students0_.stuName as stuName2_1_0_,students0_.stuPassword as stuPassw3_1_0_,students0_.stuPic as stuPic4_1_0_,students0_.stuSex as stuSex5_1_0_ fromStudent students0_ wherestudents0_.classRoomId=?
班级学生人数:2

会发现一个很有意思的地方。Hibernate 查询班级时,构建了两条 SQL

先查询班级,当需要学生信息时,才构建查询学生的 SQL

大家应该也猜出来了,当从学生表查询班级表时,Hibernate 采用的是立即策略。

当查询从班级表查询到学生表时,Hibernate 采用的是延迟加载策略。

采用延迟加载都只有一个目的,需要时加载,提高响应速度。

现在,学生类和班级类的映射配置信息,能让 Hibernate 自动从学生表查询到班级表,也能从班级表查询到学生表。

这种 2 个实体类中的映射关系就称为双向一对多关联映射

无论是 @ManyToOne 还是 @OneToMany 注解都有 fetch 属性,可以设置的值有 2 个选择:

  • FetchType.EAGER
  • FetchType.LAZY

所以,在双向一对多关联映射可以选择是否启用延迟加载,这和一对一关联映射中是一样的,就不在此重复复述。

是否采用延迟加载,由项目逻辑决定。

4. 一对多关联映射中的级联操作

什么是级联操作?

关系型数据库中由主外键维系的两张表,具有主从关系。

如学生表和班级表,班级班是主表,学生表是从表。

类似于删除某一个班级的信息,则需要先删除所在班的学生信息,再删除班级信息,这个操作就是级联操作。

所谓级联操作,指操作一张表时,是否会牵连到与之有关联的其它表。

现在,咱们是使用 Hibernate 进行数据操作,不可能还要劳驾自己亲力亲为吧。只需要做些简单配置,就可以让 Hibernate 自动做级联操作。

进入班级类,修改代码如下:

@OneToMany(targetEntity=Student.class,mappedBy="classRoom",cascade=CascadeType.REMOVE)public Set<Student> getStudents() {return students;}

很简单,只需要使用 @OneToMany 的 cascade 属性,就能让 Hibernate 明白如何做级联操作。默认情况下,没有级联效应。

cascade 是一个枚举类型:

public enum CascadeType {ALL,PERSIST,MERGE,REMOVE,REFRESH,DETACH
}
  • ALL: 级联所有操作;
  • PERSIST: 级联新增;
  • MERGE: 级联更新或者新增;
  • REMOVE: 级联删除;
  • REFRESH: 级联刷新;
  • DETACH: 级联分离。

测试删除班级实例:

HibernateTemplate<ClassRoom> hibernateTemplate = new HibernateTemplate<ClassRoom>();hibernateTemplate.template(new Notify<ClassRoom>() {@Overridepublic ClassRoom action(Session session) {ClassRoom classRoom=(ClassRoom)session.get(ClassRoom.class, new Integer(1));	session.delete(classRoom);return null;}});

如果不添加 cascade 相关说明,因为有学生引用班级信息,班级信息是不能被删除的。

添加后再测试,查看表中内容:班级以及班级所在学生信息全部删除!

 

删除班级时能级联删除学生,反过来,删除学生能删除班级吗?

想法很好,实践是检验真理的唯一手段,学生类中修改成如下代码:

@ManyToOne(targetEntity=ClassRoom.class,cascade=CascadeType.REMOVE)@JoinColumn(name="classRoomId")public ClassRoom getClassRoom() {return classRoom;}

测试实例:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();hibernateTemplate.template(new Notify<Student>() {@Overridepublic Student action(Session session) {Student stu=(Student)session.get(Student.class, new Integer(2));session.delete(stu);return stu;}});

结果很残酷!学生被删除了,班级也被删除了!

级联级联,只要设置了级联,不管删除学生还是班级,只要在对应表中有引用关系的数据就会被删除。

现在,学生类、班级类中的级联删除都打开了。如果对下面情形的数据(编号 1、2 的学生的班级编号都为 1)进行删除操作,则会发生什么事情?

 

数据库中的数据如下:

 测试删除编号为 1 的学生:

HibernateTemplate<Student> hibernateTemplate = new HibernateTemplate<Student>();hibernateTemplate.template(new Notify<Student>() {@Overridepublic Student action(Session session) {Student stu=(Student)session.get(Student.class, new Integer(1));session.delete(stu);return stu;}});

进入 MySql,查看一下:

天呀!这是级联还是株连呀,太让人后怕,数据都没有了。

删除学生时,会级联删除和学生有关的班级,班级删除时,又会查看学生表中是否还存在与班级有关联的学生,有,则一刀下去,连根拔起。

Hibernate 有点刹不住车,产生了级联连锁反应。

针对上面的测试,如果班级表的级联关闭,执行测试代码,请问结果又会怎样?

本节课程,讲解了级联删除,级联添加的内容留到下节课继续展开。

5. 小结

本文和大家聊了双向一对多关联映射。

无论是一对一双向关联映射,还是一对多双向关联映射。都可以根据需要随时设置是否延迟加载、级联等操作。

在使用级联操作时,一定要小心,避免产生连锁反应,删除了不应该删除的数据。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/49568.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解Linux网络(八):内核如何发送网络包

深入理解Linux网络&#xff08;八&#xff09;&#xff1a;内核如何发送网络包 一、总览二、网卡启动准备三、ACCEPT 创建新 SOCKET四、开始发送数据send 系统调⽤实现传输层处理传输层拷贝传输层发送 网络层发送原理邻居⼦系统网络设备子系统软中断调度igb网卡驱动发送发送完成…

Python 实现PDF和TIFF图像之间的相互转换

PDF是数据文档管理领域常用格式之一&#xff0c;主要用于存储和共享包含文本、图像、表格、链接等的复杂文档。而TIFF&#xff08;Tagged Image File Format&#xff09;常见于图像处理领域&#xff0c;主要用于高质量的图像文件存储。 在实际应用中&#xff0c;我们可能有时需…

wefwefwe

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

在 ArkTS 中集成 C 语言模块来管理文件描述符

文章目录 前言ArkTS模块C语言模块C模块代码 总结 前言 在现代开发中&#xff0c;尤其是在处理文件操作时&#xff0c;使用文件描述符&#xff08;fd&#xff09;是一种常见的方法。ArkTS提供了一种强大的方式来与底层C代码交互&#xff0c;使我们能够利用C语言的性能优势来管理…

dsa加训

refs: OI Wiki - OI Wiki (oi-wiki.org) 1. 枚举 POJ 2811 熄灯问题 refs : OpenJudge - 2811:熄灯问题 如果要枚举每个灯开或者不开的情况&#xff0c;总计2^30种情况&#xff0c;显然T。 不过我们可以发现&#xff1a;若第i行的某个灯亮了&#xff0c;那么有且仅有第i行和第…

Win10使用VS Code远程连接Ubuntu服务器时遇到SSH公钥错误的解决方案

在使用Windows 10上的Visual Studio Code&#xff08;VS Code&#xff09;远程连接Ubuntu 20.04服务器时&#xff0c;遇到了以下错误&#xff1a; 错误的原因 这个错误消息表明&#xff0c;SSH 客户端检测到远程主机的 ECDSA 公钥已更改。可能是由于以下原因之一&#xff1a…

组蛋白乳酸化 | 调控蛋白Writers、Erasers和Readers

组蛋白修饰的调控可以被归类为三类蛋白&#xff1a;Writers&#xff08;写入者&#xff09;、Erasers&#xff08;擦除者&#xff09;和Readers&#xff08;读取者&#xff09;。Writers是负责在组蛋白上添加修饰基团的蛋白&#xff0c;包括乙酰化、甲基化等修饰。Erasers则是负…

学习记录——day17 数据结构 队列 链式队列

队列介绍 1、队列也是操作受限的线性表:所有操作只能在端点处进行&#xff0c;其删除和插入必须在不同端进行 2、允许插入操作的一端称为队尾&#xff0c;允许删除操作的一端称为队头 3、特点:先进先出(FIFO) 4、分类&#xff1a; 顺序存储的栈称为顺序栈 链式存储的队列&a…

IP数据报结构详解:从基础到进阶

目录 IP数据报的格式 IP数据报首部的固定部分 IP数据报首部的可变部分 实例分析&#xff1a;数据报的分片 生存时间&#xff08;TTL&#xff09;与协议 首部检验和 总结 在网络通信中&#xff0c;IP数据报是至关重要的基本单元。本文将带您深入了解IP数据报的格式及其各个…

【Python】使用抓包Fiddler软件,网络查询 遇到“您的连接不是私密连接”的问题的解决方法

使用Fiddler抓包软件很久&#xff0c;忽然发现网络使用有问题&#xff0c;一点开浏览器就会出现类似下面的页面&#xff1a; 检查了网络情况发现不是网络的问题&#xff0c;也排除了封号的可能。发现只要把抓包软件Fiddler关闭以后就没问题了&#xff0c;就知道问题是出在软件…

国产光电耦合器2024年的机遇与挑战

随着科技的飞速发展&#xff0c;2024年对于国产光电耦合器行业来说&#xff0c;无疑是充满机遇与挑战的一年。本文将深入探讨该行业在技术创新、市场竞争、5G时代、新兴应用领域和国际市场拓展方面的现状及未来前景。 技术创新的黄金期 物联网和人工智能技术的迅猛发展&#x…

Java之集合底层-数据结构

Java集合之数据结构 1 概述 数据结构是计算机科学中研究数据组织、存储和操作的一门学科。它涉及了如何组织和存储数据以及如何设计和实现不同的数据操作算法和技术。常见的据结构有线性数据结构&#xff08;含数组、链表、栈和队列等&#xff09;&#xff0c;非线性数据结构…

睿考网:中级会计师考试各科分值是多少?

中级会计考试是会计领域的一个重要考核&#xff0c;考试题型包含多种&#xff1a;单选题、多选题、判断题、计算分析题和综合题。这些不同的题型不仅覆盖了广泛的知识点&#xff0c;而且各自的评分标准也是不一样的。为了帮助大家更全面地掌握各类题型的得分规则&#xff0c;睿…

解决:Nacos无法获取远程配置数据,导致项目启动各种配置异常

解决&#xff1a;Nacos无法获取远程配置数据&#xff0c;导致项目启动各种配置异常 一问题描述&#xff1a;1.项目pom依赖版本&#xff1a;2.bootstrap.yml配置信息3.远程配置&#xff1a;默认public命名空间4.启动报异常&#xff0c;显示没有配置数据源&#xff0c;实际远程已…

韦东山嵌入式linux系列-查询方式的按键驱动程序_编写框架

1 LED 驱动回顾 对于 LED&#xff0c; APP 调用 open 函数导致驱动程序的 led_open 函数被调用。在里面&#xff0c;把 GPIO 配置为输出引脚。安装驱动程序后并不意味着会使用对应的硬件&#xff0c;而 APP 要使用对应的硬件&#xff0c;必须先调用 open 函数。所以建议在驱动…

Adobe Character Animator (CH) 安装包软件下载

目录 一、软件简介 二、下载与安装 1. 下载 2. 安装 三、注意事项 1. 硬件要求 2. 兼容性 四、功能介绍 1. 实时面部捕捉 2. 实时语音同步 3. 动作捕捉 五、快捷键操作 CH 提供了一系列快捷键以方便用户快速操作。以下是一些常用的快捷键&#xff1a; 一、软件简介…

django电商用户消费数据分析系统-计算机毕业设计源码20891

摘 要 随着电子商务的快速发展&#xff0c;电商平台积累了大量的用户消费数据。为了更好地理解用户行为、优化商品结构和提升用户体验&#xff0c;本文设计并实现了一个基于Django框架的电商用户消费数据分析系统。 该系统包含后台首页、系统用户&#xff08;管理员&#xf…

Hive分布式SQL计算平台

Hive分布式SQL计算平台 一、Hive 概述二、Hive架构三、Hive客户端 1、Hive有哪些客户端可以使用2、Hive第三方客户端 四、Hive使用语法 1、数据库操作2、内部表&#xff0c;外部表3、数据的导入与导出4、分区表5、分桶表6、复杂类型操作7、数据抽样8、Virtual Columns 虚拟列9…

Samtec技术科普小课堂 | 一文入门射频连接器~

【摘要/前言】 在本文中&#xff0c;我们将回到基础知识&#xff0c;了解一下什么是射频连接器。如果您是信号完整性专家&#xff0c;请点击阅读原文访问我们的网站视频&#xff0c;通过我们的网络研讨会视频了解教科书上可能找不到的知识。 如果您是电气工程领域的新手&#…

pycharm git 新建备忘

git 提交时出现如下错误&#xff1a; Committer identity unknown *** Please tell me who you are. Run git config --global user.email "youexample.com" git config --global user.name "Your Name" to set your accounts default identity. Omit…