hibernate筆記(三)
目標:
第1部分: 對象的狀態:
第2部分:緩存
1) 一級緩存
2) 相關知識
----懶加載---
第3部分:映射
一對一映射
組件映射
繼承映射
一、對象的狀態
舉例: User user = new User();
Hibernate中對象的狀態: 臨時/瞬時狀態、持久化狀態、遊離狀態。
臨時狀態
特點:
直接new出來的對象;
不處於session的管理;
數據庫中沒有對象的記錄;
持久化狀態
當調用session的save/saveOrUpdate/get/load/list等方法的時候,對象就是持久化狀態。
處於持久化狀態的對象,當對對象屬性進行更改的時候,會反映到數據庫中!
特點:
處於session的管理;
數據庫中有對應的記錄;
遊離狀態
特點
不處於session的管理;
數據庫中有對應的記錄
Session關閉後,對象的狀態;
對象狀態的轉換,
二、一級緩存
為什麽要用緩存?
目的:減少對數據庫的訪問次數!從而提升hibernate的執行效率!
Hibernate中緩存分類:
一級緩存
二級緩存
概念
1)Hibenate中一級緩存,也叫做session的緩存,它可以在session範圍內減少數據庫的訪問次數! 只在session範圍有效! Session關閉,一級緩存失效!
2)當調用session的save/saveOrUpdate/get/load/list/iterator
3)Session的緩存由hibernate維護, 用戶不能操作緩存內容; 如果想操作緩存內容,必須通過hibernate提供的evit/clear方法操作。
特點:
只在(當前)session範圍有效,作用時間短,效果不是特別明顯!
在短時間內多次操作數據庫,效果比較明顯!
緩存相關幾個方法的作用
session.flush(); 讓一級緩存與數據庫同步
session.evict(arg0); 清空一級緩存中指定的對象
session.clear(); 清空一級緩存中緩存的所有對象
在什麽情況用上面方法?
批量操作使用使用:
Session.flush(); // 先與數據庫同步
Session.clear(); // 再清空一級緩存內容
面試題1: 不同的session是否會共享緩存數據?
不會。
User1 u1 = Session1.get(User.class,1); 把u1對象放入session1的緩存
Session2.update(u1); 把u1放入session2的緩存
U1.setName(‘new Name’);
如果生成2條update sql, 說明不同的session使用不同的緩存區,不能共享。
面試題2: list與iterator查詢的區別?
list()
一次把所有的記錄都查詢出來,
會放入緩存,但不會從緩存中獲取數據
Iterator
N+1查詢; N表示所有的記錄總數
即會先發送一條語句查詢所有記錄的主鍵(1),
再根據每一個主鍵再去數據庫查詢(N)!
會放入緩存,也會從緩存中取數據!
三、懶加載
面試題3: get、load方法區別?
get: 及時加載,只要調用get方法立刻向數據庫查詢
load:默認使用懶加載,當用到數據的時候才向數據庫查詢。
懶加載:(lazy)
概念:當用到數據的時候才向數據庫查詢,這就是hibernate的懶加載特性。
目的:提供程序執行效率!
lazy 值
true 使用懶加載
false 關閉懶加載
extra (在集合數據懶加載時候提升效率),在真正使用數據的時候才向數據庫發送查詢的sql;如果調用集合的size()/isEmpty()方法,只是統計,不真正查詢數據!
懶加載異常
Session關閉後,不能使用懶加載數據!
如果session關閉後,使用懶加載數據報錯:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
如何解決session關閉後不能使用懶加載數據的問題?
// 方式1: 先使用一下數據
//dept.getDeptName();
// 方式2:強迫代理對象初始化
Hibernate.initialize(dept);
// 方式3:關閉懶加載
設置lazy=false;
// 方式4: 在使用數據之後,再關閉session!
四、一對一映射
需求: 用戶與身份證信息
一條用戶記錄對應一條身份證信息! 一對一的關系!
設計數據庫:
JavaBean:
映射:
基於外鍵的映射
// 身份證 public class IdCard {
// 身份證號(主鍵) private String cardNum;// 對象唯一表示(Object Identified, OID) private String place; // 身份證地址 // 身份證與用戶,一對一的關系 private User user;
|
// 用戶 public class User {
private int userId; private String userName; // 用戶與身份證信息, 一對一關系 private IdCard idCard;
|
|
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.c_one2one">
<class name="IdCard" table="t_IdCard"> <id name="cardNum"> <generator class="assigned"></generator> </id> <property name="place" length="20"></property>
<!-- 一對一映射,有外鍵方 unique="true" 給外鍵字段添加唯一約束 --> <many-to-one name="user" unique="true" column="user_id" class="User" cascade="save-update"></many-to-one>
</class>
</hibernate-mapping>
|
|
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.c_one2one">
<class name="User" table="t_user"> <id name="userId"> <generator class="native"></generator> </id> <property name="userName" length="20"></property> <!-- 一對一映射: 沒有外鍵方 --> <one-to-one name="idCard" class="IdCard"></one-to-one>
</class>
</hibernate-mapping>
|
基於主鍵的映射
// 身份證 public class IdCard {
private int user_id; // 身份證號 private String cardNum; private String place; // 身份證地址 // 身份證與用戶,一對一的關系 private User user;
|
|
<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="cn.itcast.c_one2one2">
<class name="IdCard" table="t_IdCard"> <id name="user_id"> <!-- id 節點指定的是主鍵映射, 即user_id是主鍵 主鍵生成方式: foreign 即把別的表的主鍵作為當前表的主鍵; property (關鍵字不能修改)指定引用的對象 對象的全名 cn..User、 對象映射 cn.User.hbm.xml、 table(id) --> <generator class="foreign"> <param name="property">user</param> </generator> </id> <property name="cardNum" length="20"></property> <property name="place" length="20"></property>
<!-- 一對一映射,有外鍵方 (基於主鍵的映射) constrained="true" 指定在主鍵上添加外鍵約束 --> <one-to-one name="user" class="User" constrained="true" cascade="save-update"></one-to-one>
</class>
</hibernate-mapping>
|
|
|
|
五、組件映射與繼承映射
類的關系
組合關系
一個類中包含了另外一個類。這2個類中就是組合關系。
需求: 汽車與車輪
繼承關系
一個類繼承另外一個類。這2個類中就是繼承關系。
需求:動物
貓
猴子
組件映射
類組合關系的映射,也叫做組件映射!
註意:組件類和被包含的組件類,共同映射到一張表!
需求: 汽車與車輪
數據庫
T_car
主鍵 汽車名稱 輪子大小 個數
Javabean:
public class Car {
private int id; private String name; // 車輪 private Wheel wheel; } |
// 車輪 public class Wheel {
private int count; private int size; } |
<hibernate-mapping package="cn.itcast.d_component">
<class name="Car" table="t_car"> <id name="id"> <generator class="native"></generator> </id> <property name="name" length="20"></property>
<!-- 組件映射 --> <component name="wheel"> <property name="size"></property> <property name="count"></property> </component>
</class>
</hibernate-mapping>
|
|
繼承映射
需求:動物
貓
猴子
簡單繼承映射
// 動物類 public abstract class Animal {
private int id; private String name;
|
<!-- 簡單繼承 --> <hibernate-mapping package="cn.itcast.e_extends1">
<class name="Cat" table="t_Cat"> <!-- 簡單繼承映射: 父類屬性直接寫 --> <id name="id"> <generator class="native"></generator> </id> <property name="na"></property> <property name="catchMouse"></property> </class>
</hibernate-mapping> |
|
@Test public void getSave() {
Session session = sf.openSession(); session.beginTransaction();
// 保存 // Cat cat = new Cat(); // cat.setName("大花貓"); // cat.setCatchMouse("抓小老鼠"); // session.save(cat);
// 獲取時候註意:當寫hql查詢的使用,通過父類查詢必須寫上類的全名 Query q = session.createQuery("from cn.itcast.e_extends1.Animal"); List<Animal> list = q.list(); System.out.println(list);
session.getTransaction().commit(); session.close();
}
|
|
總結:
簡單繼承映射,有多少個子類,寫多少個映射文件!
繼承映射
需求:貓、猴子、動物。
所有子類映射到一張表 (1張表)
什麽情況用?
子類教多,且子類較為簡單,即只有個別屬性!
好處:因為使用一個映射文件, 減少了映射文件的個數。
缺點:(不符合數據庫設計原則)
一個映射文件: Animal.hbm.xml
(如何區分是哪個子類的信息?)
數據庫:
T_animal (要存儲所有的子類信息) “鑒別器”
Id name catchMouse eatBanana type_(區別是哪個子類)
1 大馬猴 NULL 吃10個香蕉 猴子
2 大花貓 不抓老鼠 NULL 貓
總結:
寫法較為簡單:所有子類用一個映射文件,且映射到一張表!
但數據庫設計不合理!
(不推薦用。)
每個類映射一張表(3張表)
數據庫
T_anmal (存儲父類信息)
1 大花貓
T_cat (引用父類的主鍵)
1 抓小老鼠
T_monkey(引用父類的主鍵)
Javabean設計一樣,映射實現不同: |
<!-- 繼承映射, 每個類對應一張表(父類也對應表) --> <hibernate-mapping package="cn.itcast.e_extends3">
<class name="Animal" table="t_animal"> <id name="id"> <generator class="native"></generator> </id> <property name="name"></property>
<!-- 子類:貓 t_cat key 指定_cat表的外鍵字段 --> <joined-subclass name="Cat" table="t_cat"> <key column="t_animal_id"></key> <property name="catchMouse"></property> </joined-subclass>
<!-- 子類:猴子 t_monkey --> <joined-subclass name="Monkey" table="t_monkey"> <key column="t_animal_id"></key> <property name="eatBanana"></property> </joined-subclass>
</class>
</hibernate-mapping>
|
總結:
一個映射文件,存儲所有的子類; 子類父類都對應表;
缺點:表結構比較復雜,插入一條子類信息,需要用2條sql: 往父類插入、往子類插入!
(推薦)每個子類映射一張表, 父類不對應表(2張表)
數據庫:
T_cat
Id name catchMounse
T_monkey
Id name eatBanana
<union-subclass name="Cat" table="t_cat"> <property name="catchMouse"></property> </union-subclass> |
註意:主鍵不能是自增長! |
總結:
所有的子類都寫到一個映射文件;
父類不對應表; 每個子類對應一張表
Hibernate中映射:
多對一
一對多
多對多
一對一 (多對一的特殊應用)
組件
繼承
hibernate筆記(三)