视频课程链接:http://edu.51cto.com/course/10747.html
一、Hibernate关联关系
1. 实体之间的关系
-
泛化关系
通过对象之间的继承来实现
-
关联关系
通过一个对象持有另一个对象的实例来实现
类与类之间最常见的关系就是关联关系
2. 关联关系
- 多对一
- 一对多
- 多对多
- 一对一
二、 多对一关系
1. 数据库表
create table t_dept(
id int primary key auto_increment,
name varchar(200)
)engine=Innodb default charset=utf8;
create table t_emp(
id int primary key auto_increment,
name varchar(200),
dept_id int,
foreign key(dept_id) references t_dept(id)
)engine=Innodb default charset=utf8;
多对一关系:多个员工在同一个部门
多方 ——> 员工
一方 ——> 部门
2. 配置方式
public class Emp implements Serializable {
private Integer id;
private String name;
private Dept dept; // 在多方定义一方的引用
//...
}
<!--
配置多对一关系
name:属性名
column:列名,外键列
class:属性的类型
-->
<many-to-one name="dept" column="dept_id" class="Dept"></many-to-one>
3. load/get查询
3.1 默认不查询关联数据
调用load/get查询对象时,默认只会查询当前类对象的表,不会查询关联表数据,称为关联数据的延迟加载
只有当第一次访问时才会进行数据的查询
Session session = HibernateUtil.getSession();
TransactionManager.beginTransaction();
Emp emp=(Emp) session.get(Emp.class, 1);
System.out.println(emp.getName());
System.out.println("--------------------------");
System.out.println(emp.getDept().getName());
TransactionManager.commit();
3.2 关于no Session问题
当执行下面代码时会出现no Session问题
Emp emp=null;
try {
Session session = HibernateUtil.getSession();
TransactionManager.beginTransaction();
emp=(Emp) session.get(Emp.class, 1);
System.out.println(emp.getName());
System.out.println("--------------------------");
TransactionManager.commit();
} catch (Exception e) {
TransactionManager.rollback();
e.printStackTrace();
} finally {
HibernateUtil.close();
}
System.out.println(emp.getDept().getName());
如何解决:
- 使用lazy="false"
- 使用fetch="join"
3.3 lazy配置
<!--
lazy:数据加载策略,可取值如下
false:立即加载关联数据
使用的是:两次查询
proxy:懒加载,以代理对象的方式进行延迟加载,默认值
no-proxy:懒加载, 该方式在编译时需要进行 字节码增强,否则和proxy没区别,很少使用
-->
<many-to-one name="dept" column="dept_id" class="Dept" lazy="no-proxy"></many-to-one>
3.4 fetch配置
<!--
fetch:抓取数据,可取值如下
join:立即加载,使用联接查询
使用的是:一次查询!!
注:此时会lazy配置无效,总是会立即加载关联数据,使用联接查询
select: 懒加载,会再执行一次selecg查询,默认值
-->
<many-to-one name="dept" column="dept_id" class="Dept" lazy="false" fetch="join"></many-to-one>
4. HQL单表查询
Emp emp= null;
try {
Session session = HibernateUtil.getSession();
TransactionManager.beginTransaction();
String hql="from Emp e where e.id=:id";
emp=(Emp) session.createQuery(hql).setInteger("id", 1).uniqueResult();
System.out.println(emp.getName());
System.out.println("----------------------");
TransactionManager.commit();
} catch (Exception e) {
TransactionManager.rollback();
e.printStackTrace();
} finally {
HibernateUtil.close();
}
System.out.println(emp.getDept().getName());
结论:对于HQL查询,lazy配置项有效,fetch配置项无效
5. HQL联接查询
5.1 left join
left join 只做联接操作,不查关联数据
根据lazy配置项,择机进行再次查询
5.2 left join fetch
left join fetch既做联接操作,也查关联数据
lazy配置项无效,总是使用联接查询,一次性将数据都查询出来
6. 增删改操作
6.1 基本操作
添加操作:
-
多方对象中的一方为null 或持久态 或 游离态
正常保存
-
多方对象中的一方为临时态
报错:
org.hibernate.TransientObjectException: object references an unsaved transient instanc
解决方法:
- 转换为持久态
- 配置cascade
6.2 cascade配置
<!--
cascade:级联操作
none:不进行级联操作,默认值
all:对所有操作进行级联操作(insert/delete/update)
save-update:执行保存和更新时进行级联操作
delete:执行删除时进行级联操作,主要用于一对多操作
-->
<many-to-one name="dept" column="dept_id" class="Dept" lazy="false" fetch="select" cascade="delete"></many-to-one>
三、一对多关系
1. 数据库表
create table t_class(
id int primary key auto_increment,
name varchar(200)
)engine=Innodb default charset=utf8;
create table t_student(
id int primary key auto_increment,
name varchar(200),
class_id int,
foreign key(class_id) references t_class(id)
)engine=Innodb default charset=utf8;
一对多关联:一个班级包含多名学生
一方 —— > 班级
多方 —— > 学生
2. 配置方式
<!--
配置一对多关系
name:属性名
table:属性关联的表
cascade:级联操作
none
all
save-update
delete:级联删除,删除的过程:
1.将所有引用一方数据的外键全部设置为null
将clazz对象对应的所有student的class_id都设置为null
2.删除对应set集合中的数据
将clazz中的set集合中的student删除
3.删除自己对应的数据
将clazz对象删除
-->
<set name="students" table="t_student" lazy="false" fetch="join" cascade="delete">
<!-- 关联的列,外键列,即关联表t_student中的哪个字段关联到t_class表 -->
<key column="class_id"></key>
<!-- 关联的类,属性的类型 -->
<one-to-many class="Student"/>
</set>
3. inverse配置
inverse 反转,一般指控制权的反转
<!--
inverse:反转
false:一方维护关联关系,默认值
true:由对方维护关联关系,即由多方维护,一方放弃对set集合的维护
-->
<set name="students" table="t_student" lazy="true" fetch="select" inverse="true">
4. 双向关联关系
4.1 双向一对多
前面我们配置的都是单向的关系:
- 单向多对一
-
单向一对多
如果同时配置了单向多对一和单向一对多,则就是双向一对多 或 双向多对一
inverse属性只能在<set>中配置,一般都会配置,因为关联关系由多方维护更合适
4.2 关于栈溢出的问题
重写实体类的toString()的方法
Exception in thread "main" java.lang.StackOverflowError
原因:因为重写了关联实体的toString()方法,并且包含关联属性,当调用toString()方法时会导航查询关联对象,关联对象双会导航查询对方,最终导致栈溢出。
解决:不要重写toString()方法,或者 重写toString()时不要包含关联属性
四、多对多关系
1. 数据库表
关系型数据库中如何实现多对多关系?
- 使用中间表,将多对多转换为两个一对多
- 用中间表来维护多对多关系和数据
create table t_stu(
id int primary key auto_increment,
name varchar(200)
)engine=Innodb default charset=utf8;
create table t_course(
id int primary key auto_increment,
name varchar(200)
)engine=Innodb default charset=utf8;
create table t_stu_course(
id int primary key auto_increment,
stu_id int,
course_id int,
foreign key(stu_id) references t_stu(id),
foreign key(course_id) references t_course(id)
)engine=Innodb default charset=utf8;
多对多关系:一个学生可以选择多门课程,一个课程也可以被多个学生选择
- 多方 ——> 学生
- 多方 ——> 课程
2. 配置方式
<!-- 配置多对多关系 -->
<set name="courses" table="t_stu_course">
<!-- 关联到当前类Stu的外键列 -->
<key column="stu_id"></key>
<!-- class指定属性的类型,column指定关系的列,外键列 -->
<many-to-many class="Course" column="course_id"></many-to-many>
</set>
3. 基本操作
五、一对一关系
1. 两种方式
- 基于外键的一对一映射
- 基于主键的一对一映射
2. 基于外键的一对一
2.1 数据库表
create table t_card(
id int primary key auto_increment,
name varchar(200)
)engine=Innodb default charset=utf8;
create table t_person(
id int primary key auto_increment,
name varchar(200),
card_id int unique, -- 通过指定unique将多对一关系变成一对一关系
foreign key(card_id) references t_card(id)
)engine=Innodb default charset=utf8;
2.2 配置方式
<!--
使用many-to-one配置一对一关联关系,通过unique变为一对关联关系
name:属性名
column:外键列
class:属性类型
unique:将多对一变成一对一
-->
<many-to-one name="card" column="card_id" class="Card" unique="true"></many-to-one>
<!--
使用one-to-one配置一对一关联关系
property-ref:引用的关联类的属性,使用外键关联
-->
<one-to-one name="person" class="Person" property-ref="card"></one-to-one>
2.3 基本操作
3. 基于主键的一对一
通过主键来指定一对一的关系
六、组件映射
1. 简介
将一个对象的多个属性封装成单独的另一个对象,这个封装的对象称为组件Component,组件不需要单独的映射文件
对象关系:一个对象是另一个对象的一部分
2. 数据库表
create table t_consumer(
id int primary key auto_increment,
age int,
first_name varchar(200),
last_name varchar(200)
)engine=Innodb default charset=utf8;
3. 配置方式
<!--
配置组件映射关系
name:属性名
class:属性类型
-->
<component name="name" class="Name">
<property name="firstName" column="first_name"></property>
<property name="lastName" column="last_name"></property>
</component>
转载于:https://blog.51cto.com/12402007/2159436