Java Persistence API,是Java EE爲ORM框架定義的規範,任何使用java語言的ORM框架都必須實現該規範。Hibernate/Mybatis都是是JPA的一種實現。java
Object Relational Mapping,對象到關係的映射,在關係型數據庫與對象之間創建映射關係,以實體對象操做關係型數據庫。程序員
將對數據庫的操做永久地反映到數據庫中的過程叫作持久化。數據庫
封裝了對數據庫進行持久化操做的框架叫作持久化框架。所謂框架就是在內部實現了一些經常使用的基本操做,提供簡單的接口,縮減操做步驟,並且擴展了功能。緩存
數據庫鏈接是一個極其有限的寶貴資源,在Web應用程序中表現得尤其突出。Web應用訪問量大,而數據庫支持的併發鏈接數目是有限的,鏈接越多,速度越慢,下降了用戶體驗。安全
在數據庫鏈接池產生之前,訪問數據庫須要先創建鏈接,訪問結束後關閉鏈接,下次須要時再重複建立與關閉過程,而數據庫鏈接的建立與關閉自己消耗大量的系統資源,這時產生了共享數據庫鏈接的思想,數據庫鏈接池應運而生。session
Hibernate框架推薦使用C3P0:併發
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
負責加載配置文件,啓動Hibernate。配置文件的默認名稱爲「hibernate.cfg.xml」,放在類路徑下。app
負責建立Session對象,保存了當前數據庫中全部的映射關係,線程安全。重量級對象,初始化過程耗費大量的資源,所以在應用程序中應避免建立多個SessionFactory對象。框架
表明應用程序與數據庫的一次會話,是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>
Session一旦關閉,就標誌着應用程序與數據庫的一次會話結束,再次通訊須要創建新的會話。
生成策略 | 含義 | 維護方 |
increment | 獲取數據庫中當前主鍵的最大值,加1做爲新數據的主鍵 | Hibernate |
identity | 按照自增方式生成主鍵 | 數據庫 |
native | 從底層數據庫支持的主鍵生成策略中選擇 | 數據庫 |
assigned | 由程序員手動生成主鍵 | 程序員 |
sequence | 基於sequence表生成主鍵 | 數據庫 |
uuid | 生成一個全球惟一的32位主鍵 | 數據庫 |
foreign | 根據另外一張表的主鍵生成主鍵,造成一對一主鍵關聯 | Hibernate |
對象被建立、未被Session引用時的狀態,與數據庫中的數據無鏈接,數據庫不存在對應數據,一旦被Session經過save或者saveOrUpdate調用方法轉化爲持久化狀態。
對象被session引用,出如今session緩存時處於持久化狀態。對象只有處於持久化狀態,對其進行的操做纔會持久到數據庫中。
當Session對象關閉之後,持久化對象由持久狀態轉化爲脫管狀態,持久化對象處在脫管狀態下仍然與數據庫中的數據存在關聯,數據庫中存在對應數據,經過update或者saveOrUpdate轉化爲持久化狀態。
脫管狀態與瞬時狀態對比:
假如把關係型數據庫中的表看作一個持久化類,每一行記錄都是一個該類的對象,即持久化對象,一個對象若是在集合以外,則處於瞬時狀態;在集合以內而且正在被Session調用,處在持久化狀態;若是Session關閉,處在脫管狀態。
對象的狀態發生改變時,對象在內存中的地址並未發生改變,仍然是同一對象,只是對象與數據庫的關係發生改變。
session.save(Object obj);//底層生成一條insert語句
Object obj=(Object)session.get(Object.class,Serializable id);//底層生成一條select語句
Object obj=(Object)session.get(Object.class,Serializable id);//底層生成一條select語句
返回對象的代理,在對象被調用時,才向數據庫發送SQL語句。
Object obj=session.get(Object.class,Serializable id); session.delete(obj);//底層生成一條delete語句
Object obj=session.get(Object.class,Serializable id); obj.setter();//底層生成一條update語句
假定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也指定該對象。
事務提交是Hibernate中惟一的同步時間點,事務提交時將Session緩存中的數據同步到數據庫。
遇到Query查詢、flush方法、事務提交時,執行刪除與修改操做更新session緩存中的內容,只有在事務提交時才同步到數據庫。
同步時間點與刷新時間點所作的工做就是將以面向對象形式對數據的操做轉化爲以SQL形式對數據庫的操做,由於不管框架怎麼封裝,底層數據庫所能識別與執行的命令只有SQL語句,框架提供了面向對象這種相對簡單的操做數據庫的方式,底層再將這種方式轉化爲數據庫接受的方式。
刷新時間點與同步時間點就是啓動轉化的時機。刷新時間點將轉化的SQL語句寫入session緩存,同步時間點將轉化的SQL語句寫入數據庫。
修改緩存中已有的對象,好比修改對象屬性值,刪除對象,不會清空緩存,不會同步到數據庫,不影響那些與緩存內容無關的操縱,好比save\get操做不受flush影響,當即執行。
同步時間點與刷新時間點都受快照的約束。
將從數據庫中加載的對象分別保存在內存的3個區域,各區域之間相互獨立,互不影響。
一級緩存與二級緩存中的內容都以Map集合的形式存儲,key是持久化類與惟一性標識(id)的組合,value是對象的引用變量。
判斷一級緩存或者二級緩存中是否存在一個對象的依據是,該對象的持久化類與惟一性標識構成的組合是否存在於Map集合中。
一級緩存是Session級的,生命週期與Session相同,隨Session的建立而建立,隨Session的銷燬而銷燬。
Session緩存中的數據在Session內部共享,Session之間不共享。
快照內存中一塊存儲區域,提供了一種修改審查機制。
事務提交時,將緩存中準備同步到數據庫中的內容與快照中的內容進行對比,不一致才同步到數據庫,避免了對數據庫沒必要要的訪問,減輕了數據庫的負擔。
二級緩存即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>
實體類只有在二級緩存中設定了使用策略以後才能夠緩存在二級緩存中,設定使用策略有兩種方式:
<class> <cache usage="read-only"/> </class>
<class-cache class=""usage=""/>
用來存儲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();
在多的一段添加外鍵:
<many-to-one name="屬性名"class="一方全限定性類名"> <column name="在本表中添加的外鍵字段">//多的一方表中外鍵名 </many-to-one>
//多方映射文件 <many-to-one name="屬性名" class="一方"> <column name="外鍵"> </many-to-one> //一方映射文件 <set name="屬性名" inverse="true"> <key column="外鍵"/> <one-to-many class="多方"/> </set>
一個實體與另外一個實體一一對應,能夠經過主鍵關聯實現這種關聯關係,造成表與表之間的一對一主鍵關聯。也能夠在一張表中創建外鍵,外鍵字段惟一,引用另外一張表的主鍵,造成一對一外鍵關聯。
一對一主鍵關聯的核心是一張表的主鍵由另外一張表的主鍵生成,即從另外一張表的主鍵中取值,至關於主鍵又做爲外鍵,引用一張表的主鍵。
映射文件的建立:
<id> <generator class="foreign"> <param name="property">當前實體中另外一個實體的引用變量</param> </generator> </id> <one-to-to name="引用變量名"class="類名"constrained="true"/>
constrained:用來設定子表與父表之間是否存在外鍵約束,便是否把子表的主鍵當作外鍵關聯父表的主鍵。可取值false/true,取值爲false時,兩張表孤立,無任何關係,取值爲true時,子表的主鍵引用父表的主鍵。通常設定爲爲true。
一對一外鍵關聯是多對一外鍵關聯的特例,將外鍵字段設定爲不可重複,即由多對一外鍵關聯轉化爲一對一外鍵關聯。
<many-to-one name=""class=""unique="true"> <column name=""/> </many-to-one>
將多對多關聯分解成2個一對多關聯,提供第三張表做爲中間表,做爲多的一方。
<set name="屬性名"table="tb_mid"> <key column="中間表中的外鍵"/> <many-to-many class=""column="關聯實體在中間表中的外鍵"/> </set>
實體A與實體B的屬性徹底相同,其中一方包含另外一方,據此建立一個實體,包含A與B雙向的關聯關係。
由於兩個實體屬性全相同,存在共用一張表的可能,放在一張表中不只能夠節省空間,並且能夠提升查詢效率。
將自關聯看做多對一雙向關聯。
相關聯的兩方,一方爲主動方,另外一方爲被動方,當主動方發生變更時,被動方產生一樣的變更,這種變化上的關聯關係叫作級聯。
一張表中的某個字段引用另外一張表中的某個字段,爲了保證源字段發生該表時,引用字段作出一樣的改變,保證數據的正確性,能夠設置級聯操做。
在映射文件中級聯屬性用cascade表示,可取值:
刪除與當前對象不存在關聯的對象,只能從父表刪除,由於可能子表中多個對象引用同一父表對象,若是從子表刪除,那麼子表中引用該父表的其餘對象也可能被刪除,而這是不指望的。
Child oneChild=session.get(xxxxx);//首先獲取子對象
parentObj.remove(oneChild);//解除子對象與父對象之間的關係,將子對象從子表中刪除
不設置成all-delete-orphan時,只解除關聯關係,即子表中對應數據的外鍵爲空。
對一方做爲主加載方加載多方的操做進行優化。
lazy可取值:
對多方做爲主加載方加載一方的操做進行優化。
lazy可取值:
不管是一端加載,仍是多端加載,默認採用延時加載,分別查詢的方式。
將存在繼承關係的多個實體映射成一張表,表中既有共有字段,又有特有字段,爲了代表數據從屬的實體,增長一個身份標識字段:
<discriminator column="字段名"type="string">//身份標識字段,緊跟主鍵定義 //省略父類屬性映射 <subclass name=""class=""discriminator-value="身份名"> <property name=""/>//特有字段定義 </subclass>
父類映射成一張表,子類新增屬性映射成一張表,子類主鍵做爲外鍵引用父類的主鍵,實現表的鏈接:
<joined-subclass name="全限定性類名"table="子表名"> <key column="id">//在子表中增長外鍵,關聯本表的主鍵,該外鍵同時是子表的主鍵 <property name=""/> </joined-class>
將子類繼承屬性與新增屬性映射到一張表中,該表爲子類獨有。
<class name="父類"abstract="true">//由於只爲子類建立表,不爲父類建立表,因此將父類定義爲abstract <id name="id"> <generator class="assigned"/>//主鍵由程序負責生成 </id> -------------------xxxxxxxxxxxxxx----------------------- <union-subclass name="子類"table="子表名"> <property name="子類新增屬性名"/> </union-subclass> </class>
Hibernate Query Language,一種徹底面向對象的查詢語言,查詢範圍是對象的集合,返回結果也是對象的集合。
select obj.attr from Object obj where obj.attr xxxx group by xxxx having order by xxx;
String hql="from xxxx"; Query q=session.createQuery(hql); List list=q.list();//以List集合返回查詢結果
Hibernate參數綁定機制提供了兩種佔位符,不一樣的佔位符有不一樣的操做:
Query.setParameter(0,"具體指");
Query.setParameter("parameterName","具體指");
hql語句中支持limit關鍵字,分頁查詢能夠經過如下方式實現:
String hql="不含hql";
Query q=session.createQuery(hql).setFirst(beginIndex).setMaxResults(length);
若是修改了hql語句,不但願從新編譯java文件,能夠預先把hql語句定義在映射文件中,在java代碼中經過名稱調用hql語句,這種查詢方式叫作命名查詢。實現方式:
<class> xxxxxxxxxxxxxxx </class> <query name="namedQuery">hql</> Query q=session.getNamedQuery("namedQuery");
在配置文件中註冊實體類 :
<mapping class="實體類全限定性類名" />
經常使用註解: