随笔-193  评论-715  文章-1  trackbacks-0

其實這些內容應該算是進階部分的內容,常常可以用到。分享給大家。


Criteria Query

通常也稱為 QBC ,這種查詢最大的好處就是不用拼接 HQL SQL 語句。

基本用法如下:

Criteria crit=session.createCriteria(User.class);

查詢條件表達式使用: Expression

示例查詢使用: Example ,此類已經實現 Criterion 接口,它也可以用作 Criteria 的查詢條件。 其作用是:根據已有對象,查找屬性與之相符的其他對象。常用的場景是組合查詢。

我們可以通過 Criteria.createCriteria 方法在原有 Criteria 對象的基礎上構建復合查詢。

DetachedCriteria 可以脫離 Session 實例獨立存在,這樣,我們就可以將某些通用的 Criteria 查詢條件進行抽離,每次使用時再與當前 Session 實例綁定以獲得更好的代碼重用效果。它也可以用於子查詢表達( Subqueries )。

Criteria 的分組和統計采用 Projections 類來搞定。如下

Criteria.setProjection(Projections.groupProperty( age ));

 

HQL

HQL 是最常用的方式,在使用時最好能用參數綁定的方式,否則可能會帶來下面三個問題:

 A、    編碼更加零亂,可讀性降低。

 B、難以進行性能優化,根據JDBC以及數據庫操作原理可知,每次SQL執行時,數據庫都對SQL語法進行解析和優化,並將其處理結果保存在緩存中,如果之後有參數不同,語法相同的SQL命令,則直接以此緩存結果加以執行,從而避免SQL解析和優化的開銷。另外,從Hibernate角度而言,它使用了PreparedStatement作為底層數據庫訪問手段,對於相同的SQL,也可以重用已經創建的PreparedStatement對象。

 C、引入額外的安全風險,SQL Injection是常見的系統玫擊手段,這種方法的目標即針對由SQL字符串拼裝造成的漏洞。如在用戶名欄中輸入:a or x=x 這樣的字符串。

參數綁定有兩種方式,一種是順序綁定,一種是引用綁定。

 

引用查詢

HQL 語句寫在 Mapping XML 文件中。

Query query=session.getNameQuery( queryByName );

Hibernate3 中也支持存儲過程。

 

聯合查詢

聯合查詢是很有必要復習一下的知識,我想很多人可能都不能完全透徹的說清楚常用的四種聯合查詢,即: inner join , left join , right join , full join

Inner join 返回所有滿足關聯條件的記錄組合。

Left join 返回的結果集中,包含了左邊表符合條件的所有記錄及其對應的右邊表的記錄(如無,則以 NULL 替代)。

Right join left join 正好相反。

Full join left join right join 的並集。比較少用。

注意: fetch 只對 inner join left join 有效, 其意思是讓右邊的表立即取出。

另外,對兩張表進行笛卡爾積的查詢也比較少用,聽起來就有些恐怖。如 from User,Address

 

Hibernate 的世界里,有四種數據加載的方式:

即時加載( Immediate Loading ),延遲加載( Lazy Loading ),預先加載( Eager Loading ),批量加載( Batch Loading ,batch-size 應該設定為一個合理的小型數值,一般 <10 )。

 

自定義持久化實現

有時候可能還真的有用途,比如每個表都有的一些公共字段。在 Mapping XML 文件中指定,如下:

<sql-insert>

INSERT INTO USER(ID,NAME,AGE) VALUE(?,?,?)

</sql-insert>

<sql-update>

UPDATE USER SET NAME=? AGE=? WHERE ID=?

</sql-update>

<sql-delete>

DELETE FROM USER WHERE ID=?

</sql-delete>

 

 

Hibernate 中實體對象的三種狀態

Transient (自由狀態), Persistent (持久狀態), Detached (游離狀態)

Hibernate 中判定對象處於 Detached 狀態還是 Transient 狀態時,有著十分復雜的機制:

 1、首先,對象的ID屬生是否為null

 2、根據條件進行判定。

 3、如果指定了ID屬性的unsaved-value,即麼id屬性是否等於unsaved-value

4、 如果配備了 version 屬性, version 屬性是否為 null

 5、如果配備了version屬性,且為version指定了unsaved-valueversion屬性值是否等於unsaved-value

 6、如果存在Interceptor,即麼Interceptor.isUnsaved方法是否返回true

 

臟數據檢查

臟數據是指一個數據對象所攜帶的信息發生了改變之後的狀態。

臟數據檢查一般策略大致有下面兩種:

 1、數據對象監控,其實現方式大體上是通過攔截器對數據對象的setter進行攔截,攔截器的實現可以借助Dynamic Proxy或者CGlib實現。

 2、數據版本比對,在持久層框架中維護數據對象的最近讀取版本,當數據提交時將提交數據與此版本進行比對,如果發生變化則將其同步到數據相應的庫表。

Hibernate 采用第二種檢查策略。

 

Unsaved-value

數據保存時, Hibernate 將根據這個值來判斷對象是否需要保存,用於非顯示保存的場景。所謂顯示保存,是指在代碼中明確調用 session save, update, saveOrUpdate 方法對對象進行持久化。

 

數據緩存

數據緩存策略包括三個層次:事務級緩存(基於 Session 的生命周期實現),應用級 / 進程級緩存(在 SessionFactory 層實現,在 Cluster 環境中一定要特別注意這類 Cache ),分布式緩存 ( 其效果尚需結合實際情況考証 )

 

Hibernate 數據緩存分為兩個層次:內部緩存 Session Level ,也稱一級緩存),二級緩存( SessionFactory Level ,也稱二級緩存)。 將在以下情況中發揮作用:通過 id[ 主鍵 ] 加載數據時和延遲加載時。

內部緩存正常情況下由 Hibernate 自動維護,如果需要手動干預,我們可以通過以下方法完成: Session.evict() 將某個特定對象從內部緩存中清除, Session.clear() 清空內部緩存。

二級緩存涵蓋了應用緩存和分布式緩存領域,在引入時必須考慮以下問題:數據庫是否與其他應用共享,應用是否需要部署在集群環境中。

如果數據滿足以下條件,則可以將其納入緩存管理:

 1、數據不會被第三方應用修改。

 2、數據大小Data size)在可接受的范圍之內。

 3、數據更新頻率較低。

4、 同一數據可能會系統頻繁引用。

5、 非關鍵數據。

Hibernate 本身並未提供二級緩存的產品化實現(只提供了一個基於 Hashtable 的簡單緩存以供調試)。第三方緩存實現有: JCS EHCache, OSCache, JBossCache, SWarmCache ,僅 JBossCache SwarmCache 支持 Cluster SwarmCache 提供的是 invalidation 方式的分布式緩存,即當集群中的某個節點更新了緩存中的數據,即通知集群中的其他節點將此數據廢除,之後各節點需要用到這個數據的時候,會重新從數據庫中讀入並填充到緩存中。 JBossCache 提供的是 Repplication 式的緩存,即如果集群中某個節點的數據發生改變,此節點會將發生改變的數據的最新版本復制到集群中的每個節點中以保持所有節點狀態一致。啟用二級緩存,需要在 hibernate.cfg.xml 中配置以下參數:

<property name= hibernate.cache.provider_class >

        net.ehcache.hibernate.Provider

</property>

 

Hibernate 提供以下 4 種內置的緩存同步策略:

Read-only :只讀,對於不會發生改變的數據,可使用只讀型緩存。

Nonstrict-read-write :如果程序對並發訪問下的數據同步要求不同非常嚴格,且數據更新操作頻率較低,可采用本選項,獲得較好性能。

Read-write :嚴格可讀寫緩存,基于時間戳判定機制,實現了“ read committed 事務隔離等級。可用於對數據同步要求嚴格的情況,但不支持分布式緩存。

Transactional :事務型緩存,必須運行在 JTA 事務環境中。

 

Hibernate 的默認事務處理機制基於 JDBC Transaction 我們也可以通過配置文件設定采用 JTA 作為事務管理實現:

<property name= hibernate.transaction.factory_class >

        net.hibernate.transaction.JTATransactionFactory

</property>

JTA 提供了跨 Session 的事務管理能力( JDBC 事務不支持)。

 

Hibernate 支持兩種鎖機制,悲觀鎖( perssimistic locking )和樂觀鎖( optimistic locking )。 悲觀鎖主要是利用數據庫底層的機制來實現的,加鎖模式有:

LockMode.None :無鎖機制

LockMode.WRITE Insert Update 記錄時會自動獲取

LockMode.READ :讀取記錄時會自動獲取

LockMode.UPGRADE :利用數據庫的 for update 子句加鎖

LockMode.UPGRADE_NOWAIT Oracle 的特定實現,利用 Oracle for update nowait 子句實現加鎖。

加鎖一般通過以下方式實現:

Criteria.setLockMode

Query.setLockMode

Session.lock

 

樂觀鎖,大多是基於數據版本( version )記錄機制實現。即為數據增加一個版本標識,在基於數據庫表的版本解決方案中,一般是通過為數據庫表增加一“ version 字段來實現。讀取出數據時,將此版本號一同讀出,之後更新時,對此版本號加 1 。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號大於數據庫當前版本號,則予以更新,否則認為是過期數據。(註,感覺跟 CVS 版本的機制差不多)

Hibernate 中可以通過 Class 描述符的 optimistic-lock 屬性結合 version 描述符指定。

<class name=”User” table=”t_user” optimistic-lock=”version”>

<id>…</id>

<version column=”version” name=”version” type=”string” />

<!-- version 必須放在 id 之後 -->

</class>

Optimistic-lock 有四個選項: none ,無樂觀鎖, version ,通過版本機制控制實現樂觀鎖(官方推荐的方式,也是目前惟一在實體脫離 session 發生修改的情況下依然有效的鎖機制), dirty ,通過檢查發生變動過的屬性實現樂觀鎖, all ,通過檢查所有屬性實現樂觀鎖。

 

持久層操作

數據加載

get()/load() 方法的主要區別在於: 1 、未發現記錄時, get 返回 null load 拋出 ObjectNotFoundException 2 load 可返回實體的代理實例, get 方法永遠直接返回實體類。 3 load 充分利用 Level 2 Cache ,而 get 僅在內部緩存中進行數據查找。

Find () 方法只查找一次, iterate () 方法是 N+1 次查詢(先查出符合條件的 ID ,然后再根據 ID 每一筆記錄依次查出),但會充分利用 Cache ,要學會分情況利用。

對於海量數據進行操作,可能會導致 OutOfMemoryError 的解決方案之一是,采用 iterate() 方法逐條對記錄進行處理,如:

Iterator it=session.iterator(…);

While(it.hasNext()){

        Object obj=it.next();

        Session.evict(obj);

        SessionFactory.evict(obj.getClass(),obj.getId());

}

一般,對於這種特大數據量的處理,還是推荐使用 SQL 語句或存儲過程來解決。

QueryCache 只在兩種情況下產生作用: 1 、完全相同的 select SQL 重復執行, 2 、在兩次查詢之間,此 select SQL 對應的庫表沒有發生過變化。使用方式:

        配置中: hibernate.cache.use_query_cache=true

        查詢時: query.setCacheable(true);

Hibernate.initialize() 方法可以強制 Hibernate 立即加載關聯的對象集。

Hibernate2 中支持索引和實體延遲加載, Hibernate3 中還支持屬性延遲加載,但需要對 POJO 實體作一些增加處理。

數據批量導入: hibernate.jdbc.batch_size=25 ,比較適合於遠程數據庫的數據插入、更新、刪除操作。

Hibernate 中基於游標的用法:

Transaction tx=session.beginTransaction();

String hql=”from User”;

Query query=session.createQuery(hql);

ScrollableResults scRes=query.scroll();

while(scRes.next()){

        User user=(User)scRes.get(0);

}

tx.commit();

 

Hibernate3 中雖然支持 Bulk delete/update ,實際上仍然沒有解決 Cache 同步上的問題,無法保証緩存數據的一致有效性(包括內部緩存和二級緩存), Hibernate 與其它方式(如 JDBC )在一個系統中混用也會帶來同樣的問題。

 

Hibernate 中涉及的 Collection 類型共有以下幾種:

無序集: Set , Bag , Map

有序集: List

Bag 的底層是錯助一個 List 實現,但卻屏蔽了 List 的有序特性。更新時, bag 的實現方式實際上是先將數據庫表中的所有的集合數據全部刪除,再將現有數據逐條插入。利用 idbag (作為 Bag 的一種延伸),則成功地解決了這個問題。

<idbag name=”address” lazy=”true” table=”t_address”>

<collection-id type=”int” column=”id”>

        <generator class=”identity” />

</collection-id>

<key column=”user_id” />

</idbag>

Set 配置相比, Map 增加了一個 index 配置,用於指定用作 Key 的字段名:此字段要求在數據集中取值惟一。

 

數據排序有兩種方式: sort order-by

Sort=”natural” 指定采用 Java 默認的排序機制,它會調用相應數據類型的 compareTo 方法進行排序中的值比對。 也可以指定為自定義的實現。

posted on 2007-02-09 17:26 Robin's Programming World 阅读(5281) 评论(0)  编辑  收藏 所属分类: Java

只有注册用户登录后才能发表评论。


网站导航: