Hibernate框架技術視頻課程——筆記(三)
一、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>
Hibernate框架技術視頻課程——筆記(三)