hiberante3-day2

一、hibernate持久化對象狀態(tài)(一級緩存)
持久化對象 Persistent Object = POJO + hbm映射

1、 持久化對象 的 三種狀態(tài) (面試)

  • transient 瞬時態(tài)(臨時態(tài)、自由態(tài)) : 不存在持久化標識OID,尚未與Hibernate Session關(guān)聯(lián)對象,被認為處于瞬時態(tài),失去引用將被JVM回收
    OID 就是 對象中 與數(shù)據(jù)庫主鍵 映射 屬性 ,例如 Customer類 id 屬性
  • persistent 持久態(tài) : 存在持久化標識OID,與當(dāng)前session有關(guān)聯(lián),并且相關(guān)聯(lián)的session沒有關(guān)閉 ,并且事務(wù)未提交
  • detached 脫管態(tài)(離線態(tài)、游離態(tài)) : 存在持久化標識OID,但沒有與當(dāng)前session關(guān)聯(lián),脫管狀態(tài)改變hibernate不能檢測到

區(qū)分三種狀態(tài), 判斷對象是否有OID,判斷對象是否與Session關(guān)聯(lián)(被一級緩存引用 )

    // 獲得Session
    Session session = HibernateUtils.openSession();
    // 開啟事務(wù)
    Transaction transaction = session.beginTransaction();

    Book book = new Book(); // 瞬時態(tài)(沒有OID,未與Session關(guān)聯(lián))
    book.setName("hibernate精通");
    book.setPrice(56d);

    session.save(book);// 持久態(tài)(具有OID,與Session關(guān)聯(lián))

    // 提交事務(wù),關(guān)閉Session
    transaction.commit();
    session.close();

    System.out.println(book.getId()); // 脫管態(tài)(具有 OID,與Session斷開關(guān)聯(lián))

2、 持久化對象狀態(tài)轉(zhuǎn)換
參加課件 UML圖例 (狀態(tài)圖)

1) 瞬時態(tài)對象 通過new獲得
瞬時--持久 save 、 saveOrUpdate (都是Session)
瞬時--脫管 book.setId(1); 為瞬時對象設(shè)置OID

2) 持久態(tài)對象 get/load 、Query查詢獲得
持久--瞬時 delete (被刪除持久化對象 不建議再次使用 )
持久--脫管 evict(清除一級緩存中某一個對象)、close(關(guān)閉Session,清除一級緩存)、clear(清除一級緩存所有對象 )

3) 脫管態(tài)對象 無法直接獲得
脫管--瞬時 book.setId(null); 刪除對象OID
脫管--持久 update、saveOrUpdate、 lock(過時)

3、 Session中一級緩存
Hibernate框架共有兩級緩存, 一級緩存(Session級別緩存) 、 二級緩存 (SessionFactory級別緩存)

Hibernate Session接口 實現(xiàn)類 SessionImpl 類
* private transient ActionQueue actionQueue; ---- 行動隊列
* private transient StatefulPersistenceContext persistenceContext; ---- 持久化上下文

持久化對象保存Session 一級緩存中 (一級緩存 引用 持久化對象 地址 ), 只要Session不關(guān)閉,一級緩存存在,緩存中對象 也不會被回收

Session會在一些特定時間點,將緩存中數(shù)據(jù)flush 到數(shù)據(jù)庫

  • Transaction 的 commit()
  • 應(yīng)用程序執(zhí)行一些查詢操作時
  • 調(diào)用 Session 的 flush() 方法

案例一: 證明一級緩存是存在的
Book book = (Book) session.get(Book.class, 1); // 第一次查詢,緩存中沒有
System.out.println(book);

Book book2 = (Book) session.get(Book.class, 1);// 因為第一次查詢,對象已經(jīng)被放入1級緩存,不會查詢數(shù)據(jù)
System.out.println(book2);
  • 生成一條SQL語句,返回同一個對象 ,第一次查詢生成SQL,查詢對象,將對象放入一級緩存,第二次查詢,直接從一級緩存獲得

案例二 : 測試Hibernate快照 (深入理解一級緩存 內(nèi)存結(jié)構(gòu)原理 )
hibernate 向一級緩存放入數(shù)據(jù)時,同時保存快照數(shù)據(jù)(數(shù)據(jù)庫備份),當(dāng)修改一級緩存數(shù)據(jù),在flush操作時,對比緩存和快照,
如果不一致,自動更新 (將緩存的內(nèi)容 同步到數(shù)據(jù)庫, 更新快照)

  • 快照區(qū)使用,在Session 保存一份 與數(shù)據(jù)庫 相同的數(shù)據(jù) ,在session的 flush時, 通過快照區(qū) 比較得知 一級緩存數(shù)據(jù)是否改變,如果改變執(zhí)行對應(yīng) 操作(update)
  • Hibernate中 持久態(tài) 對象具有自動更新數(shù)據(jù)庫能力 (持久態(tài)對象 才保存在 Session中,才有快照 )

4、 一級緩存常見操作
1) flush : 修改一級緩存數(shù)據(jù) 針對內(nèi)存操作, 需要在session執(zhí)行flush操作時,將緩存變化同步到數(shù)據(jù)庫
* 只有在 緩存數(shù)據(jù) 與 快照區(qū) 不同時,生成update 語句
2) clear : 清除所有對象 一級緩存
3) evict : 清除一級緩存 指定對象

問題:如何將 id為1 book對象 從一級緩存清除 ?

4) refresh :重新查詢數(shù)據(jù)庫,更新快照和一級緩存

5 一級緩存 刷出時間點 設(shè)置 (FlushMode)
ALWAYS 在每次查詢時,session都會flush (不要求 )
AUTO : 在有些查詢時,session會flush (默認) ---------- 查詢、commit 、session.flush
COMMIT : 在事務(wù)提交時,session會flush ------- commit 、session.flush
MANUAL :只有手動調(diào)用 session.flush 才會刷出 ---- session.flush

刷出條件(時間點嚴格程度 )
MANUAL > COMMIT> AUTO> ALWAYS

6、 session持久化對象 操作方法
1) save 將數(shù)據(jù)保存到數(shù)據(jù)庫 , 將瞬時對象 轉(zhuǎn)換 持久對象
持久化對象,不允許隨便修改 OID
2) update 更新數(shù)據(jù) ,主要用于脫管對象的更新 (持久對象,可以根據(jù)快照自動更新 ), 將脫管對象 轉(zhuǎn)換 持久對象
問題一: 調(diào)用update,默認直接生成update語句,如果數(shù)據(jù)沒有改變,不希望生成update
在hbm文件 <class>元素 添加 select-before-update
問題二: 當(dāng)update,脫管對象變?yōu)槌志脤ο螅?一級緩存不允許出現(xiàn)相同OID 兩個持久對象
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [cn.itcast.firstcache.Book#1]
問題三 : 脫管對象 OID 在數(shù)據(jù)表中 不存在,update時,發(fā)生異常
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [cn.itcast.firstcache.Book#20]
3) saveOrUpdate , 如果參數(shù)是一個瞬時對象 執(zhí)行save, 如果參數(shù) 是一個脫管對象 執(zhí)行update, 如果參數(shù)是持久對象 直接返回
判斷對象是瞬時對象 : OID為null , 在hbm文件中為 <id>元素指定 unsaved-value屬性,如果PO對象OID為 unsaved-value 也是瞬時對象
<id name="id" unsaved-value="-1"> 如果對象 OID為-1 也是瞬時對象
4) get/load
如果查詢OID 不存在, get方法 返回 null , load 方法返回代理對象 (代理對象初始化時 拋出 ObjectNotFoundException )
5) delete 方法
方法既可以刪除一個脫管對象, 也可以刪除一個持久化對象
如果刪除脫管,先將脫管對象 與 Session 關(guān)聯(lián),然后再刪除
執(zhí)行delete,先刪除一級緩存數(shù)據(jù),在session.flush 操作時,刪除數(shù)據(jù)表中數(shù)據(jù)

===============================================================================================================================
二、 hibernate 關(guān)聯(lián)關(guān)系映射 (多表映射配置 和 數(shù)據(jù) 增加、刪除 )
1、 系統(tǒng)模型中 實體設(shè)計三種關(guān)系
關(guān)系型數(shù)據(jù)庫 設(shè)計 通常用E-R 繪制
概念E-R圖也稱實體-聯(lián)系圖(EntityRelationshipDiagram),提供了表示實體類型、屬性和聯(lián)系的方法,用來描述現(xiàn)實世界的概念模型。

實體之間存在 三種關(guān)系
一對多
多對多
一對一

不同實體關(guān)系 建表原則
一對多: 在多個一方添加 一的一方 主鍵作為外鍵
多對多: 產(chǎn)生中間關(guān)系表,引入兩個實體主鍵,作為外鍵 ,兩個主鍵成為聯(lián)合主鍵
一對一: 在任意一方 引入對方主鍵 作為外鍵 (開發(fā)中使用非常少 )

繪制 E-R圖 Vision 畫圖軟件

2、 企業(yè)中設(shè)計數(shù)據(jù)庫 時,使用PowerDesigner (簡稱PD)
PD 設(shè)計軟件時,繪制概念圖(面向需求)、物理數(shù)據(jù)表(面向數(shù)據(jù)庫)、 面向?qū)ο箨P(guān)系圖(面向程序) --- 三種圖之間相互轉(zhuǎn)換

瀑布模型軟件開發(fā): 需求---分析---設(shè)計--- 編碼--- 測試 --- 實施維護
* 以前開發(fā)軟件,先設(shè)計數(shù)據(jù)庫, 再根據(jù)數(shù)據(jù)庫 編寫 實體類 (通過表 生成 類 )
* 編寫類圖 (類結(jié)構(gòu) 和關(guān)系) , 生成數(shù)據(jù)表

表能夠描述實體數(shù)據(jù)關(guān)系,通過對象也可以
一對多 (一個A對應(yīng)多個B )
class A {
// 對應(yīng)多個B
B[] b 、 List b 、 Set b
}

   class B {
      // 對應(yīng)一個A
      A a
   }
多對多 (一個A 對應(yīng)多個B, 一個B 對應(yīng)多個A )
    class A {
        Set b;
    }

    class B {
        Set a;
    }
一對一
    class A {
       B b;
    }
    class B {
       A a;
    }

下午重點學(xué)習(xí) 一對多、 多對多 (一對一 作為了解 )

中午練習(xí): 掌握Session一級緩存,持久化對象三種狀態(tài) 轉(zhuǎn)換

三、 一對多 關(guān)聯(lián)關(guān)系映射
以 客戶Customer 和 訂單 Order 為例

1、 一對多關(guān)聯(lián)映射的配置和 Java對象編寫
客戶類
public class Customer {
private Integer id;
private String name;

    // 客戶對應(yīng)多個訂單
    private Set<Order> orders = new HashSet<Order>();
}

訂單類
public class Order {
private Integer id;
private Double money;
private String receiverinfo;

    // 訂單關(guān)聯(lián)一個客戶
    private Customer customer;
}

Order.hbm.xml

<many-to-one name="customer" class="cn.itcast.onetomany.Customer" column="customer_id"></many-to-one>
* 使用not-null 設(shè)置外鍵不能為空

Customer.hbm.xml


<set name="orders">


<key column="customer_id"></key>


<one-to-many class="cn.itcast.onetomany.Order"/>
</set>

案例一: 一對多保存操作
session.save(customer);
session.save(order1);
session.save(order2);
// 建立關(guān)系
customer.getOrders().add(order1);
customer.getOrders().add(order2);

order1.setCustomer(customer);
order2.setCustomer(customer);

案例二 : 保存操作,只保存客戶 或者 只保存 訂單是否可以
默認情況下 異常
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: cn.itcast.onetomany.Order
原因: 持久化對象 關(guān)聯(lián) 瞬時態(tài) 對象

在添加用戶時,同時將用戶關(guān)聯(lián)訂單也保存到數(shù)據(jù)表 (將訂單自動保存 )

級聯(lián)保存 : 當(dāng)一個對象是持久化對象,該對象關(guān)聯(lián)瞬時/脫管 對象,持久態(tài)對象 自動對關(guān)聯(lián)對象 進行保存或者更新操作
* 保存客戶 同時 保存關(guān)聯(lián)訂單
在Customer.hbm.xml 配置 <set name="orders" cascade="save-update">
* 保存訂單 同時 保存關(guān)聯(lián)客戶
在Order.hbm.xml 配置 <many-to-one name="customer" cascade="save-update"></many-to-one>

練習(xí): 對象導(dǎo)航練習(xí)

案例三 : 級聯(lián)刪除
刪除訂單時,關(guān)聯(lián)客戶是否 也被刪除 ??? 刪除客戶時,訂單會不會刪除 ???
* 默認情況下 ,刪除客戶時,將相關(guān)訂單外鍵設(shè)置為null , 完成刪除
* 如果設(shè)置 Order.hbm.xml 中 <manytoone name="customer" not-null="true" /> 必須 設(shè)置inverse=true

不允許存在 沒有客戶的訂單 ?。。?! 在開發(fā)時,需要刪除客戶時, 級聯(lián)刪除該客戶的訂單
* 配置 Customer.hbm.xml cascade="delete"
* 刪除脫管對象無法產(chǎn)生級聯(lián)刪除效果, 必須刪除持久對象

案例四 : 孤兒刪除 (孤子刪除 )
在一對多模型中,存在父子表關(guān)系 , 例如 客戶和訂單, 一方通常是父方, 多方通常是子方 , 不存在沒有客戶的訂單

問題: 解除客戶和訂單關(guān)系時,訂單是否有效 ----- 無效
對于 無效訂單 應(yīng)該刪除, 客戶解除和訂單關(guān)系時,自動刪除訂單

孤兒刪除
1) 在Customer.hbm.xml 配置 <set name="orders" cascade="delete-orphan">
2) 在程序 customer.getOrders().remove(order); 刪除解除關(guān)系的訂單對象

cascade屬性取值, 有JPA規(guī)范定義的, Hibernate框架實現(xiàn)JPA規(guī)范,對其進行擴展
* save-Update 級聯(lián)保存更新,持久對象 關(guān)聯(lián)瞬時對象 執(zhí)行save, 關(guān)聯(lián)脫管對象 執(zhí)行update
* delete 級聯(lián)刪除
* delete-orphan 主要用于一對多模型,進行孤兒刪除
* all 除掉delete-orphan外 所有級聯(lián)關(guān)系
* all-delete-orphan 包含 all和 delete-orphan

案例五 : 雙向維護 --- 多余的SQL
將1號訂單 改為 屬于 2號客戶 ---------- 產(chǎn)生兩條SQL語句

不要在一對多模型中,使雙方都具有 外鍵維護能力
* 設(shè)置inverse屬性 --- 單向維護
通過inverse屬性來設(shè)置由雙向關(guān)聯(lián)的哪一方來維護表和表之間的關(guān)系. inverse=false 的為主動方,inverse=true 的為被動方, 由主動方負責(zé)維護關(guān)聯(lián)關(guān)系

* 哪一方設(shè)置 inverse=true,代表放棄外鍵維護能力 ,在實際開發(fā)中 通常由多方來維護外鍵 , 在一方添加 inverse=true

面試: inverse 和 cascade的區(qū)別 ?
cascade 進行級聯(lián)操作,如果配置cascade 對數(shù)據(jù)進行級聯(lián)操作,
inverse 屬性只是針對外鍵列維護能力 ,如果設(shè)置inverse=true,將外鍵維護交給另一方

小結(jié):
1) 一對多存在 父子關(guān)系,一方是父方,多方是子方, 子方數(shù)據(jù) 依賴 父方數(shù)據(jù)
2) 通常將cascade 配置在 父方 (一的一方 客戶表)
例如: 添加客戶時 添加訂單, 刪除客戶時 刪除訂單 , 孤兒刪除
3) 外鍵方維護權(quán),通常交給多方負責(zé),需要在一方配置 inverse="true"
<set name="orders" cascade="all-delete-orphan" inverse="true" >

====================================================================================================================
四、 多對多 關(guān)聯(lián)關(guān)系映射
以 學(xué)生Student 選課 Course 為例
1、 多對多 hbm映射配置 和 實體類編寫
學(xué)生類
public class Student {
private Integer id;
private String sname;

    // 一個學(xué)生 可以選 多門課程
    private Set<Course> courses = new HashSet<Course>();
}

課程類
public class Course {
private Integer id;
private String cname;

    // 一門課程 可以被 多個學(xué)生選修
    private Set<Student> students = new HashSet<Student>();
}

Student.hbm.xml


<set name="courses" table="student_course">

<key column="student_id"></key>

        <!-- column 指定集合對應(yīng)表 在中間表 產(chǎn)生外鍵列名  -->
        <many-to-many column="course_id" class="cn.itcast.manytomany.Course"/>
    </set>

Course.hbm.xml

<set name="students" table="student_course">

<key column="course_id"></key>
<many-to-many column="student_id" class="cn.itcast.manytomany.Student"></many-to-many>
</set>

案例一: 測試保存
多對多關(guān)聯(lián)映射中,不存在 一對多那樣父子表關(guān)系
在實際開發(fā)中,多對多可以不配置 cascade

  • 只有兩個對象,建立一次關(guān)系,就會向中間表插入一條數(shù)據(jù)
    student1.getCourses().add(course1); // 產(chǎn)生一條 insert
    course1.getStudents().add(student1); // 產(chǎn)生一條 insert
    解決: 只需要建立一方面 關(guān)聯(lián),或者在任何一方 添加inverse=true (推薦)(如果配置了這一選項,則不能在remove的時候刪除中間表數(shù)據(jù)了)

案例二: 測試解除關(guān)系,刪除中間表數(shù)據(jù)
student.getCourses().remove(course); 從中間表 刪除數(shù)據(jù)

案例三 :改變學(xué)生選課關(guān)系
student.getCourses().remove(course); 刪除選課數(shù)據(jù)
student.getCourses().add(course); 重新選課

案例四: 關(guān)羽存在選課記錄,關(guān)羽數(shù)據(jù)能否刪除
可以,先刪除關(guān)羽所有選課記錄,再刪除關(guān)羽數(shù)據(jù)
* 如果刪除課程,先刪除選課記錄,再刪除課程

案例五: 刪除課程時,能否將選課學(xué)生刪除 (在實際中很少用)
使用 cascade 級聯(lián)

  • 多對多 最好不要使用 級聯(lián)操作

=============================================================================================================================
五 一對一關(guān)聯(lián)映射 (了解)
兩種方式

  • 外鍵關(guān)聯(lián)
    類編寫相同,表結(jié)構(gòu)不同
    public class Company {
    private Integer id;
    private String name;

    private Address address;
    }

public class Address {
private Integer id;
private String addressinfo;

private Company company;

}

=========== 添加外鍵一方,寫 <many-to-one> , 不加外鍵一方 寫<one-to-one >
Company.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.onetoonefk.Company" table="company">
<id name="id">
<generator class="identity"></generator>
</id>
<property name="name"></property>

    <many-to-one name="address" class="cn.itcast.onetoonefk.Address" column="aid" unique="true"></many-to-one>
</class>

</hibernate-mapping>

Address.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.onetoonefk.Address" table="address">
<id name="id">
<generator class="identity"></generator>
</id>
<property name="addressinfo"></property>

    <!--  property-ref 對方 company 生成外鍵 屬性名 -->
    <one-to-one name="company" class="cn.itcast.onetoonefk.Company" property-ref="address"></one-to-one>
</class>

</hibernate-mapping>

  • 主鍵關(guān)聯(lián)
    Address表主鍵 直接就是 外鍵,引入Company表主鍵
    address表 主鍵作為外鍵

Company.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.onetoonepk.Company" table="company">
<id name="id">
<generator class="identity"></generator>
</id>
<property name="name"></property>

    <one-to-one name="address" class="cn.itcast.onetoonepk.Address"></one-to-one>
</class>

</hibernate-mapping>

Address.hbm.xml
<hibernate-mapping>
<class name="cn.itcast.onetoonepk.Address" table="address">

<id name="id">

<generator class="foreign">

<param name="property">company</param>
</generator>
</id>
<property name="addressinfo"></property>

        <!-- 為address 主鍵添加 外鍵約束 -->
        <one-to-one name="company" class="cn.itcast.onetoonepk.Company" constrained="true"></one-to-one>
    </class>
</hibernate-mapping>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Hibernate: 一個持久化框架 一個ORM框架 加載:根據(jù)特定的OID,把一個對象從數(shù)據(jù)庫加載到內(nèi)存中OID...
    JHMichael閱讀 2,092評論 0 27
  • 目錄 1. Hibernate的持久化類狀態(tài) 1.1 三種持久化對象的狀態(tài) 1.2 區(qū)分三種持久化對象的狀態(tài) 1....
    深海魚Q閱讀 515評論 0 3
  • 非本人總結(jié)的筆記,抄點筆記復(fù)習(xí)復(fù)習(xí)。感謝傳智博客及黑馬程序猿記筆記啊記筆記 持久化類的狀態(tài) 持久化類三種狀態(tài) 持久...
    鍵盤瞎閱讀 457評論 0 1
  • 如有轉(zhuǎn)載,請申明:轉(zhuǎn)載自 IT天宇: http://www.itdecent.cn/p/50964e92c5fb ...
    IT天宇閱讀 4,312評論 8 33
  • ThreadLocal是面試中比較容易碰上的問題,一般會要求講解它的實現(xiàn)原理以及存在的問題。最近在美團的面試中聊到...
    吃貓的老鼠閱讀 5,614評論 2 5

友情鏈接更多精彩內(nèi)容