單一數據加載:Session.get/load
均可以根據指定的實體類和id從數據庫中讀取記錄,并返回與之對應的實體對象。
區別:
1.如果未能發現符合條件的記錄,get方法返回null,而load方法會拋出一個ObjectNotFoundException。
2.Load方法可返回實體的代理類實例,而get方法永遠直接返回實體類。
3.load方法可以充分利用內部緩存和二級緩存中的現有數據,而get方法僅在內部緩存中查找,如果沒有發現對應的數據,將越過二級緩存,直接調用SQL完成數據讀取。
數據加載的過程:
1.在一級緩存中,根據實體類型和id進行查找,如果在第一級緩存中命中,且數據狀態合法,則直接返回。
2.Session會在當前"NonExists"記錄中進行查找,如果在"NonExists"記錄中存在同樣的條件,返回null。
3.對load方法而言,如果內部緩存中沒法現有效數據,查詢二級緩存,命中則返回。
4.如果緩存中無有效數據,發起數據庫查詢操作(Select SQL),如果經過查詢未發現對應記錄,將此次查詢的信息在"NonExists"中加以記錄,返回null.
5.根據映射配置和Select SQL查詢得到的ResultSet,創建對應的數據對象。
6.將對象納入一級緩存。
7.執行Interceptor.onLoad方法(如果有對應的Interceptor)
8.將數據納入二級緩存
9.如果數據對象實現了LifeCycle接口,則調用數據對象的onLoad方法。
10.返回數據對象。
批量查詢:Session.find/iterate
查詢性能往往是系統性能表現的一個重要方面,查詢機制的優劣很大程度上決定了系統的整體性能。這個領域往往也存在最大的性能調整空間。
hibernate2中Session.find()對應于3中的session.createQuery().list();
hibernate2中Session.iterate()對應于3中的session.createQuery().iterate();
find和iterate區別:
find方法通過一條Select SQL實現了查詢操作,而iterate方法要執行多條Select SQL.
iterate第一次查詢獲取所有符合條件的記錄的id,然后再根據各個id從庫表中讀取對應的記錄,這是一個典型的N+1次的查詢問題,如果符合條件記錄有10000條,就需要執行10001條Select SQL,可想性能會如何的差。
那為什么要提供iterator方法,而不只是提供高效率的find方法?
原因1.與hibernate緩存機制密切相關
find方法實際上是無法利用緩存的,它對緩存只寫不讀。
find方法只執行一次SQL查詢,它無法判斷緩存中什么樣的數據是符合條件的,也無法保證查詢結果的完整性。而iterate方法,會首先查詢所有符合條件記錄的id,然后根據id去緩存中找,如果緩存中有該id,就返回,沒有可以根據id再去數據庫查詢。
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);
順序執行,iterate方法只會執行一次SQL查詢,就是查找id,然后根據id就可以從緩存中獲得數據。
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);
緩存是不起作用的。
如果目標數據讀取相對較為頻繁,通過iterate這種機制,會減少性能損耗。
原因2.內存使用上的考慮
find方法將一次獲得的所有記錄并將其讀入內存。如果數據量太大,可能會觸發OutOfMemoryError,從而導致系統異常。解決方案之一就是結合iterate方法和evict方法逐條對記錄進行處理,將內存消化保持在一個可以接受的范圍之內。如:
String hql = "from TUser where age > ?";
Iterator it = session.iterate(hql, new Integer(18), Hibernate.INTEGER);
while(it.hasNext()) {
??? TUser user = (TUser)it.next();
???
??? //將對象從一級緩存中刪除
??? session.evict(user);
??? //二級緩存可以設定最大緩存量,達到后自動對較老數據進行廢除,但也可以通過編
??? //碼移除,這樣有助于保持數據有效性。
??? sessionFactory.evict(TUser.class, user.getID());
}
批量數據處理的緩存同步問題
1.hibernate 2:
session.delete("from TUser");
會先查詢出id,然后逐個id執行 delete from T_User where id = ?;
這樣造成效率低下。
為什么不直接采用一條Delete SQL?是因為ORM要自動維持其內部狀態屬性,必須知道用戶作了什么操作。必須先從數據庫中獲得待刪除對象,然后根據這些對象對內部緩存和二級緩存的數據進行整理,以保持內存狀態與數據庫的一致性。
單執行一條刪除語句,刪除了什么數據,只有數據庫知道,ORM無法得知。下次用戶從緩存中讀出的數據,很可能就是被刪除的數據,從而導致邏輯錯誤。當然,如果ORM可以根據DELETE SQL對緩存中數據進行處理,將緩存中符合條件的對象廢除,然后再執行DELETE SQL
,但是這樣導致緩存的管理復雜性大大增加(實際相當于實現了一個支持SQL的內存數據庫),這對于輕量級的ORM實現而言太苛刻了。
2.hibernate 3
性能提高。
但無法解決緩存同步上的問題,無法保證緩存數據的一致有效性。
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-07-16 10:24
knowhow 閱讀(421)
評論(0) 編輯 收藏 所屬分類:
ORM:Hibernate及其他