Hibernate詳講

一 概述

1.JPA

Java Persistence API,是Java EE爲ORM框架定義的規範,任何使用java語言的ORM框架都必須實現該規範。Hibernate/Mybatis都是是JPA的一種實現。java

2.ORM

Object Relational Mapping,對象到關係的映射,在關係型數據庫與對象之間創建映射關係,以實體對象操做關係型數據庫。程序員

3.持久化

將對數據庫的操做永久地反映到數據庫中的過程叫作持久化。數據庫

4.持久層框架

封裝了對數據庫進行持久化操做的框架叫作持久化框架。所謂框架就是在內部實現了一些經常使用的基本操做,提供簡單的接口,縮減操做步驟,並且擴展了功能。緩存

二 數據庫鏈接池

1.背景

數據庫鏈接是一個極其有限的寶貴資源,在Web應用程序中表現得尤其突出。Web應用訪問量大,而數據庫支持的併發鏈接數目是有限的,鏈接越多,速度越慢,下降了用戶體驗。安全

在數據庫鏈接池產生之前,訪問數據庫須要先創建鏈接,訪問結束後關閉鏈接,下次須要時再重複建立與關閉過程,而數據庫鏈接的建立與關閉自己消耗大量的系統資源,這時產生了共享數據庫鏈接的思想,數據庫鏈接池應運而生。session

2.數據庫鏈接池的含義

  • 數據庫鏈接池提供與管理用於訪問數據庫的鏈接。
  • 數據庫鏈接池在初始化階段會建立必定數量的數據庫鏈接,投放到數據鏈接池中。
  • 用戶訪問數據庫時不建立鏈接,而是從數據庫鏈接池中獲取鏈接,訪問完畢後,不關閉鏈接,而是將鏈接歸還鏈接池,以實現鏈接的重複使用。

3.經常使用數據鏈接池

  • C3P0是一種開放源代碼的JDBC鏈接池。
  • DBCP。

4.配置數據源

Hibernate框架推薦使用C3P0:併發

<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>

三 Hibernate簡介

1.基本流程

2.Configuration

負責加載配置文件,啓動Hibernate。配置文件的默認名稱爲「hibernate.cfg.xml」,放在類路徑下。app

3.SessionFactory

負責建立Session對象,保存了當前數據庫中全部的映射關係,線程安全。重量級對象,初始化過程耗費大量的資源,所以在應用程序中應避免建立多個SessionFactory對象。框架

4.Session

⑴含義

表明應用程序與數據庫的一次會話,是Hibernate中持久化操做的核心,直接負責全部的持久化操做。ide

⑵非線程安全

session對象線程不安全,應避免多個線程共享一個Session對象。多個線程共享一個session對象,可能會由於session緩存的存在出現數據泄露,或者一個線程關閉了session對象,另外一個線程出現異常。

⑶依賴事務

Session只能在事務內部運行,即未開啓事務,沒法運行Session。

⑷建立

Session session=sessionFactory.openSession();
Session session=sessionFactory.getCurrentSession();
  • 第一種方式不須要配置,直接使用,第二種方式必須在配置文件中配置,配置以下:
    <property name="current_session_context_class">thread</property>
  • openSession每調用一次建立一個Session對象,getCurrentSession在同一個線程內部得到的是同一個Session對象。
  • 第二種方式建立的對象與線程綁定,第一種未與線程綁定。第二種方式建立的對象在事務提交或者回滾後自動關閉,第一種方式建立的鏈接必須手動關閉。

⑸關閉

  • Session一旦被關閉,就與線程解除了綁定關係,下一次經過該線程得到的是不一樣的Session對象。
  • Session關閉之後,斷開了與數據庫的鏈接,就沒法經過該Session對象訪問數據庫。

Session一旦關閉,就標誌着應用程序與數據庫的一次會話結束,再次通訊須要創建新的會話。

5.主鍵生成策略

生成策略 含義 維護方
increment 獲取數據庫中當前主鍵的最大值,加1做爲新數據的主鍵 Hibernate
identity 按照自增方式生成主鍵 數據庫
native 從底層數據庫支持的主鍵生成策略中選擇 數據庫
assigned 由程序員手動生成主鍵 程序員
sequence 基於sequence表生成主鍵 數據庫
uuid 生成一個全球惟一的32位主鍵  數據庫
foreign 根據另外一張表的主鍵生成主鍵,造成一對一主鍵關聯 Hibernate

四 持久化對象的狀態

1.瞬時狀態

對象被建立、未被Session引用時的狀態,與數據庫中的數據無鏈接,數據庫不存在對應數據,一旦被Session經過save或者saveOrUpdate調用方法轉化爲持久化狀態

2.持久化狀態

對象被session引用,出如今session緩存時處於持久化狀態。對象只有處於持久化狀態,對其進行的操做纔會持久到數據庫中。

3.脫管狀態

當Session對象關閉之後,持久化對象由持久狀態轉化爲脫管狀態,持久化對象處在脫管狀態下仍然與數據庫中的數據存在關聯,數據庫中存在對應數據,經過update或者saveOrUpdate轉化爲持久化狀態。

脫管狀態與瞬時狀態對比:

  • 相同點:都不在session緩存中。
  • 不一樣點:脫管狀態的對象肯定存在於數據庫中,主要用於修改數據庫中的數據,經過update轉化爲持久化狀態;瞬時狀態的對象不存在於數據庫中,主要用於插入數據,經過save轉化爲持久化狀態。

4.集合角度

假如把關係型數據庫中的表看作一個持久化類,每一行記錄都是一個該類的對象,即持久化對象,一個對象若是在集合以外,則處於瞬時狀態;在集合以內而且正在被Session調用,處在持久化狀態;若是Session關閉,處在脫管狀態。

 5.內存地址

對象的狀態發生改變時,對象在內存中的地址並未發生改變,仍然是同一對象,只是對象與數據庫的關係發生改變。

五 持久化操做

1.通常步驟

2.插入數據

session.save(Object obj);//底層生成一條insert語句

3.查詢數據

⑴get

Object obj=(Object)session.get(Object.class,Serializable id);//底層生成一條select語句

⑵load

Object obj=(Object)session.get(Object.class,Serializable id);//底層生成一條select語句

返回對象的代理,在對象被調用時,才向數據庫發送SQL語句。

4.刪除數據

Object obj=session.get(Object.class,Serializable id);
session.delete(obj);//底層生成一條delete語句

5.修改數據

Object obj=session.get(Object.class,Serializable id);
obj.setter();//底層生成一條update語句

6.其餘方法

  • clear():清空Session緩存,取消全部未執行的操做。
  • evict():當即將Session緩存中對象的引用變量賦值爲null,不會生成SQL語句。

7.操做的本質

假定session緩存中保存的是對象自己,而不是對象的引用,那麼清空session緩存,會銷燬對象,測試一下:

    @Test
    public void testClear() { Session session = HibernateUtils.getSession(); try { session.beginTransaction(); Student stu = session.get(Student.class, 1); System.out.println("stu=" + stu); session.clear(); System.out.println("stu=" + stu); } catch (Exception e) { e.printStackTrace(); session.getTransaction().rollback(); } }

輸出:

很明顯,session緩存清空之後,依然能夠輸出對象的詳情,說明對象依然存在,從而得出結論:session緩存中保存的不是對象,而是對象的引用。

加載完成之後,在session緩存中創建一個對象的引用變量,而且使引用變量stu也指定該對象。

  • get()/load()/save(stu):在session緩存中創建引用變量stu,指向內存中的對象obj。
  • delete(stu):不是從內存中刪除對象obj,而是使session緩存中的引用變量「stu0=null」。
  • update(stu):對數據庫的修改直接來源於session緩存,因此在執行修改操做前必須保存session緩存中存在該對象,即session緩存中存在對象的引用。

六 同步時間點與刷新時間點

1.同步時間點

事務提交是Hibernate中惟一的同步時間點,事務提交時將Session緩存中的數據同步到數據庫。

2.刷新時間點

遇到Query查詢、flush方法、事務提交時,執行刪除與修改操做更新session緩存中的內容,只有在事務提交時才同步到數據庫。

3.本質

同步時間點與刷新時間點所作的工做就是將以面向對象形式對數據的操做轉化爲以SQL形式對數據庫的操做,由於不管框架怎麼封裝,底層數據庫所能識別與執行的命令只有SQL語句,框架提供了面向對象這種相對簡單的操做數據庫的方式,底層再將這種方式轉化爲數據庫接受的方式。

刷新時間點與同步時間點就是啓動轉化的時機。刷新時間點將轉化的SQL語句寫入session緩存,同步時間點將轉化的SQL語句寫入數據庫。

4.flush

修改緩存中已有的對象,好比修改對象屬性值,刪除對象,不會清空緩存,不會同步到數據庫,不影響那些與緩存內容無關的操縱,好比save\get操做不受flush影響,當即執行。

5.快照約束

同步時間點與刷新時間點都受快照的約束。

七 緩存

1.Hibernate緩存結構

將從數據庫中加載的對象分別保存在內存的3個區域,各區域之間相互獨立,互不影響。

2.一級緩存與二級緩存內容結構

一級緩存與二級緩存中的內容都以Map集合的形式存儲,key是持久化類與惟一性標識(id)的組合,value是對象的引用變量。

判斷一級緩存或者二級緩存中是否存在一個對象的依據是,該對象的持久化類與惟一性標識構成的組合是否存在於Map集合中。

3.一級緩存

⑴生命週期

一級緩存是Session級的,生命週期與Session相同,隨Session的建立而建立,隨Session的銷燬而銷燬。

⑵內部共享

Session緩存中的數據在Session內部共享,Session之間不共享。

4.快照

⑴做用

快照內存中一塊存儲區域,提供了一種修改審查機制。

⑵基本原理

事務提交時,將緩存中準備同步到數據庫中的內容與快照中的內容進行對比,不一致才同步到數據庫,避免了對數據庫沒必要要的訪問,減輕了數據庫的負擔。

5.二級緩存

⑴做用範圍

二級緩存即SessionFactory緩存,爲全部Session對象共享。

⑵配置

Hibernate自己並未實現二級緩衝,須要使用第三方插件,其中一種插件爲EHCache,註冊後纔可以使用

  • 開啓二級緩存:
  • <property name="hibernate.cache.use_second_level_cache">true</property>
  • 二級緩存對應內存一塊區域,須要指明該區域的管理者,即二級緩存提供商:
  • <property name="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
  • 在src目錄下加入緩衝配置文件:ehcache.xml。

實體類只有在二級緩存中設定了使用策略以後才能夠緩存在二級緩存中,設定使用策略有兩種方式:

  • 在映射文件中配置:
  • <class>
         <cache usage="read-only"/>
    </class>
  • 在配置文件中配置:
  • <class-cache class=""usage=""/>

6.Query緩存

⑴什麼是Query緩存?

用來存儲Query查詢結果的內存區域,獨立於一級與二級緩存,供Query查詢使用。

⑵開啓

Hibernate默認關閉了Query緩存,若是須要使用,須要在配置文件中開啓:

<property name="cache.use_query_cache">true</property>

⑶存儲結構

Query緩存不只包含查詢結果,並且包含hql語句,搜索Query緩存時,首先對比hql語句,Query緩存只有對徹底相同的hql語句可見。

⑷使用

Query q=session.createQuery(hql);
List list=q.setCacheable(true).list();

八 實體關聯關係映射

1.多對一單向關聯

在多的一段添加外鍵:

<many-to-one name="屬性名"class="一方全限定性類名">
     <column name="在本表中添加的外鍵字段">//多的一方表中外鍵名
</many-to-one>

2.多對一雙向關聯

//多方映射文件
<many-to-one name="屬性名" class="一方">
      <column name="外鍵">
</many-to-one>

//一方映射文件
<set name="屬性名" inverse="true">
      <key column="外鍵"/>
      <one-to-many class="多方"/>
</set>
  • inverse:指明關聯關係由哪一方控制,關聯關係是經過外鍵創建起來的,所以維護關聯關係就是維護外鍵,默認爲false,雙方均可控制,有可能致使數據重複;取值true,由另外一方維護關聯關係。
  • 沒有維護權的一方嘗試維護關聯關係時,沒法修改外鍵字段。
  • 爲了防止出現雙方都放棄維護權的狀況,只有一方纔有權利設置維護權。
  • 在MySQL中關聯關係只能由父表維護,在Hibernate中子表也能夠維護關聯關係,底層實現也是遵循MySQL規則的。

3.一對一主鍵關聯

一個實體與另外一個實體一一對應,能夠經過主鍵關聯實現這種關聯關係,造成表與表之間的一對一主鍵關聯。也能夠在一張表中創建外鍵,外鍵字段惟一,引用另外一張表的主鍵,造成一對一外鍵關聯。

一對一主鍵關聯的核心是一張表的主鍵由另外一張表的主鍵生成,即從另外一張表的主鍵中取值,至關於主鍵又做爲外鍵,引用一張表的主鍵。

映射文件的建立:

<id>
     <generator class="foreign">
         <param name="property">當前實體中另外一個實體的引用變量</param>
     </generator>
</id>
<one-to-to name="引用變量名"class="類名"constrained="true"/>

constrained:用來設定子表與父表之間是否存在外鍵約束,便是否把子表的主鍵當作外鍵關聯父表的主鍵。可取值false/true,取值爲false時,兩張表孤立,無任何關係,取值爲true時,子表的主鍵引用父表的主鍵。通常設定爲爲true。

4.一對一外鍵關聯

一對一外鍵關聯是多對一外鍵關聯的特例,將外鍵字段設定爲不可重複,即由多對一外鍵關聯轉化爲一對一外鍵關聯。

<many-to-one name=""class=""unique="true">
      <column name=""/>
</many-to-one>

5.多對多關聯

將多對多關聯分解成2個一對多關聯,提供第三張表做爲中間表,做爲多的一方。

<set name="屬性名"table="tb_mid">
      <key column="中間表中的外鍵"/>
      <many-to-many class=""column="關聯實體在中間表中的外鍵"/>
</set>
  • 多對多關聯的實現是在中間表中建立了一個聯合主鍵,聯合主鍵的每個字段都是外鍵,分別引用一張表的主鍵。
  • 在雙向關聯中,因爲數據之間的相互引用,若是設置了級聯刪除,那麼一旦刪除一條數據,整個數據庫中就可能被清空,所以在雙向關聯中儘可能不設置級聯刪除。

6.自關聯

⑴什麼是自關聯?

實體A與實體B的屬性徹底相同,其中一方包含另外一方,據此建立一個實體,包含A與B雙向的關聯關係。

⑵目的

由於兩個實體屬性全相同,存在共用一張表的可能,放在一張表中不只能夠節省空間,並且能夠提升查詢效率。

⑶處理措施

將自關聯看做多對一雙向關聯。

九 級聯操做

1.什麼是級聯操做?

相關聯的兩方,一方爲主動方,另外一方爲被動方,當主動方發生變更時,被動方產生一樣的變更,這種變化上的關聯關係叫作級聯。

2.做用

一張表中的某個字段引用另外一張表中的某個字段,爲了保證源字段發生該表時,引用字段作出一樣的改變,保證數據的正確性,能夠設置級聯操做。

3.cascade

在映射文件中級聯屬性用cascade表示,可取值:

  • all:所用狀況下均採用級聯操做。
  • none:默認狀況。所用狀況下均不採用級聯操做。
  • save-update:只有在主動方執行save或者update操做時,被動方纔發生相應的改變。
  • delete:只有在主動方執行delete操做時,被動方纔發生相應的改變。

4.all-delete-orphan

刪除與當前對象不存在關聯的對象,只能從父表刪除,由於可能子表中多個對象引用同一父表對象,若是從子表刪除,那麼子表中引用該父表的其餘對象也可能被刪除,而這是不指望的。

Child oneChild=session.get(xxxxx);//首先獲取子對象
parentObj.remove(oneChild);//解除子對象與父對象之間的關係,將子對象從子表中刪除

不設置成all-delete-orphan時,只解除關聯關係,即子表中對應數據的外鍵爲空。

十 檢索優化

1.優化切入點

  • 加載對象的時機,經過屬性lazy設定。
  • 加載對象的方式,經過屬性fetch設定,取值join時採用左外鏈接加載,延時加載失效;取值select時,兩張表分別查詢。 

2.多端加載優化

對一方做爲主加載方加載多方的操做進行優化。

lazy可取值:

  • true:延遲加載,只有在訪問多端對象中的未知數據數據時才加載多端對象。
  • false:在加載一段對象時,當即加載多端對象。
  • extra:相比true更加延遲加載,若是當前操做可以經過其餘不加載詳情的方式實現,就不加載詳情。

3.一端加載優化

對多方做爲主加載方加載一方的操做進行優化。

lazy可取值:

  • false:不採用延時加載,當即加載。
  • proxy:是否當即加載由一方在映射文件中的設置決定。

4.默認狀況

不管是一端加載,仍是多端加載,默認採用延時加載,分別查詢的方式。

十一 實體繼承關係映射

1.類繼承關係樹映射成一張表

將存在繼承關係的多個實體映射成一張表,表中既有共有字段,又有特有字段,爲了代表數據從屬的實體,增長一個身份標識字段:

<discriminator column="字段名"type="string">//身份標識字段,緊跟主鍵定義
//省略父類屬性映射
<subclass name=""class=""discriminator-value="身份名">
   <property name=""/>//特有字段定義
</subclass>

2.子類新增屬性單獨映射成一張表

父類映射成一張表,子類新增屬性映射成一張表,子類主鍵做爲外鍵引用父類的主鍵,實現表的鏈接:

<joined-subclass name="全限定性類名"table="子表名">
   <key column="id">//在子表中增長外鍵,關聯本表的主鍵,該外鍵同時是子表的主鍵
   <property name=""/>
</joined-class>

3.子類映射成一張獨立的表

將子類繼承屬性與新增屬性映射到一張表中,該表爲子類獨有。

<class name="父類"abstract="true">//由於只爲子類建立表,不爲父類建立表,因此將父類定義爲abstract
   <id name="id">
       <generator class="assigned"/>//主鍵由程序負責生成
   </id>
-------------------xxxxxxxxxxxxxx-----------------------
   <union-subclass name="子類"table="子表名">
       <property name="子類新增屬性名"/>
   </union-subclass>
</class>
  • 由於父類多是一個抽象類,現實中不存在對應的對象,只是充當過渡環節,所以沒有必要爲父類建立表,因此將父表定義爲abstract,表示不爲該實體建立表。
  • 主鍵由程序負責生成。

十二 HQL

1.什麼是HQL?

Hibernate Query Language,一種徹底面向對象的查詢語言,查詢範圍是對象的集合,返回結果也是對象的集合。

2.基本語法

select obj.attr from Object obj where obj.attr xxxx group by xxxx having order by xxx;

3.基本操做

String hql="from xxxx";
Query q=session.createQuery(hql);
List list=q.list();//以List集合返回查詢結果

4.參數綁定機制

Hibernate參數綁定機制提供了兩種佔位符,不一樣的佔位符有不一樣的操做:

  • ?:採用索引肯定佔位符,索引從0開始。爲佔位符賦值:
  • Query.setParameter(0,"具體指");
  • :parameterName:採用參數名肯定佔位符。爲佔位符賦值:
  • Query.setParameter("parameterName","具體指");

5.limit

hql語句中支持limit關鍵字,分頁查詢能夠經過如下方式實現:

String hql="不含hql";
Query q=session.createQuery(hql).setFirst(beginIndex).setMaxResults(length);

6.namedQuery

若是修改了hql語句,不但願從新編譯java文件,能夠預先把hql語句定義在映射文件中,在java代碼中經過名稱調用hql語句,這種查詢方式叫作命名查詢。實現方式:

<class>
    xxxxxxxxxxxxxxx
</class>
<query name="namedQuery">hql</>

Query q=session.getNamedQuery("namedQuery");

十三 註解式開發

在配置文件中註冊實體類 :

<mapping class="實體類全限定性類名" />

經常使用註解:

  • @Entity:註釋在類名上,代表該類是一個實體類。
  • @Table:註釋在類名上,指明該類映射的表名,省略時採用類名做爲表名。
  • @Id:註釋在標識屬性上,代表該屬性映射爲主鍵。
  • @GeneratedValue(Strategy=GenarationType.xxxx):註釋在@Id下面,設置主鍵生成策略。
  • @Basic:註釋在非標識屬性上,將該屬性映射爲非主鍵字段,是一個默認註釋,能夠取消。
  • @Column:註釋在@Basic下面,設定字段名稱,能夠省略,省略時採用屬性名做爲字段名。
  • @Transient:註釋在不創建映射字段的屬性上,必須註釋,由於系統默認將全部屬性映射到表中。
相關文章
相關標籤/搜索