[Java面試八]Hibernate總結以及在面試中的一些問題.


1.為什么要使用Hibernate開發你的項目呢?Hibernate的開發流程是怎么樣的?

為什么要使用
.JDBC訪問數據庫的代碼做了封裝,大大簡化了數據訪問層繁瑣的重復性代碼。
.Hibernate 是一個基於JDBC的主流持久化框架,是一個優秀的ORM 實現。他很大程度 的簡化DAO層的編碼工作
.hibernate 的性能非常好,因為它是個輕量級框架。映射的靈活性很出色。它支持各種關系數據庫,從一對一到多對多的各種復雜關系。
開發流程
01987414-db11-4848-8ae2-3e956bdf36b8

2.什么是延遲加載?

    延遲加載機制是為了避免一些無謂的性能開銷而提出來的,所謂延遲加載就是當在真正需要 數據的時候,才真正執行數據加載操作。在Hibernate中提供了對實體對象的延遲加載以及 對集合的延遲加載,另外在Hibernate3中還提供了對屬性的延遲加載。

3.說一下hibernate的緩存機制

Ahibernate一級緩存
1hibernate支持兩個級別的緩存,默認只支持一級緩存;
2)每個Session內部自帶一個一級緩存;
3)某個Session被關閉時,其對應的一級緩存自動清除;
B:hibernate二級緩存
(1) 二級緩存獨立於session,默認不開啟;

4.Hibernate的查詢方式有哪些?

本地SQL查詢、CriteriaHql

5.如何優化Hibernate?

1.使用雙向一對多關聯,不使用單向一對多
2.靈活使用單向一對多關聯
3.不用一對一,用多對一取代
4.配置對象緩存,不使用集合緩存
5.一對多集合使用Bag,多對多集合使用Set
6. 繼承類使用顯式多態
7. 表字段要少,表關聯不要怕多,有二級緩存撐腰 

6.Hibernate中GET和LOAD的區別?

    請注意如果沒有匹配的數據庫記錄,load()方法可能拋出無法恢復的異常(unrecoverable exception)。 如果類的映射使用了代理(proxy)load()方法會返回一個未初始化的代理,直到你調用該代理的某方法時才會去訪問數據庫。若你希望在某對象中創建一個指向另一個對象的關聯,又不想在從數據庫中裝載該對象時同時裝載相關聯的那個對象,那么這種操作方式就用得上的了。 如果為相應類映射關系設置了batch-size, 那么使用這種操作方式允許多個對象被一批裝載(因為返回的是代理,無需從數據庫中抓取所有對象的數據)。 如果你不確定是否有匹配的行存在,應該使用 get()方法,它會立刻訪問數據庫,如果沒有對應的行,會返回null
    session.get 方法, 查詢立即執行 , 返回Customer類對象
    session.load 方法,默認采用延遲加載數據方式,不會立即查詢,返回 Customer類子類對象 (動態生成代理對象)
* 如果 PO類使用final修飾,load無法創建代理對象,返回目標對象本身 (load效果和 get效果 相同 ) 

7.說說在 hibernate中使用Integer做映射和使用int做映射之間有什么差別?

Integer codeint code的區別:
Integer是對象.      code=null;   對象可以為空.   
int 是普通類型,     不可能=null.       
根據你的數據庫code是可以空的,故應該映射成Integer.      
你沒理由hbm.xml里寫 Integer,類里卻寫int

8.SQLHQL有什么區別?

sql 面向數據庫表查詢
hql 面向對象查詢
 
hql:from 后面跟的 類名+類對象 where 后 用 對象的屬性做條件
sql:from 后面跟的是表名  where 后 用表中字段做條件
查詢
Hibernate中使用查詢時,一般使用Hql查詢語句。
HQL(Hibernate Query Language),即Hibernate的查詢語言跟SQL非常相像。不過HQLSQL的最根本的區別,就是它是面向對象的。
 
使用HQL時需要注意以下幾點:
1.大小寫敏感
因為HQL是面向對象的,而對象類的名稱和屬性都是大小寫敏感的,所以HQL是大小寫敏感的。
HQL語句:from Cat as cat where cat.id > 1;from Cat as cat where cat.ID > 1;是不一樣的,這點與SQL不同。
2.from子句
from Cat,該句返回Cat對象實例,開發人員也可以給其加上別名,eg. from Cat as cat,對於多表查詢的情況,可參考如下:
from Cat as cat, Dog as dog
其它方面都與SQL類似,在此不再贅述。

9.Hibernate的分頁查詢

例如:從數據庫中的第 20000條數據開始查后面100 條記錄
Query q = session.createQuery("from Cat as c");;   
q.setMaxResults(100);;   
List l = q.list();;
q.setFirstResult(20000);;  

10.HibernateJava對象的狀態以及對應的特征有哪些?

持久化對象的三種狀態
    持久化對象POOID
PO=POJO + hbm
映射配置
    編寫規則
  1. 必須提供無參數public構造器
  2. 所有屬性private,提供publicgettersetter方法
  3. 必須提供標識屬性,與數據表中主鍵對應,例如Customer id屬性
  4. PO類屬性應盡量使用基本數據類型的包裝類型(區分空值)  例如int---Integer  long---Long
  5. 不要用final修飾(將無法生成代理對象進行優化)
OID  指與數據表中主鍵對應 PO類中屬性,例如 Customer id 屬性
    Hibernate 框架使用OID來區分不同PO對象
        * 例如內存中有兩個PO對象,只要具有相同 OID Hibernate認為同一個對象
    * Hibernate 不允許緩存同樣OID
的兩個不同對象
     
①瞬時態 (臨時態、自由態):不存在持久化標識OID,尚未與Hibernate Session關聯對象,被認為處於瞬時態,失去引用將被JVM回收
②持久態:存在持久化標識
OID,與當前session有關聯,並且相關聯的session沒有關閉 ,並且事務未提交
③脫管態
(離線態、游離態):存在持久化標識OID,但沒有與當前session關聯,脫管狀態改變hibernate
不能檢測到

    區分三種狀態:判斷對象是否有OID,判斷對象是否與session關聯(被一級緩存引用)
// 獲得Session
Session session =HibernateUtils.openSession();
// 開啟事務
Transaction transaction = session.beginTransaction();
 
Book book =newBook();// 瞬時態(沒有OID,未與Session關聯)
book.setName("hibernate精通");
book.setPrice(56d);
 
session.save(book);// 持久態(具有OID,與Session關聯)
 
// 提交事務,關閉Session
transaction.commit();
session.close();
 
System.out.println(book.getId());// 脫管態(具有 OID,與Session斷開關聯)
持久化對象狀態轉換
    ceb8decb-fd8e-4b0e-a5bf-a7e7bcc79ec5
    瞬時態對象:通過new獲得
        瞬時----->持久    save、saveOrUpdate(都是session)
        瞬時----->脫管    book.setId(1) 為瞬時對象設置OID

    持久態對象:通過get/load 、Query查詢獲得
持久----->瞬時    delete  (被刪除持久化對象 不建議再次使用 )
持久----->脫管    evict(清除一級緩存中某一個對象)、close(關閉Session,清除一級緩存)、clear(清除一級緩存所有對象 )
 
    脫管態對象 無法直接獲得
脫管----->瞬時    book.setId(null); 刪除對象OID
脫管----->持久    update、saveOrUpdate、 lock(過時)

11.Hibernate中怎樣處理事務?

Hibernate是對JDBC的輕量級對象封裝,Hibernate本身是不具備Transaction 處理功能的,HibernateTransaction實際上是底層的JDBC Transaction的封裝,或者是JTA Transaction的封裝,下面我們詳細的分析:
Hibernate可以配置為JDBCTransaction或者是JTATransaction,這取決於你在hibernate.properties中的配置:

  1. #hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
  2. #hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory
如果你什么都不配置,默認情況下使用JDBCTransaction,如果你配置為:

hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
將使用JTATransaction,不管你准備讓Hibernate使用JDBCTransaction,還是JTATransaction我的忠告就是什么都不配,將讓它保持默認狀態,如下:
 

  1. #hibernate.transaction.factory_class net.sf.hibernate.transaction.JTATransactionFactory
  2. #hibernate.transaction.factory_class net.sf.hibernate.transaction.JDBCTransactionFactory
在下面的分析中我會給出原因。

一、JDBC Transaction
看看使用JDBC Transaction的時候我們的代碼例子:

  1. Session session = sf.openSession();
  2. Transaction tx = session.beginTransactioin();
  3. ...
  4. session.flush();
  5. tx.commit();
  6. session.close();
這是默認的情況,當你在代碼中使用HibernateTransaction的時候實際上就是JDBCTransaction。那么JDBCTransaction究竟是什么東西呢?來看看源代碼就清楚了:

Hibernate2.0.3源代碼中的類

  1. net.sf.hibernate.transaction.JDBCTransaction:
  2. public void begin() throws HibernateException {
  3.     ...
  4.     if (toggleAutoCommit) session.connection().setAutoCommit(false);
  5.     ...
  6. }
這是啟動Transaction的方法,看到 connection().setAutoCommit(false) 了嗎?是不是很熟悉?

再來看

  1. public void commit() throws HibernateException {
  2.     ...
  3.     try {
  4.         if ( session.getFlushMode()!=FlushMode.NEVER ) session.flush();
  5.     try {
  6.         session.connection().commit();
  7.         committed = true;
  8.     }
  9.     ...
  10.     toggleAutoCommit();
  11. }
這是提交方法,看到connection().commit() 了嗎?下面就不用我多說了,這個類代碼非常簡單易懂,通過閱讀使我們明白HibernateTransaction都在干了些什么?我現在把用 Hibernate寫的例子翻譯成JDBC,大家就一目了然了:
  1. Connection conn = ...; <--- session = sf.openSession();
  2. conn.setAutoCommit(false); <--- tx = session.beginTransactioin();
  3. ... <--- ...
  4. conn.commit(); <--- tx.commit(); (對應左邊的兩句)
  5. conn.setAutoCommit(true);
  6. conn.close(); <--- session.close();
    看明白了吧,HibernateJDBCTransaction根本就是conn.commit而已,根本毫無神秘可言,只不過在Hibernate中, Session打開的時候,就會自動conn.setAutoCommit(false),不像一般的JDBC,默認都是true,所以你最后不寫 commit也沒有關系,由於Hibernate已經把AutoCommit給關掉了,所以用Hibernate的時候,你在程序中不寫 Transaction的話,數據庫根本就沒有反應。

12.簡單的介紹一下Hibernate的核心API?

1.Configuration
    用於加載hibernate配置
    加載核心屬性配置hibernate.properties和hibernate.cfg.xml
//方式一src 讀取 hibernate.properties 屬性配置文件
Configuration cfg =newConfiguration(); 
//方式二src讀取 hibernate.cfg.xml  
Configuration cfg =newConfiguration().configure(); 
 Configuration cfg =newConfiguration().configure("自定義xml文件");src 加載指定文件 
 
    手動加載hbm映射配置,持久化類與數據表的映射關系(*.hbm.xml 文件)
如果沒有對PO類進行hbm映射,會報錯 :
org.hibernate.MappingException:Unknown entity: cn.itcast.domain.Customer 
        那么我們可以手動加載其映射文件:
//方式一:
configuration.addResource("cn/itcast/domain/Customer.hbm.xml");加載hbm文件 
//方式二:
configuration.addClass(Customer.class);加載Class,自動搜索hbm映射文件
    * 如果使用 hibernate.cfg.xml配置,將映射配置xml中 <mapping resource="cn/itcast/domain/Customer.hbm.xml"/>
 
2.SessionFactory
    保存了當前的數據庫配置信息和所有映射關系以及預定義的SQL語句這個對象是線程安全的
//預定義SQL語句
<sql-queryname="login">
    <![CDATA[select * from user where username= ? and password =?]]>
</sql-query>
3.Session
    代表hibernate操作會話對象,相當於Connection 
session是一個單線程對象,線程不安全(在方法內部定義和使用Session,不會出現線程問題)
* 每個線程方法調用棧,是線程私有的
session 進行PO(Persistent Object)對象常見持久化操作, 存在一級緩存
    常用API
save 完成插入
update 完成修改
delete完成刪除
get/load 根據主鍵字段查詢
createQuery createSQLQuery 創建查詢對象Query接收HQLSQLQuery接收SQL
createCriteria()  面向對象條件查詢
4.Transaction 事務操作
commit 提交
rollback 回滾
 
    如果沒有開啟事務,那么每個Session的操作,都相當於一個獨立的事務
* 事務是否提交
//默認false
<property name="hibernate.connection.autocommit">false</property> 事務不提交
<propertyname="hibernate.connection.autocommit">true</property> 事務提交
5.Query
    session.createQuery()獲得
面向對象查詢,操作類,操作屬性
接收參數 HQL語句
開發代碼步驟
    獲得HibernateSession對象
    編寫HQL語句
    調用session.createQuery 創建查詢對象
    如果HQL語句包含參數,則調用QuerysetXXX設置參數
    調用Query對象的list()uniqueResult()方法執行查詢
6.Criteria 接口(QBC查詢 Query By Criteria )
  主要為了解決多條件查詢問題,以面向對象的方式添加條件,無需拼接HQL語句 

13.update與saveOrUpdate有什么區別?

save() 方法很顯然是執行保存操作的,如果是對一個新的剛new出來的對象進行保存,自然要使用這個方法了,數據庫中沒有這個對象。
update()
如果是對一個已經存在的托管對象進行更新那么肯定是要使用update()方法了,數據中有這個對象。
saveOrUpdate()
這個方法是更新或者插入,有主鍵就執行更新,如果沒有主鍵就執行插入。【此方法慎用
    HibernatesaveOrUpdate()方法在執行的時候,先會去session中去找存不存在指定的字段,如果存在直接update,否則save,這個時候問題就發生了。
    有兩張表,表A和表B,這兩張表的主鍵都是一樣的,例如都是MASTER_ID,同時對應的BO里面屬性都是masterID,現在要執行的操作是,以 MASTER_ID為條件將表A中的數據查詢出來,然后將部分值插入到表B中,然后再更新表B,在查詢表A后,session中已經存在masterID 了,這個時候再去對表B進行savaOrUpdate的時候,Hibernate會發現session中已經存在masterID了,所以執行的就是 update,但是實際上表B中根本不存在masterID這個值,當你執行完查詢數據庫的時候會發現沒有插入數據,像這種情況,就得先用 masterID對表B進行查詢,當返回的BONULL時,new一個新BO然后再進行插入,這個時候用到的就是createbo了。

14.Hibernate的inverse屬性的作用?

1 .明確inversecascade的作用 
inverse 
決定是否把對對象中集合的改動反映到數據庫中,所以inverse只對集合起作用,也就是只對one-to-manymany-to-many有效(因為只有這兩種關聯關系包含集合,而one-to-onemany-to-one只含有關系對方的一個引用)。

cascade
決定是否把對對象的改動反映到數據庫中,所以cascade對所有的關聯關系都起作用(因為關聯關系就是指對象之間的關聯關系)。

2.inverse 屬性 inverse所描述的是對象之間關聯關系的維護方式。
inverse
只存在於集合標記的元素中 。Hibernate提供的集合元素包括<set/> <map/> <list/> <array /> <bag />
Inverse
屬性的作用是:是否將對集合對象的修改反映到數據庫中。 inverse屬性的默認值為false,表示對集合對象的修改會被反映到數據庫中;inverse=false 的為主動方,由主動方負責維護關聯關系。  inverse=”true” 表示對集合對象的修改不會被反映到數據庫中。
為了維持兩個實體類(表)的關系,而添加的一些屬性,該屬性可能在兩個實體類(表)或者在一個獨立的表里面,這個要看這雙方直接的對應關系了: 這里的維護指的是當主控放進行增刪改查操作時,會同時對關聯關系進行對應的更新。
   
一對多: 該屬性在多的一方。應該在一方的設置 inverse=true ,多的一方設置 inverse=false(多的一方也可以不設置inverse屬性,因為默認值是false),這說明關聯關系由多的一方來維護。如果要一方維護關 系,就會使在插入或是刪除""方時去update""方的每一個與這個""的對象有關系的對象。而如果讓""方面維護關系時就不會有update 操作,因為關系就是在多方的對象中的,直指插入或是刪除多方對象就行了。顯然這樣做的話,會減少很多操作,提高了效率。
注:單向one-to-many關聯關系中,不可以設置inverse="true",因為被控方的映射文件中沒有主控方的信息。
   多對多: 屬性在獨立表中。inverse屬性的默認值為false。在多對多關聯關系中,關系的兩端 inverse不能都設為false,即默認的情況是不對的,如果都設為false,在做插入操作時會導致在關系表中插入兩次關系。也不能都設為 true,如果都設為true,任何操作都不會觸發對關系表的操作。因此在任意一方設置inverse=true,另一方inverse=false
   
一對一: 其實是一對多的一個特例,inverse 的設置也是一樣的,主要還是看關聯關系的屬性在哪一方,這一方的inverse=false
   
多對一: 也就是一對多的反過來,沒什么區別。

3.cascade 屬性
級聯操作:指當主控方執行某項操作時,是否要對被關聯方也執行相同的操作。
cascade屬性的作用是描述關聯對象進行操作時的級聯特性。因此,只有涉及到關系的元素才有cascade屬性。具有cascade屬性的標記包括<many-to-one /> <one-to-one /> <any /> <set /><bag /> <idbag /> <list /> <array />
注意:<one-to-many /> <many-to-many />是用在集合標記內部的,所以是不需要cascade屬性的。

4.inversecascade的區別
作用的范圍不同:
    Inverse是設置在集合元素中的。
    Cascade對於所有涉及到關聯的元素都有效。
    <many-to-one/><ont-to-many/>沒有inverse屬性,但有cascade屬性
執行的策略不同
    Inverse 會首先判斷集合的變化情況,然后針對變化執行相應的處理。
    Cascade 是直接對集合中每個元素執行相應的處理
執行的時機不同
    Inverse是在執行SQL語句之前判斷是否要執行該SQL語句
    Cascade則在主控方發生操作時用來判斷是否要進行級聯操作
執行的目標不同
    Inverse對於<ont-to-many><many-to-many>處理方式不相同。
    對於<ont-to-many>inverse所處理的是對被關聯表進行修改操作。
    對於<many-to-many>inverse所處理的則是中間關聯表
    Cascade不會區分這兩種關系的差別,所做的操作都是針對被關聯的對象。
總結 
<one-to-many>

    <one-to-many>中,建議inverse=”true”,由“many”方來進行關聯關系的維護
    <many-to-many>中,只設置其中一方inverse=”false”,或雙方都不設置
    Cascade,通常情況下都不會使用。特別是刪除,一定要慎重。
操作建議:
    一般對many-to-onemany-to-many不設置級聯,這要看業務邏輯的需要;one-to-oneone-to-many設置級聯。
    many-to-many關聯關系中,一端設置inverse=”false”,另一端設置為inverse=”true”。在one-to-many關聯關系中,設置inverse=”true”,由多端來維護關系表


Hibernate一級緩存相關問題

1.Session中的一級緩存
    Hibernate框架共有兩級緩存, 一級緩存(Session級別緩存)、二級緩存(SessionFactory級別緩存)
    在Session接口的實現中包含一系列的 Java 集合, 這些 Java 集合構成了 Session 緩存.  持久化對象保存Session一級緩存中(一級緩存引用持久化對象地址),只要 Session 實例沒有結束生命周期, 存放在它緩存中的對象也不會結束生命周期
    Hibernate Session接口的實現類SessionImpl類(查看源碼,右擊session,選擇Open Type Hierarchy) ,里面有2個重要的字段:
*private transient ActionQueue actionQueue;                       ----行動隊列(標記數據活動)
*private transient StatefulPersistenceContext persistenceContext;----持久化上下文 
    當session的save()方法持久化一個對象時,該對象被載入緩存,以后即使程序中不再引用該對象,只要緩存不清空,該對象仍然處於生命周期中。當試圖get()、 load()對象時,會判斷緩存中是否存在該對象,有則返回,此時不查詢數據庫。沒有再查詢數據庫
    Session 能夠在某些時間點, 按照緩存中對象的變化來執行相關的 SQL 語句, 來同步更新數據庫, 這一過程被稱為刷出緩存(flush)
        *  Transaction的commit()
        *  應用程序執行一些查詢操作時
        *  調用Session的flush()方法

    驗證一級緩存的存在
Book book =(Book) session.get(Book.class,1);// 第一次查詢,緩存中沒有
System.out.println(book);
 
Book book2 =(Book) session.get(Book.class,1);// 因為第一次查詢,對象已經被放入1級緩存,不會查詢數據
System.out.println(book2);
 
*生成一條SQL語句,返回同一個對象,第一次查詢生成SQL,查詢對象,將對象放入一級緩存,第二次查詢,直接從一級緩存獲得
    06313632-5fa2-41bd-8eb0-cb3dbcf074df
 
 
    測試Hibernate快照 (深入理解一級緩存內存結構原理)
    hibernate 向一級緩存放入數據時,同時保存快照數據(數據庫備份),當修改一級緩存數據,在flush操作時,對比緩存和快照,如果不一致,自動更新(將緩存的內容同步到數據庫,更新快照)
 
*  快照區使用,在Session 保存一份與數據庫相同的數據,在session的flush時, 通過快照區比較得知一級緩存數據是否改變,如果改變執行對應操作(update)
/**
* 測試快照區的使用
  */
@Test
publicvoid demo3(){
    // 獲得Session
Session session =HibernateUtils.openSession();
// 開啟事務
Transaction transaction = session.beginTransaction();
 
// 查詢id 為1 的圖書對象
Book book =(Book) session.get(Book.class,1);// 第一次查詢,將對象加入一級緩存
System.out.println(book);
 
book.setName("深入淺出Hibernate技術");// 修改書名(一級緩存被修改,自動update)
 
// 沒有手動執行update,因為快照區原因,自動更新
 
// 提交事務,關閉Session
transaction.commit();
session.close();
}
    使用Debug模式進行截圖說明:
    我們重點關注session中的2個屬性actionQueue和persistenceContext
7f2c4be2-ee6d-40fa-94c5-8c700599438b
    大白話解析
        **當執行get后,緩存里面有數據了,活動隊列沒有發生變化,說明沒有需要提交到數據的內容,PersistenceContext里面有內容了。
            我們說,持久化上下文里面存放的是一個Map,它的鍵為一級緩存對象,值為快照(它是一級緩存對象的一個副本)。
        **當執行setName后,一級緩存里面的數據發生了改變,在緩存flush時,會對比緩存和快照,如果不一致,那么會將緩存中的內容同步到數據庫,並更新快照!
 
*  Hibernate中 持久態 對象具有自動更新數據庫能力 (持久態對象 才保存在 Session中,才有快照 )
 
2.一級緩存常見操作
    所有操作需要使用斷點調試才能看得比較清楚!
    1)flush : 修改一級緩存數據針對內存操作,需要在session執行flush操作時,將緩存變化同步到數據庫
     * 只有在緩存數據與快照區不同時,生成update語句
    2)clear : 清除所有對象 一級緩存
    3)evict : 清除一級緩存指定對象
    4)refresh :重新查詢數據庫,更新快照和一級緩存
@Test
// Session 對於 一級緩存操作
publicvoid demo4(){
// 獲得Session
Session session =HibernateUtils.openSession();
// 開啟事務
Transaction transaction = session.beginTransaction();
 
// 查詢id 為1 的圖書對象
Book book =(Book) session.get(Book.class,1);// 第一次查詢,將對象加入一級緩存
System.out.println(book);
 
// book.setPrice(80d); // 修改一級緩存數據
// 將緩存內容同步到數據庫
// session.flush();
 
// 清除一級緩存所有數據
// session.clear();
 
// 清除一級緩存中 指定對象
// session.evict(book);
 
book.setPrice(30d);// 一級緩存改變
session.refresh(book);// 用數據庫內容 覆蓋快照區和一級緩存
 
// 提交事務,關閉Session
transaction.commit();
session.close();
}
 
3.一級緩存刷出時間點設置 (FlushMode)
ALWAYS :在每次查詢時,session都會flush  (不要求 )
AUTO   : 在有些查詢時,session會flush  (默認)  ---------- 查詢、commit 、session.flush
COMMIT : 在事務提交時,session會flush   ------- commit 、session.flush
MANUAL :只有手動調用  session.flush 才會刷出  ----  session.flush
 
刷出條件(時間點嚴格程度 )
MANUAL > COMMIT> AUTO> ALWAYS
@Test
// 理解 FlushMode作用
publicvoid demo5(){
// 獲得Session
Session session =HibernateUtils.openSession();
 
// 設置 flushMode
session.setFlushMode(FlushMode.MANUAL);
 
// 開啟事務
Transaction transaction = session.beginTransaction();
 
// 查詢id 為1 的圖書對象
Book book =(Book) session.get(Book.class,1);// 第一次查詢,將對象加入一級緩存
System.out.println(book);
 
book.setPrice(1000d);// 修改價格
 
session.createQuery("from Book").list();// 查詢所有圖書 (AUTO 級別 flush)
 
// 提交事務,關閉Session
transaction.commit();// (COMMIT 級別 flush)
 
// session.flush(); // MANUAL 級別 flush
 
session.close();
}
 
4.session持久化對象操作方法
    1) save 將數據保存到數據庫 , 將瞬時對象轉換持久對象 
持久化對象,不允許隨便修改 OID
 
    2) update 更新數據 ,主要用於脫管對象的更新(持久對象,可以根據快照自動更新 ), 將脫管對象轉換持久對象        
@Test
// 脫管對象更新
publicvoid demo6(){
// 獲得Session
Session session =HibernateUtils.openSession();
// 開啟事務
Transaction transaction = session.beginTransaction();
 
Book book =newBook();// 瞬時
book.setId(1);// 脫管
book.setName("java入門");
book.setPrice(40d);
 
session.update(book);// 持久
 
session.flush();
 
// book.setPrice(50d);
 
// 提交事務,關閉Session
transaction.commit();
session.close();
}
        問題一: 調用update,默認直接生成update語句,如果數據沒有改變,不希望生成update
    在hbm文件 <class>元素 添加 select-before-update="true"
<classname="cn.itcast.domain.firstcache.Book"table="book"catalog="hibernate3day2"select-before-update="true">
問題二: 當update,脫管對象變為持久對象, 一級緩存不允許出現相同OID 兩個持久對象
@Test
// 一級緩存 存在兩個相同OID 持久態對象 報錯
publicvoid demo7(){
// 獲得Session
Session session =HibernateUtils.openSession();
// 開啟事務
Transaction transaction = session.beginTransaction();
 
// 查詢
// Book b = (Book) session.get(Book.class, 1); // 持久
 
Book book =newBook();// 瞬時
book.setId(1);// 脫管
book.setName("java入門");
book.setPrice(50d);
session.update(book);// 持久
 
// 提交事務,關閉Session
transaction.commit();
session.close();
}
    org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [cn.itcast.domain.firstcache.Book#1]
問題三: 脫管對象 OID 在數據表中不存在,update時,發生異常
    org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [cn.itcast.domain.firstcache.Book#20]

    3) saveOrUpdate , 如果參數是一個瞬時對象執行save, 如果參數是一個脫管對象執行update, 如果參數是持久對象直接返回
判斷對象是瞬時對象 : OID為null , 在hbm文件中為 <id>元素指定 unsaved-value屬性,如果PO對象OID為 unsaved-value 也是瞬時對象
<id name="id" unsaved-value="-1">  如果對象 OID為-1 也是瞬時對象,此時執行的是save操作
@Test
// PO對象,OID為 hbm文件 配置 unsaved-value 也是瞬時對象, saveOrUpdate 執行 save操作
publicvoid demo8(){
// 獲得Session
Session session =HibernateUtils.openSession();
// 開啟事務
Transaction transaction = session.beginTransaction();
 
Book book =newBook();// 瞬時
book.setId(-1);// 存在OID , -1是unsaved-value 也是瞬時
book.setName("xxx");
book.setPrice(100d);
 
session.saveOrUpdate(book);
 
// 提交事務,關閉Session
transaction.commit();
session.close();
}
 
    4) get/load
        如果查詢OID不存在, get方法返回 null , load 方法返回代理對象 (代理對象初始化時拋出 ObjectNotFoundException )
 
    5) delete 方法既可以刪除一個脫管對象, 也可以刪除一個持久化對象
        **如果刪除脫管,先將脫管對象 與 Session 關聯,然后再刪除
**執行delete,先刪除一級緩存數據,在session.flush 操作時,刪除數據表中數據


Hibernate二級緩存相關問題

1.二級緩存的相關介紹

緩存好處: 將數據庫或者硬盤數據,保存在內存中,減少數據庫查詢次數,減少硬盤交互,提高檢索效率

    hibernate 共有兩個級別的緩存

        * 一級緩存,保存Session中, 事務范圍的緩存

        * 二級緩存,保存SessionFactory 進程范圍的緩存

    SessionFacoty 兩部分緩存

    內置 Hibernate 自帶的, 不可卸載. 通常在 Hibernate 的初始化階段, Hibernate 會把映射元數據和預定義的 SQL 語句放到 SessionFactory 的緩存中, 映射元數據是映射文件中數據的復制, 而預定義 SQL 語句時 Hibernate 根據映射元數據推到出來的. 該內置緩存是只讀的.

 

    外置 一個可配置的緩存插件. 在默認情況下, SessionFactory 不會啟用這個緩存插件. 外置緩存中的數據是數據庫數據的復制, 外置緩存的物理介質可以是內存或硬盤,必須引入第三方緩存插件才能使用。


2.二級緩存的內部結構以及存儲特點
Hibernate二級緩存分為:   
    * 類緩存區域
    * 集合緩存區域
    * 更新時間戳區域
    *
查詢緩存區域

68725fa5-da27-4eff-9b0b-080d2051517e
類緩存區數據存儲特點
* 從二級緩存區返回數據每次地址都是不同的(散裝數據 每次查詢二級緩存,都是將散裝數據構造為一個新的對象
4429f183-caf3-4141-ba88-a984f97bac02
集合緩存區
415e97a5-0600-4ccd-a964-f37892470bd7
如果注釋掉 Order類緩存,orders 集合無法緩存
* 集合緩存區數據緩存依賴類緩存區數據緩存

 

** 一級緩存的操作會同步到二級緩存

更新時間戳區域

作用:記錄數據最后更新時間,確保緩存數據是有效的

    Hibernate 提供了和查詢相關的緩存區域:

    **時間戳緩存區域: org.hibernate.cahce.UpdateTimestampCache
    時間戳緩存區域存放了對於查詢結果相關的表進行插入 , 更新或刪除操作的時間戳.  Hibernate 通過時間戳緩存區域來判斷被緩存的查詢結果是否過期, 其運行過程如下 :
    T1
時刻執行查詢操作, 把查詢結果存放在 QueryCache 區域, 記錄該區域的時間戳為 T1
    T2
時刻對查詢結果相關的表進行更新操作, Hibernate T2 時刻存放在 UpdateTimestampCache 區域.
    T3
時刻執行查詢結果前, 先比較 QueryCache 區域的時間戳和 UpdateTimestampCache 區域的時間戳, T2 >T1, 那么就丟棄原先存放在 QueryCache 區域的查詢結果, 重新到數據庫中查詢數據, 再把結果存放到 QueryCache 區域; T2 < T1, 直接從 QueryCache
中獲得查詢結果。
8dd017ca-54a8-4691-9834-e9aea4ff8db0
** 更新時間戳區域,記錄數據最后更新時間,在使用二級緩存時,比較緩存時間t1 更新時間 t2  如果 t2 > t1 丟棄原來緩存數據,重新查詢緩存
查詢緩存
    有人稱查詢緩存 為hibernate 第三級緩存

* 二級緩存緩存數據都是類對象數據,數據都是緩存在 "類緩存區域" ,二級緩存緩存PO類對象,條件(key)id

    查詢緩存適用場合:

        **應用程序運行時經常使用查詢語句

        **很少對與查詢語句檢索到的數據進行插入, 刪除和更新操作

    如果查詢條件不是id查詢, 緩存數據不是PO類完整對象 =====> 不適合使用二級緩存

查詢緩存: 緩存的是查詢數據結果, key是查詢生成SQL語句  , 查詢緩存比二級緩存功能更加強大

 
適用查詢緩存的步驟
    1)配置二級緩存(查詢緩存依賴二級緩存)

    2)啟用查詢緩存 hibernate.cfg.xml

 

<property name="hibernate.cache.use_query_cache">true</property>
    3)必須在程序中指定使用查詢緩存

 

query.setCacheable(true);
 
3.二級緩存的並發策略

    transactional  提供Repeatable Read事務隔離級別,緩存支持事務,發生異常的時候,緩存也能夠回滾

    read-write     提供Read Committed事務隔離級別,更新緩存的時候會鎖定緩存中的數據

    nonstrict-read-write 導致臟讀, 很少使用

    read-only      數據不允許修改,只能查詢

* 很少被修改,不是很重要,允許偶爾的並發問題, 適合放入二級緩存。考慮因素二級緩存監控【后面學習】,它是是否采用二級緩存主要參考指標)

4.Hibernate支持哪些二級緩存技術?
    EHCache  ( 主要學習,支持本地緩存,支持分布式緩存 )
        可作為進程范圍內的緩存, 存放數據的物理介質可以是內存或硬盤, 對 Hibernate 的查詢緩存提供了支持。
    *  OSCache
        可作為進程范圍內的緩存, 存放數據的物理介質可以是內存或硬盤, 提供了豐富的緩存數據過期策略, 對 Hibernate 的查詢緩存提供了支持
    *  SwarmCache
        可作為集群范圍內的緩存, 但不支持 Hibernate 的查詢緩存
    *  JBossCache
        可作為集群范圍內的緩存, 支持 Hibernate 的查詢緩存


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM