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映射 -- 一对多关联映射
映射原理 一对多关联映射和多对一关联映射的映射原理是一样一样的,所以说嘛,知识都是相通的,一通百通,为什么说一对多关联映射和多对一关联映射是一样的呢?因为她们都是在多的一端加入一个 ...
随机推荐
- jQuery控制TR的显示隐藏
网上有很多,这里介绍三种: 第一种方法,就是使用id,这个方法可以在生成html的时候动态设置tr的id,也是用得最多最简单的一种,如下: <table> <tr><td ...
- SDUST 作业10 Problem I 液晶显示
Description 你的朋友刚买了一台新电脑,他以前用过的最强大的计算工具是一台袖珍计算器.现在,看着自己的新电脑,他有点失望,因为他更喜欢计算器上的LC显示器.所以,你决定写一个LC显示风格的程 ...
- ip的正则表达式 完美版
IP地址的长度为32位2进制,分为4段,每段8位,用十进制数字表示,每段数字范围为0~255,段与段之间用英文句点“.”隔开.例如:IP地址为10.0.0.100. 分析IP地址的每组数特点:百位,十 ...
- 索尼MT27i Android2.3.7 线刷Android4.04
Author:KillerLegend From:http://www.cnblogs.com/killerlegend/p/3733150.html Date:2014.5.16 工具:Window ...
- php中的日期
1.在PHP中获取日期和时间 time()返回当前时间的 Unix 时间戳. getDate()返回日期/时间信息. gettimeofday()返回当前时间信息.date_sunrise()返回给定 ...
- highcharts 显示点值的效果
plotOptions: { line: { /* <s:if test='#request.rdflags=="point"'> <s:if test=&quo ...
- hdu 1412 {A} + {B}
题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=3282 {A} + {B} Description 给你两个集合,要求{A} + {B}.注:同一个集合 ...
- 关于跨域响应头Access-Control-Allow-Headers的一些说明
在跨域PreFlight的过程中,Options请求会返回一个响应头,如果服务器返回了这个响应头,则允许用户发起GET.POST.HEAD这三个简单方法的请求,如需发起其他形式的请求则需要显示地在 A ...
- django概述
一.django的特点 1.提供一体化的web解决方案,什么叫一体化:mvc 2.等你玩儿牛逼了可以拔插组件,换成自己顺手或者更牛逼的组件
- Ubuntu 12.04 Desktop安装XAMPP
1/打开终端 在Dash里搜索.打开Dash,在里面搜索“gnome-terminal”,就可以找到终端应用序.快捷键Ctrl+Alt+L也可以,不过如果是虚拟机的话可能会有问题. 如果想以后快捷打开 ...