深刻淺出hibernate總結

1、總括
 
一、sessionFactory與session
 
(1)sessionFactory:一個數據庫對應一個factory,線程安全、單例,通常隨應用開啓和關閉。
 
(2)session:非線程安全,管理connection(池)
 
session對於sessionFactory的關係至關於conn對於jdbc的關係,但session不等同於connection。
 
(3)另外,通常是一個session對一個事務。
 
二、hibernate不適合的場景
(1)複雜查詢/大批量數據導出
(2)批量修改/刪除
(3)實體關係過於複雜
 
三、鏈接池與prepareStatement池的原理
 
    鏈接池,在getConn與closeConn的時候,不是真正的建立和關閉,採用代理攔截,在get的時候,從池中拿,若是有則返回,沒有則建立一個返回,歸還的時候,若是池沒有滿,則放入池中,若是滿了,則真正關閉。
 
  (補上示例代碼)
 
    prepareStatement池的原理就是緩存ps語句,不讓其隨着conn的關閉而失效。步驟就是攔截ps的close方法,而後每次create的時候,從池中查找,若是有則返回,沒有則建立,每次close的時候,看池有沒有滿,若是有滿,則關閉,沒有則放入池中。
 
(補上示例代碼)
 
 
四、hibernate configure的配置
 
     Configuration cfg=new Configuration();
     cfg.addClass();
     cfg.addFile(「xxx.hbm.xml」);
 
或者
 
    Configuration cfg=new Configuration().configure("myhibernate.cfg.xml");
   
    默認從classpath去讀取hibernate.properties和hibernate.cfg.xml
 
 
五、實體狀態
 
     一旦事務提交,實體就變成detached狀態
 
2、關係映射
 
一、一對多
 
     @oneToMany()
     好比一個產品有多個價格,則在product裏面的一對多,要mappedby=「product」,告訴hibernate不要建立中間表,採用product的外鍵來映射
     inverse=true,即本身無論理關係,就是在保持product和price的時候,product不去管理(set/add)本身有多少個price,讓price去setProduct。這樣子,就能夠減小update 多端表的外鍵值的sql。
 
     @joinColumn:設置外鍵值
     @primaryKeyJoinColumn:即多的一段的主鍵就是其與主錶鏈接的外鍵,其值就是主表的主鍵值
   
 
   通常設置雙向關聯:
單向關聯會額外發sql更新,好比,User與Address,先saveAddress,再saveUser,而後再發sql更新address的user外鍵值
雙向關聯,user的inverse設爲true,而後先saveuser,在saveAddress
 
user的inverse爲true表示讓address來維護關係,即保存address的時候以下操做:
 
add.setUser(user);
user.getAddresses().add(add);
session.save(add);
 
 
二、一對一映射與組件映射
 
    一對一映射,從屬類有本身的id,而組件映射從屬類沒有本身的id
 
    OneToOne,好比User與Passport,每一個passport都有本身的證件號
    組件映射,好比User與Address/Name(first/last)
 
    一對一,constraint=true,告訴hibernate,passport引用了user的主鍵
    outer-join爲true的話,即加載User時,left-outer-join了passport
                       false的話,分兩次查,一次查user,一次查passport
 
   外鍵關聯,與一對多的外鍵關聯相似
 
 
 
三、繼承映射
(1)每一個子類一張表 union
 
        每一個子類的表都有共同的字段,當父類的字段要該的時候,則要該全部子類。
 
      好處:自動隱式多態
 
 
(2)子類+父類都有表, join
 
      爲了不重複,父類抽出來一張表,子類查詢,left-outer-join
 
(3)一個繼承體系一張表,dicriminator     
     爲解決一、2的性能問題而引人
      缺點是有字段空缺,但好處是一張表,性能好
 
 
四、查詢多態
 
    polymorphism=explict,顯示多態,即在查詢的時候,要顯示指定是要從父類仍是從子類查詢
 
    createQuery("from Dog").list();
    createQuery("from Aminal").list();
 
    from Object,是隱式的多態,implicit,默認查詢的時候會加載父類的全部子類
 
五、多對多映射
 
    好比group與role,role與right,得雙向各自add值
 
 
3、查詢與緩存
 
0、DetachedCriteria
   在無session的狀況下可用,可用脫離session存在,在執行時綁定session,可用在方法裏面傳參數。
 
通常是推薦用hql來查詢
 
from TUser,是返回TUser的list
select u.name from TUser u,返回的是List<Object[]>
不過select new TUser() 返回的是非託管的類集合
 
一、組合查詢
 
     採用example的方法能夠減小if else的判斷
 
二、統計查詢
     setProjection, projection.avg()/rowCount()/max()/min()/distinct
 
三、加載個別字段
 
     List<Object[]>,select a,b,c from TUser
 
     List<TUser> , select new TUser(a,b,c) from TUser
 
    注意,後者加載出來的是非託管狀態的,默認不加載oid,保持的時候,能夠考慮merge
 
四、四種加載方式
 
(1)即時加載(immediate)
 
        先加載實體,在發出N個sql去加載其餘關聯實體
 
(2)延遲加載(lazy)
        加載實體通常屬性,而後按需加載關聯屬性。
 
        延遲加載,必須在session開的狀態,並且是get的時候加載的。若是想在session關閉以前,強制加載全部集合的話,能夠在session關閉以前用hibernate.intialize(set);
 
       屬性的延遲加載:
A、對同一個表創建不一樣粒度的實體映射,好比從user表拆出resume類
B、在select的時候限定加載屬性列表
C、property設置lazy=true
 
(3)預先加載(eager)
 
         一條sql,outer join 關聯屬性一次性加載
 
(4)批量加載(batch)
         能夠認爲與上面沒有多少關係,主要是設置batchsize,積累相似的sql到必定量再批量處理,好比select * from user where id=1/2/3/4/5,這5條累積一塊兒批量執行。
 
   
    集合方式分無序的set、bag、map 與有序的list
 
其中bag容許存在重複的元素
 
    排序的話:
A、在JVM內排序,comparator藉口
B、在db內排序,order by (針對set、bag、map)
 
 
五、session.get與load的區別
 
    get的話,返回真實實體,若是沒有找到則返回null,只查一級緩存
    load的話,若是lazy爲true,返回的是代理類,沒有找到則拋出ObjectNotFound異常,會先查一級緩存,再查二級緩存
 
六、iterate與find的區別
 
    iterate的話,典型的1+N,取id集合,而後去查緩存
    find的話,取全部屬性,而後放入緩存
 
七、fetchtype
   若是爲select的話,則會發一條sql加載集合,批量加載從屬屬性
   若是爲join的話,left outer join, lazy失效
 
八、一級緩存與二級緩存
 
     事務範圍內緩存,一級緩存(session),隨事務結束而結束
     進程範圍/應用範圍緩存,二級緩存(session factory),緩存生命週期與sessionFactory的生命週期一致,適合較少被修改的緩存,非併發的訪問。如ehcache
     集羣範圍內的緩存,如memcache
 
    關聯對象的緩存策略:
set中配置cache-usage=read-only,只緩存數據索引(即id)
在one中配置cache-usage=read-write,能夠緩存集合中的實體
 
九、緩存管理
    A、一級緩存
          寫入:save、update、saveOrupdate、load、get、list、iterate、lock方法都會向緩存中存對象         
          更新:update、saveOrupdate,對於經過sql或query更新的,須要本身更新緩存
          讀取:get、load、iterate,(query.list不會讀一級緩存,只寫入)
          刪除:手動刪除,或者session關閉的時候自動刪除
 
    B、二級緩存
 
             寫入:save、update、saveOrupdate、load、get、list、query、Criteria方法都會填充二級緩存。session.save(user)   若是user主鍵使用「native」生成,則不放入二級緩存.
             更新:update、saveOrupdate、delete,對於經過sql或query更新的,須要本身更新緩存             
             讀取:get、load、iterate會從二級緩存中取數據。若是經過ehcache緩存組件爲Hibernate配置查詢緩存(query cache[hibernate 3.x以上版本]),這Query.list會每次查詢的過程當中先訪問二級緩存中的查詢緩存,若是沒有再執行SQL語句,查詢返回的結果會分別保存在一,二級緩存中。
             刪除:手工清除或者sessionFactory關閉的時候
    
       對於sql的查詢,都是直接從數據庫查,只不過查出來後根據ID放入緩存,只有根據id獲取的時候,才查緩存。(於是,此時1+N纔是發揮威力的時候)
      若是配置了query cache的話,能夠對整個查詢條件的結果集進行緩存。
 
 --------------
     批量導出的時候,採用純sql,不走緩存,獲取較高性能。
 
-----------
 
當應用程序調用Session的save()、update()、savaeOrUpdate()、get()或load(),以及調用查詢接口的list()、iterate()或filter()方法時,若是在Session緩存中還不存在相應的對象,Hibernate就會把該對象加入到第一級緩存中。當清理緩存時,Hibernate會根據緩存中對象的狀態變化來同步更新數據庫。
Session爲應用程序提供了兩個管理緩存的方法:
evict(Object obj):從緩存中清除參數指定的持久化對象。
clear():清空緩存中全部持久化對象。
 
--------
Hibernate的二級緩存策略,是針對於ID查詢的緩存策略,對於條件查詢則毫無做用。爲此,Hibernate提供了針對條件查詢的Query Cache。
 
Hibernate的二級緩存策略(根據ID緩存)的通常過程以下:
1) 條件查詢的時候,老是發出一條select * from table_name where …. (選擇全部字段)這樣的SQL語句查詢數據庫,一次得到全部的數據對象。
2) 把得到的全部數據對象根據ID放入到第二級緩存中。
3) 當Hibernate根據ID訪問數據對象的時候,首先從Session一級緩存中查;查不到,若是配置了二級緩存,那麼從二級緩存中查;查不到,再查詢數據庫,把結果按照ID放入到緩存。
4) 刪除、更新、增長數據的時候,同時更新緩存。
---------
 
十、二級緩存併發策略
(1)read-only
(2)non-strict-read-write(使用更新頻率低)
(3)read-write,讀提交,基於timestamp/version
(4)transactional
 
 
 
 
 
4、持久化與事務
 
一、openSession與getCurrentSession
 
     openSession,永遠建立一個新的session,須要自動關閉conn
     getCurrentSession,查找上下文,若是有session,則與之連在一塊兒,會自動關閉conn
 
二、save、update、delete、lock
    save,查緩存,若是存在,判斷數據是否變化,沒有則直接返回,如有變化,則進行髒數據檢查,而後update
    若是save查緩存不存在,則insert
 
    save方法歸入一級緩存,不歸入二級緩存
 
    update,根據key去session緩存中查找。在事務提交/flush的時候發出updatesql
 
    saveOrUpdate
    根據version/timestamp判斷
 
    delete:先查找再刪除。對於批量刪除,比較費勁,由於若是直接一條sql查找的話,會使緩存與db不一樣步
    但後來也提供bulk delete/update 方法,但仍是能會不一樣步,獲得的是過時的緩存
 
    lock:將一個已經讀入內存但未通過更改的遊離態的對象與session關聯起來,以便實現延遲加載。
 
三、merge
   對於沒有oid的會insert,用於將不在session中管理時的對象變成託管狀態
 
四、flush
   與數據庫同步,累積必定量執行,默認在事務提交的時候執行
 
五、自定義持久化
 
    好比不想更新的時候更新全部字段,如今可使用dynamic update/insert,之前是能夠自定義sql-insert或者sql-update或者sql-delete來指定。
 
六、JDBC事務與JTA事務
 
     hibernate的openSession,在初始化數據庫鏈接的時候,setAutoCommit爲false
     JDBC事務依託於driver的connection,屬於session生命週期內
     JTA事務,依託於JTA容器,跨多個session,甚至多個sessionFactory
 
七、併發異常與數據庫讀寫策略
    併發異常:主要是一類更新,二類更新,髒讀、不可重複讀、幻讀
    讀寫策略:讀未提交、讀提交(大多數據庫默認)、重複讀、串行讀
 
5、其餘
 
   一、對象惟一性的判斷
(1)主鍵,不建議用業務主鍵來當db主鍵
(2)關鍵業務屬性(推薦,由於主鍵可能有null的狀況)
 
   二、判斷是insert仍是update
-->經過unsaved-value,其值默認爲null,好比主鍵,若是對象的id是null,則insert,不然則update
 
   三、回調與攔截機制
 
(1)實現lifecycle接口
(2)validatable接口,實現非業務的數據驗證
 
  上面兩種侵入性強,能夠改用interceptor
==>通常用在audit用,不在攔截裏面作業務相關操做,也不在裏面對session進行實例化操做。
    onSave/FlushDirty/postFlush
 
6、ThreadLocal的session
 
     參考HibernateUtil類
 
public   final   class  HibernateUtil {  
     private   static  SessionFactory sessionFactory;  
     private   static  ThreadLocal session  =   new  ThreadLocal();  
 
     private  HibernateUtil() {  
    }  
 
     static  {  
        Configuration cfg  =   new  Configuration();  
        cfg.configure();  
        sessionFactory  =  cfg.buildSessionFactory();  
    }  
 
     public   static  Session getThreadLocalSession() {  
        Session s  =  (Session) session.get();  
         if  (s  ==  null) {  
            s  =  getSession();  
            session.set(s);  
        }  
         return  s;  
    }  
 
     public   static   void  closeSession() {  
        Session s  =  (Session) session.get();  
         if  (s  !=  null) {  
            s.close();  
            session.set(null);  
        }  
    }  
 
     public   static  SessionFactory getSessionFactory() {  
         return  sessionFactory;  
    }  
 
     public   static  Session getSession() {  
         return  sessionFactory.openSession();  
    }  
 
     public   static   void  add(Object entity) {  
        Session s  =  null;  
        Transaction tx  =  null;  
         try  {  
            s  =  HibernateUtil.getSession();  
            tx  =  s.beginTransaction();  
            s.save(entity);  
            tx.commit();  
        }   finally  {  
             if  (s  !=  null)  
                s.close();  
        }  
    }  
 
     public   static   void  update(Object entity) {  
        Session s  =  null;  
        Transaction tx  =  null;  
         try  {  
            s  =  HibernateUtil.getSession();  
            tx  =  s.beginTransaction();  
            s.update(entity);  
            tx.commit();  
        }   finally  {  
             if  (s  !=  null)  
                s.close();  
        }  
    }  
 
     public   static   void  delete(Object entity) {  
        Session s  =  null;  
        Transaction tx  =  null;  
         try  {  
            s  =  HibernateUtil.getSession();  
            tx  =  s.beginTransaction();  
            s.delete(entity);  
            tx.commit();  
        }   finally  {  
             if  (s  !=  null)  
                s.close();  
        }  
    }  
 
     public   static  Object get(Class clazz, Serializable id) {  
        Session s  =  null;  
         try  {  
            s  =  HibernateUtil.getSession();  
            Object obj  =  s.get(clazz, id);  
             return  obj;  
        }   finally  {  
             if  (s  !=  null)  
                s.close();  
        }  
    }  
}
 
     實現session在單個線程裏面的共享,但要注意的是當線程結束的時候,session就關閉了。
 
     在線程裏面,session也實現自身的延遲加載,即在發生對數據庫操做的時候,才從數據庫獲取conn
 
     在web中的應用,通常一個http請求,一個線程,佔用一個session。長session是指在多個request中重複使用同一個session,最後使用完才關閉,這個比較危險,要慎重。
相關文章
相關標籤/搜索