03.Hibernate一对多关联
前言:在域模型中,类与类之间最普遍的关系就是关联关系,在UML语言中关联关系是有方向的。在数据库中表与表之间也会有关联关系,本节介绍通过Hibernate映射一对多的关联关系,这是一种最普遍的关联关系。
1.数据库表的一对多关联关系

CREATE TABLE tb_class(id bigint NOT NULL auto_increment COMMENT 'ID',no varchar(10) NOT NULL COMMENT '班级编号',name varchar(50) NOT NULL COMMENT '班级名称',PRIMARY KEY (id)) COMMENT '班级信息表';CREATE TABLE tb_student(id bigint NOT NULL auto_increment COMMENT 'ID',no varchar(10) NOT NULL COMMENT '学号',name varchar(50) NOT NULL COMMENT '姓名',sex char(1) NOT NULL COMMENT '性别',birthday datetime COMMENT '出生日期',class_id bigint NOT NULL COMMENT '班级ID',PRIMARY KEY (id)) COMMENT = '学生信息表';
ALTER TABLE tb_student ADD CONSTRAINT fk_tb_student_tb_class_1 FOREIGN KEY (class_id) REFERENCES tb_class (id);
package model;public class ClassInfo{private Long id;private String no;private String name;private List<Student> students = new ArrayList<Student>();//省略setter、getter、toString...}
package model;import java.sql.Date;public class Student{private Long id;private String no;private String name;private String sex;private Date birthday;private ClassInfo classInfo;//省略setter、getter、toString...}
2.建立多对一的单向关联关系
- <hibernate-mapping package="model">
<class name="ClassInfo" table="tb_class"><id name="id"><generator class="native"></generator></id><property name="no" column="no"/><property name="name" column="name"/></class></hibernate-mapping>
<hibernate-mapping package="model"><class name="Student" table="tb_student"><id name="id"><generator class="native"></generator></id><property name="no" column="no"/><property name="name" column="name"/><property name="sex" column="sex"/><property name="birthday" column="birthday"/><many-to-one name="classInfo" column="class_id" class="model.ClassInfo" lazy="false"/></class></hibernate-mapping>
- name:设置需要映射的实体类属性名。
- column:设置和实体类的属性对应的表的外键。注意:Hibernate默认此外键参照的是对应实体类的主键,在这里即是class_id参照classInfo实体类的id。
- class:设置需要映射的实体类属性的类型。
- lazy:是否使用懒加载,使用懒加载Hibernate会在你第一次访问关联的对象时才会去数据库取数据,不会立即去数据库检索与此对象关联的所有数据。
public static void main(String[] args){Student student;Configuration cfg = new Configuration();cfg.configure();ServiceRegistry sr = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();SessionFactory sf = cfg.buildSessionFactory(sr);System.out.println("连接数据库");Session session = sf.openSession();student=(Student) session.get(Student.class, new Long(1));System.out.println(student);System.out.println(student.getClassInfo());session.close();System.out.println("关闭数据库");System.exit(0);}
<hibernate-configuration><session-factory><!-- 省略其他...,设置实体类到数据库的映射文件 --><mapping resource="model/ClassInfo.hbm.xml"/><mapping resource="model/Student.hbm.xml"/></session-factory></hibernate-configuration>
连接数据库Hibernate:selectstudent0_.id as id1_2_0_,student0_.no as no2_2_0_,student0_.name as name3_2_0_,student0_.sex as sex4_2_0_,student0_.birthday as birthday5_2_0_,student0_.class_id as class_id6_2_0_fromtb_student student0_wherestudent0_.id=?Hibernate:selectclassinfo0_.id as id1_1_0_,classinfo0_.no as no2_1_0_,classinfo0_.name as name3_1_0_fromtb_class classinfo0_whereclassinfo0_.id=?Student [id=1, no=000001, name=学生1, sex=男, birthday=2015-01-27]ClassInfo [id=22, no=000022, name=班级22]关闭数据库
3.建立一对多的单向关联关系
<hibernate-mapping package="model"><class name="Student" table="tb_student"><id name="id"><generator class="native"></generator></id><property name="no" column="no"/><property name="name" column="name"/><property name="sex" column="sex"/><property name="birthday" column="birthday"/></class></hibernate-mapping>
<hibernate-mapping package="model"><class name="ClassInfo" table="tb_class"><id name="id"><generator class="native"></generator></id><property name="no" column="no"/><property name="name" column="name"/><bag name="students"><key column="class_id"/><one-to-many class="model.Student"/></bag></class></hibernate-mapping>
- bag name:设置需要映射的实体类属性名。
- key column:设置和实体类的属性对应的表的外键。注意:Hibernate默认此外键参照的是对应实体类的主键,在这里即是class_id参照ClassInfo实体类的id。
- one-to-many class:设置需要映射的实体类属性的类型。
public static void main(String[] args){ClassInfo classInfo;Configuration cfg = new Configuration();cfg.configure();ServiceRegistry sr = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();SessionFactory sf = cfg.buildSessionFactory(sr);System.out.println("连接数据库");Session session = sf.openSession();classInfo =(ClassInfo) session.get(ClassInfo.class, new Long(1));System.out.println(classInfo);System.out.println(classInfo.getStudents().size());System.out.println(classInfo.getStudents().get(0));session.close();System.out.println("关闭数据库");System.exit(0);}
连接数据库Hibernate:selectclassinfo0_.id as id1_1_0_,classinfo0_.no as no2_1_0_,classinfo0_.name as name3_1_0_fromtb_class classinfo0_whereclassinfo0_.id=?ClassInfo [id=1, no=000001, name=班级1]Hibernate:selectstudents0_.class_id as class_id6_1_0_,students0_.id as id1_2_0_,students0_.id as id1_2_1_,students0_.no as no2_2_1_,students0_.name as name3_2_1_,students0_.sex as sex4_2_1_,students0_.birthday as birthday5_2_1_,students0_.class_id as class_id6_2_1_fromtb_student students0_wherestudents0_.class_id=?35Student [id=24, no=000024, name=学生24, sex=女, birthday=2015-01-27]关闭数据库
4.建立一对多的双向关联关系
<hibernate-mapping package="model"><class name="Student" table="tb_student"><id name="id"><generator class="native"></generator></id><property name="no" column="no"/><property name="name" column="name"/><property name="sex" column="sex"/><property name="birthday" column="birthday"/><many-to-one name="classInfo" column="class_id" class="model.ClassInfo" lazy="false"/></class></hibernate-mapping>
<hibernate-mapping package="model"><class name="ClassInfo" table="tb_class"><id name="id"><generator class="native"></generator></id><property name="no" column="no"/><property name="name" column="name"/><bag name="students" inverse="false" cascade="none" lazy="false"><key column="class_id"/><one-to-many class="model.Student"/></bag></class></hibernate-mapping>
public static void main(String[] args){Student student;Configuration cfg = new Configuration();cfg.configure();ServiceRegistry sr = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();SessionFactory sf = cfg.buildSessionFactory(sr);System.out.println("连接数据库");Session session = sf.openSession();student=(Student) session.get(Student.class, new Long(1));System.out.println(student);System.out.println(student.getClassInfo());System.out.println(student.getClassInfo().getStudents().size());session.close();System.out.println("关闭数据库");System.exit(0);}
连接数据库Hibernate:selectstudent0_.id as id1_2_0_,student0_.no as no2_2_0_,student0_.name as name3_2_0_,student0_.sex as sex4_2_0_,student0_.birthday as birthday5_2_0_,student0_.class_id as class_id6_2_0_fromtb_student student0_wherestudent0_.id=?Hibernate:selectclassinfo0_.id as id1_1_0_,classinfo0_.no as no2_1_0_,classinfo0_.name as name3_1_0_fromtb_class classinfo0_whereclassinfo0_.id=?Hibernate:selectstudents0_.class_id as class_id6_1_0_,students0_.id as id1_2_0_,students0_.id as id1_2_1_,students0_.no as no2_2_1_,students0_.name as name3_2_1_,students0_.sex as sex4_2_1_,students0_.birthday as birthday5_2_1_,students0_.class_id as class_id6_2_1_fromtb_student students0_wherestudents0_.class_id=?Student [id=1, no=000001, name=学生1, sex=男, birthday=2015-01-27]ClassInfo [id=22, no=000022, name=班级22]38关闭数据库
public static void main(String[] args){ClassInfo classInfoNew;ClassInfo classInfo;Student studentNew;Student student;Configuration cfg = new Configuration();cfg.configure();ServiceRegistry sr = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();SessionFactory sf = cfg.buildSessionFactory(sr);System.out.println("连接数据库");Session session = sf.openSession();Transaction transaction = session.beginTransaction();try{classInfoNew = new ClassInfo();classInfoNew.setName("测试Class");classInfoNew.setNo("000");studentNew = new Student();studentNew.setName("测试Student");studentNew.setNo("000");studentNew.setSex("男");studentNew.setBirthday(new Date(1993, 6, 11));classInfoNew.getStudents().add(studentNew);studentNew.setClassInfo(classInfoNew);session.save(classInfoNew);//一定要先保存classInfo不然会报错,因为数据库约束了class_id为NOT NULLsession.save(studentNew);student = (Student) session.get(Student.class, new Long(1));student.setClassInfo(classInfoNew);//执行update维护关系classInfoNew.getStudents().add(student);//执行update维护关系,update语句数受关联对象数影响,此处的update会有重复!transaction.commit();}catch (Exception e){transaction.rollback();System.out.println("错误:" + e);}finally{session.close();System.out.println("关闭数据库");}System.exit(0);}
连接数据库Hibernate:insert into tb_class (no, name) values (?, ?)Hibernate:insert into tb_student (no, name, sex, birthday, class_id) values (?, ?, ?, ?, ?)Hibernate:- ........ 省略若干获取关联对象的查询语句
Hibernate:update tb_student set no=?, name=?, sex=?, birthday=?, class_id=? where id=?Hibernate:update tb_student set class_id=? where id=?Hibernate:update tb_student set class_id=? where id=?关闭数据库
- <bag name="students" lazy="false" inverse="true">
<key column="class_id"/><one-to-many class="model.Student"/></bag>
Hibernate:update tb_student set no=?, name=?, sex=?, birthday=?, class_id=? where id=?
- 在映射一对多的双向关联关系时,应该在“one”放把bag元素的inverse属性设置为true,这样可以减少不必要的SQL执行,提高应用程序的性能。
- 在建立两个对象的双向关联时,应该同时修改关联两端对象的相应属性:student.setClassInfo(classInfoNew); classInfoNew.getStudents().add(student);
- <many-to-one name="classInfo" column="class_id" class="model.ClassInfo" lazy="false" cascade="save-update"/>
连接数据库Hibernate:insert into tb_class (no, name) values (?, ?)Hibernate:insert into tb_student (no, name, sex, birthday, class_id) values (?, ?, ?, ?, ?)关闭数据库
<bag name="students" lazy="false" inverse="true" cascade="delete"><key column="class_id"/><one-to-many class="model.Student" /></bag>
Hibernate:delete from tb_student where id=?Hibernate:delete from tb_class where id=?
<bag name="students" lazy="false" inverse="true" cascade="all-delete-orphan"><key column="class_id"/><one-to-many class="model.Student" /></bag>
5.建立一对多的双向自身关联关系

CREATE TABLE tb_area(id bigint NOT NULL auto_increment COMMENT 'ID',name varchar(50) NOT NULL COMMENT '地区名称',parent_id bigint COMMENT '所属区域ID',PRIMARY KEY (id)) COMMENT = '区域信息表';-- 外键约束可续选ALTER TABLE tb_area ADD CONSTRAINT fk_tb_area_1 FOREIGN KEY (parent_id) REFERENCES tb_area (id);
package model;import java.util.Set;public class Area{private Long id;private String name;private Area parentArea;private Set<Area> childAreas = new HashSet<Area>();// 省略setter、getter、toString...}
<hibernate-mapping package="model"><class name="Area" table="tb_area"><id name="id"><generator class="native"></generator></id><property name="name" column="name"/><many-to-one name="parentArea" column="parent_id" class="model.Area" cascade="save-update"/><set name="childAreas" inverse="true"><key column="parent_id"/><one-to-many class="model.Area"/></set></class></hibernate-mapping>
public static void main(String[] args){Configuration cfg = new Configuration();cfg.configure();ServiceRegistry sr = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();SessionFactory sf = cfg.buildSessionFactory(sr);System.out.println("连接数据库");Session session = sf.openSession();Transaction transaction = session.beginTransaction();try{Area area1=new Area();Area area2=new Area();Area area3=new Area();Area area4=new Area();Area area5=new Area();area1.setName("中国");area2.setName("湖北省");area3.setName("湖南省");area4.setName("武汉市");area5.setName("仙桃市");area2.setParentArea(area1);area3.setParentArea(area1);area4.setParentArea(area2);area5.setParentArea(area2);session.save(area3);session.save(area4);session.save(area5);transaction.commit();}catch (Exception e){transaction.rollback();System.out.println("错误:" + e);}finally{session.close();System.out.println("关闭数据库");}System.exit(0);}
连接数据库Hibernate: insert into tb_area (name, parent_id) values (?, ?)Hibernate: insert into tb_area (name, parent_id) values (?, ?)Hibernate: insert into tb_area (name, parent_id) values (?, ?)Hibernate: insert into tb_area (name, parent_id) values (?, ?)Hibernate: insert into tb_area (name, parent_id) values (?, ?)关闭数据库
6.改进持久化类
area1.setName("中国");area2.setName("湖北省");area2.setParentArea(area1);area1.getChildAreas().add(area2);
public Area addChildArea(Area area){if (area != null){if (this.childAreas == null){this.childAreas = new HashSet<Area>();}//删除原有的关系if (area.getParentArea() != null){area.getParentArea().getChildAreas().remove(area);}//设置当前关系this.childAreas.add(area);area.setParentArea(this);}return this;}
area1.setName("中国");area2.setName("湖北省");area3.setName("湖南省");area1.addChildArea(area2).addChildArea(area3);
7.一对多、多对一相关配置详细说明
<many-to-one name="PropertyName"access="field|property"column="TableColumn"class="ClassName"property-ref="PropertyNameFromAssociatedClass"foreign-key="foreign-key"formula="arbitrary SQL expression"update="true|false"insert="true|false"not-null="true|false"unique="true|false"unique-key="unique-key"index="index_name"not-found="exception|ignore"outer-join="true|false"fetch="join|select"lazy="true|false"cascade="all|none|save-update|delete"optimistic-lock="true|false"embed-xml="true|false"entity-name="EntityName"node="element-name"/>
- name:实体类属性名。
- access:默认的实体类属性访问模式,取值为property表示访问getter、setter方法间接访问实体类字段,取值为field表示直接访问实体类字段(类成员变量)。
- column:对应的数据库字段名,此处的字段是外键字段。
- class:关联的类的名字,默认是通过反射得到属性类型。
- property-ref:指定关联类的一个属性,这个属性将会和本外键相对应。如果没有指定,会使用对方关联类的主键。
- foreign-key:关联的数据库外键名。
- formula:一个SQL表达式,定义了这个计算属性的值,计算属性没有和它对应的数据库字段。
- update:在update时是否含有此字段。
- insert:在insert时是否含有此字段。
- not-null:字段能否为空。
- unique:字段是否唯一。
- unique-key:为此字段创建唯一约束,属性值即为数据库唯一约束名,只影响自动生成的schema脚本。
- index:为此字段创建索引,属性值即为数据库索引名,只影响自动生成的schema脚本。
- not-found:指定外键引用的数据不存在时如何处理,ignore会将数据不存在作为关联到一个空对象(null)处理,默认为exception。
- outer-join:设置Hibernate是否使用外连接获取关联的数据,设置成true可以减少SQL语句的条数。
- fetch:参数指定了关联对象抓取的方式是select查询还是join查询,默认为select。fetch="join"等同于outer-join="true",fetch="select"等同于outer-join="false"。
- lazy:是否采用延迟加载策略。
- cascade:指明哪些操作会从父对象级联到关联的对象。
- optimistic-lock:指定这个属性在做更新时是否需要获得乐观锁定,默认为true。
- embed-xml:如果embed-xml="true",则对应于被关联实体或值类型的集合的XML树将直接嵌入拥有这些关联的实体的XML树中,默认值为true。
- entity-name:Hibernate3新增特性,用于动态模型(Dynamic Model)支持。Hibernate3允许一个类进行多次映射(前提是映射到不同的表)。
- node:配置说明。
<bag name="EntityClassName"access="field|property|ClassName"collection-type="collection-type"schema="schema"catalog="catalog"check="arbitrary sql check condition"table="TableName"subselect="SQL expression"where="arbitrary sql where condition"optimistic-lock="false|true"inverse="false|true"fetch="join|select"batch-size="5"cascade="all|none|save-update|delete"lazy="false|true"mutable="false|true"outer-join="false|true"order-by="arbitrary sql order by condition"embed-xml="false|true"persister="PersisterClass"node="element-name"/>
- name:实体类属性名。
- access:默认的实体类属性访问模式,取值为property表示访问getter、setter方法间接访问实体类字段,取值为field表示直接访问实体类字段(类成员变量)。
- collection-type:
- schema:数据库schema。
- catalog:数据库catalog。
- check:这是一个SQL表达式,用于为自动生成的schema添加多行约束检查。
- table:此集合里的实体类对应的数据库表名。
- subselect:一个SQL子查询,它将一个不可变并且只读的实体映射到一个数据库的子查询。
- where:一个SQL查询的where条件,获取这个关联类的对象时会一直增加这个条件。
- optimistic-lock:指定这个属性在做更新时是否需要获得乐观锁定,默认为true。
- inverse:当设置inverse="true"时,Hibernate将根此集合里的实体类类型的关联属性维护关联关系,默认值false。
- fetch:参数指定了关联对象抓取的方式是select查询还是join查询,默认为select。fetch="join"等同于outer-join="true",fetch="select"等同于outer-join="false"。
- batch-size:用于设置批次操作的SQL语句的数量,默认为1。
- cascade:指明哪些操作会从父对象级联到关联的对象。
- lazy:是否采用延迟加载策略。
- mutable:此集合里的实体类是否会发生改变,如果类实例对应的数据库表记录不会发生更新,可将其设为false,适用于单纯的Insert操作不使用update操作。
- outer-join:设置Hibernate是否使用外连接获取关联的数据,设置成true可以减少SQL语句的条数。
- order-by:一个SQL查询的order by条件,获取这个关联类的对象时会一直增加这个条件。
- embed-xml:如果embed-xml="true",则对应于被关联实体或值类型的集合的XML树将直接嵌入拥有这些关联的实体的XML树中,默认值为true。
- persister:指定持久化实现类,通过指定持久化类,我们可以实现自定义的持久化方法。持久化类为ClassPersister接口的实现。
- node:配置说明。
<one-to-many class="ClassName"not-found="exception|ignore"embed-xml="true|false"entity-name="EntityName"node="element-name" />
- class:关联的类的名字,默认是通过反射得到属性类型。
- not-found:指定外键引用的数据不存在时如何处理,ignore会将数据不存在作为关联到一个空对象(null)处理,默认为exception。
- embed-xml:如果embed-xml="true",则对应于被关联实体或值类型的集合的XML树将直接嵌入拥有这些关联的实体的XML树中,默认值为true。
- entity-name:Hibernate3新增特性,用于动态模型(Dynamic Model)支持。Hibernate3允许一个类进行多次映射(前提是映射到不同的表)。
- node:配置说明。
03.Hibernate一对多关联的更多相关文章
- hibernate一对多关联映射
一对多关联映射 映射原理 一对多关联映射和多对一关联映射的映射原理是一致的,都是在多的一端加入一个外键,指向一的一端.关联关系都是由多端维护,只是在写映射时发生了变化. 多对一和一对多的区别 多对一和 ...
- Hibernate一对多关联映射的配置及其级联删除问题
首先举一个简单的一对多双向关联的配置: 一的一端:QuestionType类 package com.exam.entity; import java.util.Set; public class Q ...
- 转Hibernate 一对多关联的CRUD__@ManyToOne(cascade=(CascadeType.ALL))
一:Group和Users两个类 假定一个组里有n多用户,但是一个用户只对应一个用户组. 1.所以Group对于Users是“一对多”的关联关系@OneToMany Users对于Group是“多对一 ...
- Hibernate一对多关联
一对多双向关联关系:(Dept/Emp的案例) 既可以根据在查找部门时根据部门去找该部门下的所有员工,又能在检索员工时获取某个员工所属的部门. 步骤如下: 1.构建实体类(部门实体类加set员工集合) ...
- hibernate 一对多关联
package com.bjsxt.hibernate; import java.util.HashSet; import java.util.Set; import javax.persistenc ...
- hibernate 一对多双向关联 详解
一.解析: 1. 一对多双向关联也就是说,在加载班级时,能够知道这个班级所有的学生. 同时,在加载学生时,也能够知道这个学生所在的班级. 2.我们知道,一对多关联映射和多对一关联映射是一样的,都是在 ...
- Hibernate一对多单向关联和双向关联映射方法及其优缺点 (待续)
一对多关联映射和多对一关联映射实现的基本原理都是一样的,既是在多的一端加入一个外键指向一的一端外键,而主要的区别就是维护端不同.它们的区别在于维护的关系不同: 一对多关联映射是指在加载一的一端数据的同 ...
- (Hibernate进阶)Hibernate映射——一对多关联映射(七)
一对多关联映射 映射原理 一对多关联映射和多对一关联映射的映射原理是一致的,都是在多的一端加入一个外键,指向一的一端.关联关系都是由多端维护,只是在写映射时发生了变化. 多对一和一对多的区别 多对一和 ...
- 【SSH系列】Hibernate映射 -- 一对多关联映射
映射原理 一对多关联映射和多对一关联映射的映射原理是一样一样的,所以说嘛,知识都是相通的,一通百通,为什么说一对多关联映射和多对一关联映射是一样的呢?因为她们都是在多的一端加入一个 ...
随机推荐
- 【Zend Studio】10.6.0版本设置默认字体
1.打开Windows->Prefefences 2.找到General->Appearance->Colors and Fonts->Basic->Text Font- ...
- 带有×的EditText
代码: EditTextWithDel.java(直接复制): package com.sunday.customs; import com.example.customs.R; import and ...
- android里面线程睡眠事件使用方法
SystemClock.sleep(时间); 不用Thread.sleep()的原因:要抛异常,占用资源
- TE.TYCO.AMP/JST 现货资源备份库存表日志记录-2015-04-13
行号 品牌 料号 品名规格 库存 1 molex 09-65-2028 Molex 5273-02A 600 2 tyco 103648-2 AMP 03 MTE RCPT HSG SR RIB .1 ...
- VM虚拟机无法拖拽、粘贴、复制
VM无法从客户机拖放/复制文件到虚拟机的解决办法: 将这两项取消勾选,点击[确定].再次打开,勾选,点击[确定] 原因分析:可能是VM中默认是不支持该功能的,但是在配置窗体上确实默认打钩打上的. 依据 ...
- Oracle中建表空间以及用户
第一步:创建临时表空间 --创建临时表空间-- CREATE TEMPORARY TABLESPACE JSYCCS_TEMP ---'JSYCCS_TEMP'临时表空间名 TEMPFILE 'E ...
- 第二十一章 数据访问(In .net4.5) 之 序列化
1. 概述 应用程序间传递数据,需要先将数据对象转化为字符流或字节流的形式,然后接收端收到后再转化回原始的数据对象.这就是序列化与反序列化. 本章介绍 .net中的序列化与反序列化.序列化器的种类 以 ...
- button与submit
原文来自: http://blog.sina.com.cn/s/blog_693d183d0100uolj.html submit是button的一个特例,也是button的一种,它把提交这个动作自动 ...
- spring中scope(作用越)理解
今天总结了一下spring中作用域scope的用法.在spring中作用域通过配置文件形式的用法如下. <bean id="role" class="spring. ...
- 【6.24-AppCan移动开发大会倒计时】科大讯飞来了!
6.24 AppCan移动开发者大会进入倒计时,报名通道即将关闭! 50多家移动圈服务商将出席此次大会,讯飞开放平台也将作为参展商,为参会者带去前沿的语音技术.参会者可现场体验最新连续语音识别技术,识 ...