posts - 78, comments - 34, trackbacks - 0, articles - 1
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

                   從昨晚后半夜開始下午,現在外面的雪還沒停,這是來北京后最大的一場雪。早上630起床,然后去吃早餐。今天是元旦假期的最后一天,幸好乘車的人不多,很快就坐上了車。如果是平時可能得在大雪里走幾站,甚至走到學校。氣溫還可以,在外面等車時,雪好美!

           

          今日繼續講解hibernate,也是hibernate課程的最后一天。Hibernate的內容非常多,如果詳細著講估計還得兩天,但課程安排是三天。大部分之前的學生說外面用hibernate的很少!~~ 無奈!繼續上一次課程

           

          一、Hibernate的檢索策略

                   上一次課程我們學習了類級別的檢索策略和關聯級別的一對多檢索策略,簡單復習一下:

          一對多關聯檢索:

          Fecth            lazy

          True

          false

          extra

          Join

          迫左

          迫左

          迫左

          Select

          延遲

          立即

          延遲(特別)

          subselect

           

           

           

           

          Fecth的優先級別高于lazy

          延遲(特別):select count(id) from orders where cid=?

          Subselect:使用嵌套子查詢,批量查詢的時候。

           

          多對一關聯檢索:

          Fecth            lazy

          false

          proxy

          No-proxy

          Join

          迫左

          迫左

          迫左

          Select

          立即

          延遲:對端類級別是延遲

          立即:對端類級別是立即

           

          表單所屬的客戶,客戶是表單的一級關聯,再取出客戶對應的所有表單,此時表單是前一個表單的二級關聯。一級關聯、二級關聯,兩次左外連接使用一條語句的話,會降低性能。所以使用了一條左邊連接查詢和一條查詢語句。這就是著名的N+1查詢。

          hibernate3.0的所有檢索策略都為延遲

           

          接下來我們繼續學習關聯級別的一對一檢索策略與多對多檢索策略。

           

          1.一對一檢索策略

                   一對一關聯關系是一種什么樣的關系?一對一關系不可以把他們定義為一個類嗎?把他們放在一個表里不可以嗎?當然可以!

           

                   一對一關聯兩種方案:

          1).外鍵關聯,此種解決方案有兩種方法:

          老徐舉的例子,一個用戶表對應一個地址。為什么要這么做?假設地址中包含大字段,這樣可以減少資源的浪費,提高訪問性能。

          l          方法一、就是在多對一關聯的元素節點中添加一個unique=”true”屬性便形成了一對一關聯。

          在用戶類的映射文件中添加:

          <!-- 影射一對一外鍵關聯用many-to-one進行模擬,增加唯一性約束 -->

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

          在地址類的映射文件中添加:

          <one-to-one name="user" property-ref="addr"/>

          l          方法二、直接使用一對一關系:

          在用戶類的映射文件中添加:

          <!-- 影射一對一主鍵關聯 -->

          <one-to-one name="addr" class="AddrPk" />

          在地址類的映射文件中添加:

          <one-to-one name="user" class="UserPk" constrained="true"/>

                             同時我們需要設置地址類映射文件的主鍵為:

          <id name="id" column="id" type="integer">

              <generator class="foreign">

                 <param name="property">user</param>

              </generator>

          </id>

                   將用戶表的主鍵設置為地址表的外鍵。

           

          2).主鍵關聯,此時子表的主鍵與主表的主鍵一一對應。有三種解決方案:

          老徐舉了一個例子:員工有鐘點工和普通工人之分,鐘點工領取的是時效工資(rate),普通工人領取的是月薪(salary)。所以我們有必要提取一個員工的超類,并分別實現鐘點工和普通工人的子類。

          l          方法一、我們將鐘點工和普通工人的信息放到一個表里,需要在映射文件中添加:

          <!-- 區分符 -->

          <discriminator column="etype" type="string" length="2" />

          <property name="name" column="name" type="string" length="15" />

          <!-- 子類(整個繼承關系樹對應于一個表) -->

          <subclass name="HeSingle" discriminator-value="he" lazy="false">

              <property name="rate" column="rate" type="float" />

          </subclass>

          <subclass name="SeSingle" discriminator-value="se" lazy="false">

              <property name="salary" column="salary" type="float" />

          </subclass>

          注意“discriminator”元素用于定義一個額外的列,用于區分員工的類型。

          l          方法二、我們為每類員工都分別建一個表,如:

          <!-- 每個子類對應一個表,從表和主表間一對一關系 -->

          <joined-subclass name="HeJoined" table="hib_hejoineds" lazy="false">

              <key column="eid" />

             <property name="rate" column="rate" type="float" />

          </joined-subclass>

                

          <joined-subclass name="SeJoined" table="hib_sejoineds" lazy="false">

              <key column="eid" />

              <property name="salary" column="salary" type="float" />

          </joined-subclass>

          column="eid"”被做為外鍵對應主表的員工id

          l          方法三、為每類員工建立一個表,他們與主表無關各自具有全字段。但是這三個表所使用的Id是不重復的:

          <union-subclass name="HeUnion" table="hib_heunions">

              <property name="rate" column="rate" type="float" />

          </union-subclass>

          <union-subclass name="SeUnion" table="hib_seunions">

              <property name="salary" column="salary" type="float" />

          </union-subclass>

                             此時,我們需要使用一個特殊的主鍵增值生成器:

          <id name="id" column="id" type="integer">

              <generator class="hilo">

                 <param name="table">hib_hilo</param>

                 <param name="column">currvalue</param>

                 <param name="max_lo">10</param>

              </generator>

          </id>

          "max_lo"”是id取值區間,每次插入記錄時,生成器會根據此值步長增長。如果我們執行的一次插入操作,插入了10條記錄。那么就需要將此設置為10,以便生成器自動為我們生成10個不同的id

           

           

          2.多對多檢索策略

                   在以前的學習中,我們知道多對多關聯關系需要一個中間表,用于記錄兩個多對多表的對應主鍵。反應到映射文件中,我們需要為兩個類添加set元素。我們依然使用,老師與學生的例子,一個老師可以有多個學生,一個學生可以有多個老師,映射文件如下:

          老師類映射文件中的set元素:

          <set name="stus" table="hib_tea_stu_links" lazy="false">

                 <key column="tid" />

                 <many-to-many class="Stu" column="sid" />

          </set>

          column="tid"”對應中間表“hib_tea_stu_links”的老師外鍵。“column="sid"”對應中間表“hib_tea_stu_links”學生的外鍵。

          學生類映射文件中的set元素:

          <set name="teas" table="hib_tea_stu_links" lazy="false" inverse="true">

                 <key column="sid" />

                 <many-to-many class="Tea" column="tid" />

          </set>

           

           

          檢索策略

          優點

          缺點

          優先考慮使用的場合

          立即檢索

          對應用程序完全透明,不管對象處于持久化狀態還是游離狀態,應用程序都可以從一個對象導航到關聯的對象

          (1)select語句多

          (2)可能會加載應用程序不需要訪問的對象,浪費許多內存空間。

          (1)類級別

          (2)應用程序需要立即訪問的對象

          (3)使用了二級緩存

          延遲檢索

          由應用程序決定需要加載哪些對象,可以避免執行多余的select語句,以及避免加載應用程序不需要訪問的對象。因此能提高檢索性能,并節省內存空間。

          應用程序如果希望訪問游離狀態的代理類實例,必須保證她在持久化狀態時已經被初始化。

          (1)一對多或者多對多關聯

          (2)應用程序不需要立即訪問或者根本不會訪問的對象

          迫切左外連接檢索

          (1)對應用程序完全透明,不管對象處于持久化狀態還是游離狀態,都可從一個對象導航到另一個對象。

          (2)使用了外連接,select語句少

          (1)可能會加載應用程序不需要訪問的對象,浪費內存。

          (2)復雜的數據庫表連接也會影響檢索性能。

          (1)多對一或一對一關聯

          (2)需要立即訪問的對象

          (3)數據庫有良好的表連接性能。

           

          二、Hibernate的檢索方式

          Hibernate的檢索方式:

          1.對象導航圖:根據映射文件檢索

          2.OID檢索:根據OID檢索

          3.HQL檢索:一種類似于SQL語句的面向對象的hibernate檢索文本語句

          4.QBC檢索:相當于將HQL分解為一個個對象的檢索方式

          5.本地SQL檢索:SQL語句

           

                   只有HQLQBC我們比較陌生,在此我們介紹一下:

          1.HQL語句

          session.createQuery("from Customer c where c.name = 'itcast' and c.age = 12");

          Customer:指向類名

          c.name:為映射文件中property元素的name

          c.ge:為映射文件中property元素的age

          看到沒有,HQL與數據庫毫無關系。HQL操作的持久化類的映射文件。

          HQL有三種查詢方式:

          1).使用參數名,如:

          //綁定參數(按照參數名稱綁定)

          q = s.createQuery("from Customer c where c.name = :a and c.age = :b");

          q.setString("a","itcast");

          q.setInteger("b",12);

          q.list();

          2).使用索引

          //綁定參數(按照參數索引位置綁定)

          q = s.createQuery("from Customer c where c.name = ? and c.age = ?");

          q.setString(0,"itcast");

          q.setInteger(1,12);

          q.list();

          3).命名查詢

          我們需要在映射文件中添加一個class的兄弟元素,這個元素可以添加在任意相關的映射文件中:

          <!-- 命名查詢 -->

          <query name="findCustomerByName">

              <![CDATA[from Customer c where c.name = ?]]>

          </query>

          我們在程序中直接調用:

          //命名查詢(將查詢條件定義到影射文件中,從影射文件中提取查詢條件)

          q = s.getNamedQuery("findCustomerByName");

          q.setString(0, "t,om");

          q.list();

           

          4).特別的左外連接查詢:

          //左外連接查詢,集合中的每個元素都是對象數組

          s.createQuery("from Customer c left outer join c.orders").list();

          //迫切左外連接查詢,集合中的每個元素都是客戶對象

          list = s.createQuery("from Customer c left outer join fetch c.orders").list();

          Set<Customer> customers = new HashSet<Customer>(list);

          左外連接查詢返回值:list中每個元素都是一個具有兩個成員的數組。數組[0]Customer對象,數組[1]Order對象。

          迫切左外連接返回值:list中每個元素都是一個Customer對象。

           

          2.QBC語句

          我們將上面的HQL語句,使用QBC來實現:

          Criteria cra = s.createCriteria(Customer.class);

          Criterion ctn_name = Restrictions.eq("name", "itcast");

          Criterion ctn_age = Restrictions.eq("age", 12);

          //調用list時執行查詢并返回值

          list = cra.add(ctn_name).add(ctn_age).list();

          QBC語句,一般用于動態查詢。比如,WEB應用中的高級搜索功能!

           

          上面介紹的都是相對簡單的查詢。至于復雜的查詢,我想玩過數據庫的人一想就明白了。

           

          比較方面

          HQL檢索

          QBC檢索

          可讀性

          優點:和sql相近,易讀

          將語句肢解成一組criteria,較差

          功能

          支持各種查詢

          不支持報表查詢和子查詢。有限的連接查詢

          查詢語句形式

          基于字符串形式的sql

          更加面向對象

          何時被解析

          運行時被解析

          編譯時被解析,更易排錯

          可擴展性

          不具擴展性

          用戶可擴展criteria接口

          對動態查詢語句的支持

          支持動態查詢,編程麻煩

          適合動態生成查詢語句

           

           

           

          三、Hibernate的映射繼承關系

          就是前邊檢索策略中提到的:

          joined-subclassunion-subclasssubclass這些元素。

           

          四、Hibernate批量操作

          使用HQLQBC等查詢語句進行批量操作

           

          五、Hibernate的緩存

                   二級緩存位于SessionFactory中,用于進程范圍:多個工作單元共享,可并發訪問,可存儲實例本身也可存散列數據,然后在重新組裝成對象放到一級緩存中。集群范圍:多個進程和主機間訪問,網絡通信是重點。需要將數據復制到所有集群中的節點。

           

          1. Hibernate緩存基礎

               緩存不是JPAEJB規范,用于性能優化,不同廠商方案不同。

               緩存維護數據狀態在本地,內存或者磁盤。

               緩存會減少數據庫訪問。

           

          2.緩存策略與范圍

               緩存類型

               .事務范圍:位于當前工作單元,不能并發訪問。

               .進程范圍:多個工作單元共享,可并發訪問,可存儲實例本身也可存散列數據,然后在               重新組裝。

               .集群范圍:多個進程和主機間訪問,網絡通信是重點。需要將數據復制到所有集群中的節點。

           

               緩存和OID

               事務級緩存也用于對象id的使用范圍,是理想的緩存。

               進程級緩存可選擇實現id的進程范圍存儲,也和主鍵對應。并發工作單元查詢同一id對象的話返     回相同的實例。在進程級緩存中的對象也可按值返回,每個工作單元再重新組裝形成副本。

           

                   一級緩存是強制的,他也維護了OID,二級緩存(進程級/集群級)對某些數據來說是有用的。

           

          3.緩存的架構

                   1、一級緩存即session

               2、二級緩存是可配的插件,可用于進程/集群范圍緩存。他們緩存都是狀態(按值返回),而不是真正的持久化對象。對于特定的數據項來說緩存的并發策略定義了事務的隔離細節。每個類或者每個集合的二級緩存是可選可配的。每個緩存都使用了自己的緩存區域。

               3Hibernate還實現了對查詢結果集的緩存,他和二級緩存緊密結合.而且需要額外的兩個物理緩存區域來容納緩存的查詢結果和最后更新表的時間戳。

           

          1.Hibernate二級緩存

           1、所有通過同一SessionFactory開啟的會話共享同一二級緩存。

           2、對象以拆解的形式存于二級緩存中(拆解是串行化過程,算法更多,更快比java串行)

           3、重點在于緩存方案(緩存策略與物理緩存提供商)

           4、不同數據需要不同的緩存方案。涉及如下設置:

                     a、是否開啟二級緩存

                     bHibernate并發策略

                     c、緩存過期策略(timerout LRU 內存敏感)

                     d、緩存的物理格式(內存 索引文件 集群替換)

           

          二級緩存安裝需要兩步:

          1、決定使用哪個緩存并發策略

          2、配置緩存過期和物理緩存屬性(cache provider)

           

          2).內置并發策略

               并發策略是調解人,負責在緩存中檢索數據。對于特定數據項,他也定義了事務隔離的語義。對類     或集合來說使用哪個并發策略需要做出判斷。

               四個內置的并發策略體現了遞減的事務隔離的等級。

               1Transationsal

                  只在受管環境中使用,如果需要可以確保所有的事務隔離到可重復讀,很少更新且防止臟數        據情況下該策略很重要

               2Read-write

                  該策略維護讀已提交隔離級別,使用了時間戳機制只在非集群環境下使用。

               3Nostrict-read-write

                  不保證在數據庫和緩存之間數據的一致性,如果使用的話,應該配置一個高效短期的過期超        時。否則,可能讀到臟數據。

               4Read-only

                  適用于從不發生改變的數據。只對數據進行引用。約束的降低帶來了性能提升。

           

               還可以實現自定義策略:

               orghibernatecacheCacheConcurrencyStrategy

           

          3). 選擇緩存供應商

               Hibernate要求必須為整個應用選擇一個供應商。以下是開源的實現:

               1EHCache

                  適用于單個虛擬機中單個進程范圍,可緩存至內存和磁盤,支持查詢緩存(最新版本支持集群

                  緩存,未測試)

               2OpenSymphony OSCache

                  在單個虛擬機中緩存至內存或磁盤。支持豐富的過期策略和查詢緩存。

               3SwarmCache

                  基于JGROUPS的集群緩存, 不支持Hibernate查詢緩存

               4JBoss Cache

                  同樣基于JGROUPS廣播庫的完全事務性自我復制集群緩存,支持自我復制、校驗、同/異步

                  通信、樂觀/ 悲觀鎖

           

          二級緩存講的有些快,內部實現原理沒講,因為時間來不急了。至于二級緩存的詳細配置與實現有的是以后有時間再自學吧!

           

          今天的日志主要使用了老徐的資料,在檢索策略那塊還好,因為講的比較細。之后的主要就是緩存技術,一級緩存學的挺好,就這個二級緩存留有些“懸念”。

           

                   老徐是一位優秀的老師,傳智播客確實很牛。能留住這么多人才!

           

                   明天就開始學習luncene了,期待它的到來!

           

                   加油!


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 元江| 陇南市| 平乐县| 将乐县| 六安市| 泽普县| 广元市| 河曲县| 青州市| 晋宁县| 吉木萨尔县| 扎囊县| 蒲城县| 罗源县| 武陟县| 许昌县| 泰和县| 五常市| 辽源市| 武汉市| 萝北县| 宝应县| 和龙市| 城市| 罗甸县| 龙门县| 东海县| 文安县| 赫章县| 三明市| 谢通门县| 太湖县| 河西区| 玉山县| 赫章县| 涿鹿县| 封丘县| 凌云县| 盐津县| 丹凤县| 堆龙德庆县|