Topquan's Blog

          分享價(jià)值----成就你我----我的博客----你的家

          hibernate數(shù)據(jù)加載

          單一數(shù)據(jù)加載:Session.get/load
          均可以根據(jù)指定的實(shí)體類和id從數(shù)據(jù)庫中讀取記錄,并返回與之對(duì)應(yīng)的實(shí)體對(duì)象。
          區(qū)別:
          1.如果未能發(fā)現(xiàn)符合條件的記錄,get方法返回null,而load方法會(huì)拋出一個(gè)ObjectNotFoundException。
          2.Load方法可返回實(shí)體的代理類實(shí)例,而get方法永遠(yuǎn)直接返回實(shí)體類。
          3.load方法可以充分利用內(nèi)部緩存和二級(jí)緩存中的現(xiàn)有數(shù)據(jù),而get方法僅在內(nèi)部緩存中查找,如果沒有發(fā)現(xiàn)對(duì)應(yīng)的數(shù)據(jù),將越過二級(jí)緩存,直接調(diào)用SQL完成數(shù)據(jù)讀取。
          數(shù)據(jù)加載的過程:
          1.在一級(jí)緩存中,根據(jù)實(shí)體類型和id進(jìn)行查找,如果在第一級(jí)緩存中命中,且數(shù)據(jù)狀態(tài)合法,則直接返回。
          2.Session會(huì)在當(dāng)前"NonExists"記錄中進(jìn)行查找,如果在"NonExists"記錄中存在同樣的條件,返回null。
          3.對(duì)load方法而言,如果內(nèi)部緩存中沒法現(xiàn)有效數(shù)據(jù),查詢二級(jí)緩存,命中則返回。
          4.如果緩存中無有效數(shù)據(jù),發(fā)起數(shù)據(jù)庫查詢操作(Select SQL),如果經(jīng)過查詢未發(fā)現(xiàn)對(duì)應(yīng)記錄,將此次查詢的信息在"NonExists"中加以記錄,返回null.
          5.根據(jù)映射配置和Select SQL查詢得到的ResultSet,創(chuàng)建對(duì)應(yīng)的數(shù)據(jù)對(duì)象。
          6.將對(duì)象納入一級(jí)緩存。
          7.執(zhí)行Interceptor.onLoad方法(如果有對(duì)應(yīng)的Interceptor)
          8.將數(shù)據(jù)納入二級(jí)緩存
          9.如果數(shù)據(jù)對(duì)象實(shí)現(xiàn)了LifeCycle接口,則調(diào)用數(shù)據(jù)對(duì)象的onLoad方法。
          10.返回?cái)?shù)據(jù)對(duì)象。


          批量查詢:Session.find/iterate
          查詢性能往往是系統(tǒng)性能表現(xiàn)的一個(gè)重要方面,查詢機(jī)制的優(yōu)劣很大程度上決定了系統(tǒng)的整體性能。這個(gè)領(lǐng)域往往也存在最大的性能調(diào)整空間。

          hibernate2中Session.find()對(duì)應(yīng)于3中的session.createQuery().list();
          hibernate2中Session.iterate()對(duì)應(yīng)于3中的session.createQuery().iterate();
          find和iterate區(qū)別:
          find方法通過一條Select SQL實(shí)現(xiàn)了查詢操作,而iterate方法要執(zhí)行多條Select SQL.
          iterate第一次查詢獲取所有符合條件的記錄的id,然后再根據(jù)各個(gè)id從庫表中讀取對(duì)應(yīng)的記錄,這是一個(gè)典型的N+1次的查詢問題,如果符合條件記錄有10000條,就需要執(zhí)行10001條Select SQL,可想性能會(huì)如何的差。

          那為什么要提供iterator方法,而不只是提供高效率的find方法?

          原因1.與hibernate緩存機(jī)制密切相關(guān)
          find方法實(shí)際上是無法利用緩存的,它對(duì)緩存只寫不讀。
          find方法只執(zhí)行一次SQL查詢,它無法判斷緩存中什么樣的數(shù)據(jù)是符合條件的,也無法保證查詢結(jié)果的完整性。而iterate方法,會(huì)首先查詢所有符合條件記錄的id,然后根據(jù)id去緩存中找,如果緩存中有該id,就返回,沒有可以根據(jù)id再去數(shù)據(jù)庫查詢。
          String hql = "from TUser where age > ?";
          List userList = session.find(hql, new Integer(18), Hibernate.INTEGER);
          Iterator it = session.iterate(hql, new Integer(18), Hibernate.INTEGER);
          順序執(zhí)行,iterate方法只會(huì)執(zhí)行一次SQL查詢,就是查找id,然后根據(jù)id就可以從緩存中獲得數(shù)據(jù)。

          String hql = "from TUser where age > ?";
          List userList = session.find(hql, new Integer(18), Hibernate.INTEGER);
          userList = session.find(hql, new Integer(18), Hibernate.INTEGER);
          緩存是不起作用的。
          如果目標(biāo)數(shù)據(jù)讀取相對(duì)較為頻繁,通過iterate這種機(jī)制,會(huì)減少性能損耗。

          原因2.內(nèi)存使用上的考慮
          find方法將一次獲得的所有記錄并將其讀入內(nèi)存。如果數(shù)據(jù)量太大,可能會(huì)觸發(fā)OutOfMemoryError,從而導(dǎo)致系統(tǒng)異常。解決方案之一就是結(jié)合iterate方法和evict方法逐條對(duì)記錄進(jìn)行處理,將內(nèi)存消化保持在一個(gè)可以接受的范圍之內(nèi)。如:
          String hql = "from TUser where age > ?";
          Iterator it = session.iterate(hql, new Integer(18), Hibernate.INTEGER);
          while(it.hasNext()) {
              TUser user = (TUser)it.next();
             
              //將對(duì)象從一級(jí)緩存中刪除
              session.evict(user);

              //二級(jí)緩存可以設(shè)定最大緩存量,達(dá)到后自動(dòng)對(duì)較老數(shù)據(jù)進(jìn)行廢除,但也可以通過編
              //碼移除,這樣有助于保持?jǐn)?shù)據(jù)有效性。
              sessionFactory.evict(TUser.class, user.getID());
          }


          批量數(shù)據(jù)處理的緩存同步問題
          1.hibernate 2:
          session.delete("from TUser");
          會(huì)先查詢出id,然后逐個(gè)id執(zhí)行 delete from T_User where id = ?;
          這樣造成效率低下。
          為什么不直接采用一條Delete SQL?是因?yàn)镺RM要自動(dòng)維持其內(nèi)部狀態(tài)屬性,必須知道用戶作了什么操作。必須先從數(shù)據(jù)庫中獲得待刪除對(duì)象,然后根據(jù)這些對(duì)象對(duì)內(nèi)部緩存和二級(jí)緩存的數(shù)據(jù)進(jìn)行整理,以保持內(nèi)存狀態(tài)與數(shù)據(jù)庫的一致性。
          單執(zhí)行一條刪除語句,刪除了什么數(shù)據(jù),只有數(shù)據(jù)庫知道,ORM無法得知。下次用戶從緩存中讀出的數(shù)據(jù),很可能就是被刪除的數(shù)據(jù),從而導(dǎo)致邏輯錯(cuò)誤。當(dāng)然,如果ORM可以根據(jù)DELETE SQL對(duì)緩存中數(shù)據(jù)進(jìn)行處理,將緩存中符合條件的對(duì)象廢除,然后再執(zhí)行DELETE SQL
          ,但是這樣導(dǎo)致緩存的管理復(fù)雜性大大增加(實(shí)際相當(dāng)于實(shí)現(xiàn)了一個(gè)支持SQL的內(nèi)存數(shù)據(jù)庫),這對(duì)于輕量級(jí)的ORM實(shí)現(xiàn)而言太苛刻了。
          2.hibernate 3
          性能提高。
          但無法解決緩存同步上的問題,無法保證緩存數(shù)據(jù)的一致有效性。
          Tuser user = (TUser)session.load(TUser.class, new Integer(1));

          //通過Bulk delete/update 刪除id=1的用戶記錄
          Transaction tx = session.beginTransaction();
          String hql = "delete TUser where id=1";
          Query query = session.createQuery(hql);
          query.executeUpdate();
          tx.commit();

          //再次嘗試加載
          user = (TUser)session.load(TUser.class, new Integer(1));
          可以看到第二次加載是成功的。

          posted on 2006-08-05 01:19 topquan 閱讀(1827) 評(píng)論(0)  編輯  收藏 所屬分類: Hibernate

          主站蜘蛛池模板: 庐江县| 监利县| 六盘水市| 耿马| 华池县| 涪陵区| 娄底市| 张家川| 木兰县| 华坪县| 无极县| 冕宁县| 灵武市| 扶风县| 塔城市| 黔东| 隆林| 谢通门县| 清河县| 秦皇岛市| 荥经县| 长岛县| 贵溪市| 朝阳区| 民丰县| 夏邑县| 苏州市| 哈密市| 余江县| 任丘市| 乐山市| 玉环县| 宜兰县| 桐柏县| 定结县| 土默特左旗| 隆尧县| 海伦市| 灌云县| 宁化县| 京山县|