[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
在下面的分析中我會給出緣由。

1、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.二級緩存的相關介紹

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

    hibernate 共有兩個級別的緩存 web

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

        * 二級緩存,保存SessionFactory 進程範圍的緩存數據庫

    SessionFacoty 兩部分緩存緩存

    內置 Hibernate 自帶的, 不可卸載. 一般在 Hibernate 的初始化階段, Hibernate 會把映射元數據和預約義的 SQL 語句放到 SessionFactory 的緩存中, 映射元數據是映射文件中數據的複製, 而預約義 SQL 語句時 Hibernate 根據映射元數據推到出來的. 該內置緩存是隻讀的.安全

 

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


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

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

 

** 一級緩存的操做會同步到二級緩存併發

更新時間戳區域app

做用:記錄數據最後更新時間,確保緩存數據是有效的框架

    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 的查詢緩存
相關文章
相關標籤/搜索