【Hibernate步步为营】--(一对多映射)之双向关联
上篇文章讨论了单向关联的一对多映射,在一的一端维护双向的关系这种做法虽然能实现但是存在很多缺陷,首先生成很多多余的SQL语句,因为多的一端不维护关系,只有一的一端维护,在进行操作时一的一端会发出多余的update语句;其次,因为多的一端不知道一的一端存在,所以在保存多的一端时如果外键为null值,并且在设计数据库时关系字段设为非空,则将无法保存数据。因为单向关联一对多存在很多缺点那就没有其它的办法了吗,可以采用双向关联来优化。
一、一对多双向关联
这里继续采用上篇文章的学生和班级作为示例,班级和学生之间是一对多的关系,一个班级中拥有多名学生,和上篇文章不同的是这里的关系是双向的,也就是一的一端和多的一端同时维护关联关系,所以它的对象图如下:
对应的关系模型图没有太大的变化,因为它们之间的关系是双向的,所以在关系模型中两端同时维护关联关系,映射到关系模型中如下图所示:
在一对多的单向关联中映射文件只需要在一的一端进行特殊配置就可以,使用<one-to-many>配置,并在对象模型中使用set迭代器来设置外联的对象模型,但是不同的是在双向的关联中需要在多的一端添加对应的另一端的外键关联,这时候就必须在多的一端使用<many-to-one>的关联关系来标明这种双向性。
1、映射
这里还使用Classes和Student来做示例,在Classes一端的内容和上文相同不会发生变换,但是多的一端Student的配置会发生变化,也就是在映射文件中需要添加<many-to-one>标签。
Student.hbm.xml映射文件配置需要添加外键列<many-to-one>标签,并且该列的名称要和Classes.hbm.xml的外键列的名称一致,具体如下代码:
- <?xml version="1.0"?>
- <!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.src.hibernate.Student" table="t_student">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <!-- 在多的一端Student中添加一行新的Classes列 ,并且列的名称要和Classes.hbm.xml的列明相同-->
- <many-to-one name="classes" column="classesid"></many-to-one>
- </class>
- </hibernate-mapping>
Classes.hbm.xml映射文件的配置和上篇文章相同,需要注意的是在Classes.java文件中添加了set属性映射对应了Student对象,所以在映射文件中需要添加set标签来指示为对象模型中使用了set迭代器,具体配置如下代码:
- <?xml version="1.0"?>
- <!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.src.hibernate.Classes" table="t_classes">
- <id name="id">
- <generator class="native"/>
- </id>
- <property name="name"/>
- <set name="students" inverse="true">
- <key column="classesid"></key>
- <one-to-many class="com.src.hibernate.Student"></one-to-many>
- </set>
- </class>
- </hibernate-mapping>
2、类
映射文件的配置是直接对应着类来的,所以有了映射文件就能够写出相应的类,相同的有了类就能够知道对应的映射文件如何编写,那来看看相应的类代码如何编写。
Student.java类,需要在类中添加关联的班级对象属性,在加载Student时能获得Classes的相关信息。
- package com.src.hibernate;
- public class Student {
- //关联的班级对象
- private Classes classes;
- public Classes getClasses() {
- return classes;
- }
- public void setClasses(Classes classes) {
- this.classes = classes;
- }
- //学生id
- private int id;
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- //学生姓名
- private String name;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- }
Classes.java文件具体代码内容见上篇文章,这里就不在详述。
有了对象模型接下来生成关系模型,生成的SQL语句如下:
- alter table t_student drop foreign key FK4B907570FC588BF4
- drop table if exists t_classes
- drop table if exists t_student
- create table t_classes (id integer not null auto_increment, name varchar(255), primary key (id))
- create table t_student (id integer not null auto_increment, name varchar(255), classesid integer, primary key (id))
- alter table t_student add index FK4B907570FC588BF4 (classesid), add constraint FK4B907570FC588BF4 foreign key (classesid) references t_classes (id)
3、数据操作
建立表结构后来编写测试方法来验证数据的操作,首先来看看数据的插入,向表结构中插入数据,写入数据时会有两种情况,一种是首先创建一个Classes对象,并将对象写入到数据库中,然后创建Student对象,在Classes对象中添加学生对象;另外一种是先创建学生对象,并将学生对象写入数据库中,然后创建Classes对象将学生对象加入到Classes对象中,这两种类型的操作最后是不相同的,来对比下。
3.1 先写班级后写学生
先把班级写入到数据库中后,Classes对象进入了Transient状态,并在数据库中有了一行,这时再写Student对象,Student对象会查找对应的Classes的主键将其写入到表中,所以此时关系模型中的数据都是非空的,保存的代码如下:
- public void testSave(){
- Session session=null;
- try{
- //创建session对象
- session=HibernateUtils.getSession();
- //开启事务
- session.beginTransaction();
- //创建班级对象,将班级对象写入到数据库中
- Classes classes=new Classes();
- classes.setName("class");
- session.save(classes);
- //创建学生1对象,将学生对象写入到数据库中
- Student student1=new Student();
- student1.setName("zhangsan");
- student1.setClasses(classes);
- session.save(student1);
- //创建学生2对象,将学生对象写入到数据库中
- Student student2=new Student();
- student2.setName("lisi");
- student2.setClasses(classes);
- session.save(student2);
- session.getTransaction().commit();
- }catch(Exception e){
- e.printStackTrace();
- session.getTransaction().rollback();
- }finally{
- HibernateUtils.closeSession(session);
- }
- }
对应的写入数据库中的信息列表如下图:
3.2 先写学生后写班级
先把学生写入到数据库中此时因为学生表需要获取对应的班级列的主键信息,但是因为班级信息转化到Transient状态,所以在写入学生信息时会有null值,代码如下:
- public void testSave(){
- Session session=null;
- try{
- //创建session对象
- session=HibernateUtils.getSession();
- //开启事务
- session.beginTransaction();
- //创建学生1对象,将学生对象写入到数据库中
- Student student1=new Student();
- student1.setName("zhangsan");
- session.save(student1);
- //创建学生2对象,将学生对象写入到数据库中
- Student student2=new Student();
- student2.setName("lisi");
- session.save(student2);
- //创建班级对象
- Classes classes=new Classes();
- classes.setName("Classes");
- //设置学生集合
- Set students=new HashSet();
- students.add(student1);
- students.add(student2);
- //将学生集合写入到Classes中
- classes.setStudents(students);
- //可以成功保存数据
- //但是会发出多余的update语句来维持关系,因为是一对多的原因
- session.save(classes);
- session.getTransaction().commit();
- }catch(Exception e){
- e.printStackTrace();
- session.getTransaction().rollback();
- }finally{
- HibernateUtils.closeSession(session);
- }
- }
写入后对应的数据库视图如下:
对比两种写入操作,因为两个写入的先后顺序不同所以出现了不同的结果,但因为是双向的关联关系所以在写入时并不会发生异常。
4、读取操作
- public void testLoad1(){
- Session session=null;
- try{
- session=HibernateUtils.getSession();
- session.beginTransaction();
- //通过班级读取学生信息
- Classes classes=(Classes)session.load(Classes.class,1);
- System.out.println("classes.name="+classes.getName());
- Set students=classes.getStudents();
- for(Iterator iter=students.iterator();iter.hasNext();){
- Student student=(Student)iter.next();
- System.out.println("student.name="+student.getName());
- }
- //通过学生信息读取班级信息
- Student stu=new Student();
- stu=(Student)session.load(Student.class, 1);
- System.out.println("通过学生加载班级信息Classes.id= "+stu.getClasses().getId());
- session.getTransaction().commit();
- }catch(Exception e){
- e.printStackTrace();
- session.getTransaction().rollback();
- }finally{
- HibernateUtils.closeSession(session);
- }
- }
运行上面的测试语句,生成的对应的语句信息如下:
- Hibernate: select classes0_.id as id1_0_, classes0_.name as name1_0_ from t_classes classes0_ where classes0_.id=?
- classes.name=class
- Hibernate: select students0_.classesid as classesid1_, students0_.id as id1_, students0_.id as id0_0_, students0_.name as name0_0_, students0_.classesid as classesid0_0_ from t_student students0_ where students0_.classesid=?
- student.name=lisi
- student.name=zhangsan
- 通过学生加载班级信息Classes.id= 1
结语
【Hibernate步步为营】--(一对多映射)之双向关联相关推荐
- hibernate 一对多(one-to-many)双向关联
一对多(one-to-many)双向关联实例(Department- Employee) 这里的一对多双向关联是在域模型(实体对象模型)上的概念,在关系数据库中,只存在外键参照关系,而且总是由&quo ...
- hibernate的一对多双向关联映射----客户和订单关系
客户和订单关系:客户是一的一方,订单是多的一方. customer表: CREATE TABLE `customer` ( `ID` int(4) NOT NULL AUTO_INCREMENT , ...
- 【Hibernate步步为营】--(一对多映射)之单向关联
上篇文章讨论了双向关联的一对一映射,用了两个章节.主要是从主键和外键两种关联映射展开具体讨论.双向关联的映射须要在两个映射文件里分别加入相互的相应关系,斌刚在相应的类中加入相应的关联类的属性,这样在一 ...
- Hibernate(九)一对多双向关联映射
上次的博文Hibernate从入门到精通(八)一对多单向关联映射中,我们讲解了一下一对多单向映射的相关 内容,这次我们讲解一下一对多双向映射的相关内容. 一对多双向关联映射 一对多双向关联映 射,即在 ...
- [置顶] Hibernate从入门到精通(十一)多对多双向关联映射
上次我们在中Hibernate从入门到精通(十)多对多单向关联映射讲解了一下多对多单向关联映射,这次我们讲解一下七种映射中的最后一种多对多双向关联映射. 多对多双向关联映射 按照我们之前的惯例,先看一 ...
- Hibernate映射详解(二)--多对一,一对多单向,一对多双向关联映射
在我们以前的数据库设计中,设计表就不是一件轻松的事情.多种事物之间往往都是有这样那样的关系的.那怎样设计表格,才能既将事情描述明白,又能使数据库设计的比较合理呢?那里我们提供了好多规范,好多约束来满足 ...
- Hibernate一对多单向关联和双向关联映射方法及其优缺点
一对多关联映射和多对一关联映射实现的基本原理都是一样的,既是在多的一端加入一个外键指向一的一端外键,而主要的区别就是维护端不同. 它们的区别在于维护的关系不同: 一对多关联映射是指在加载一的一端数据的 ...
- myeclipse hbm2java_myeclipse试用小记----Hibernate多对一双向关联(2)
myeclipse试用小记----Hibernate多对一双向关联(2) 在上篇文章"myeclipse试用小记----Hibernate多对一单向关联(1)"中,讲到了" ...
- hibernate的映射之二(一对多双向关联)
hibernate的一对多双关联 一对多关联映射:在多的一端加入一个外键指向一的一端,它维护的关系是一指向多 hihernate一对多关联映射(双向Dept<----->Emp) 一对多双 ...
最新文章
- 启动oracle的三过程
- es6 取数组的第一个和最后一个_ES6:解构——JavaScript 从数组和对象中提取数据的优雅方法...
- $_FILE函数总结
- Vuejs 插值操作
- 朋友圈发图多大不会被压缩_类风湿会不会引发肾病?会!本文告诉你对内脏的伤害有多大...
- 洛谷 P4568 [JLOI2011]飞行路线
- 阶段1 语言基础+高级_1-3-Java语言高级_09-基础加强_第2节 反射_7_反射_Class对象功能概述...
- 关于数据中台,扎克伯格和马化腾做法不同
- 天龙八部linux 换win,Linux从菜鸟到大师之天龙八部 第三部文本编辑处理.doc
- 产品经理应该懂得的一点技术
- linux环境下安装mencoder转码工具
- 【UWB定位】 - DWM1000模块调试简单心得 - 1
- GDELT数据介绍/GDELT项目官方文档翻译
- Gitlab CI/CD 简单介绍
- 三个臭皮匠赛过诸葛亮!白话Blending和Bagging
- android手机拍摄视频格式,怎么用手机给自己拍摄的视频加上字幕?安卓手机视频编辑器给视频加字幕的方法...
- 杀戮尖塔(Slay the Spire) mod制作 --- 1
- c# 计算圆锥的体积_急求用c#计算圆柱体和圆锥体的体积的代码,下面是要求:...
- 网易云音乐API,的调用方法 ,vue项目中(在本地使用)
- mac book pro 最全快捷键
热门文章
- 【Android 逆向】Android 中常用的 so 动态库 ( libdvm.so | libart.so | libandroid_runtime.so | libandroidfw.so )
- 【Android 应用开发】动态权限管理示例 ( 使用原生代码实现 | 申请权限 | 判定权限申请结果 | 判定 “ 不再询问 “ 情况 )
- 【错误记录】Android Studio 编译信息输出乱码
- 【Android 热修复】热修复原理 ( 热修复框架简介 | 将 Java 字节码文件打包到 Dex 文件 )
- 【计算理论】上下文无关语法 ( 代数表达式 | 代数表达式示例 | 确定性有限自动机 DFA 转为 上下文无关语法 )
- 【UML 建模】UML建模语言入门 -- 用例视图详解 用例视图建模实战
- 2016-8-18晨型养成第三天
- CentOS下php安装mcrypt扩展
- JavaScript的预编译及执行顺序
- canvas-应用大全