有不少人認爲Hibernate天生效率比較低,確實,在廣泛狀況下,須要將執行轉換爲SQL語句的 Hibernate的效率低於直接JDBC存取,然而,在通過比較好的性能優化以後,Hibernate的性能仍是讓人至關滿意的,特別是應用二級緩存之 後,甚至能夠得到比較不使用緩存的JDBC更好的性能,下面介紹一些一般的Hibernate的優化策略: 1.抓取 優化 抓取是指Hibernate如何在關聯關係之間進行導航的時候,Hibernate如何獲取關聯對象的策略,其主要定義了兩個方面:如何抓取和什麼時候抓取 1)如何抓取。 Hibernate3主要有兩種種抓取方式,分別應用於對象關聯實例(many-to-one、one-to-one)和對象關聯集合(set、map等),總共是四種變種 JOIN抓取: 經過在SELECT語句中使用OUTER JOIN來得到對象的關聯實例或者關聯集合) SELECT抓取: 另外發送一條SELECT語句來抓取當前對象的關聯實體和集合 在個人開發經歷中,此處對性能的優化是比較有限的,並不值得過多關注 例: A.應用於對象關聯實例(默認是false) <many-to-one name=".." outer-join="true/false/auto" .../> B.應用於對象關聯集合(默認是auto) <set name=".." fetch="join/select" ... > .... </set> 2)什麼時候抓取 主要分爲延遲加載和當即抓取,默認的狀況下Hibernate3對對象關聯實採用延遲加載,普通屬性採用當即抓取,經過延遲加載和採用適當的抓取粒度,與不採用優化相比每每能夠將性能提高數倍 當即抓取:當抓取宿主對象時,同時抓取其關聯對象和關聯集以及屬性 延遲加載:當抓取宿主對象時,並不抓取其關聯對象,而是當對其對象進行調用時才加載 例: A.應用於對象關聯實例(默認是延遲加載) <many-to-one name=".." lazy="true/false" .../> B.應用於對象關聯集合(默認是延遲加載) <set name=".." lazy="true/false" ... > .... </set> 對於延遲加載,須要注意的時,對延遲對象的使用必須在Session關閉以前進行,Hibernate的 LazyInitalizationException每每就是因爲在Session的生命期外使用了延遲加載的對象。當咱們進行Web開發時,可使用 OpenSessionInView模式,當請求開始時打開session,當請求響應結束時才關閉session,不過,在使用 OpenSessionInView模式時,須要注意若是響應時間比較長(業務比較複雜或者客戶端是低速網絡),將Session資源(也就是數據庫的連 接)佔用過久的話能夠會致使資源耗盡 3)抓取粒度 抓取粒度指的是對象在關聯關係之間被導航時一次預先加載的數量,Hibernate程序的性能比較差每每就在於沒有對抓取粒度仔細考慮,當加載一個列表並在列表中的每一個對象中對其關聯進行導航時,每每致使N+1條SQL語句查詢。 例: A.應用於對象關聯實例(默認爲1),注意,對對象關聯實例的設 置是在被關聯的對象之上的,譬如 class User { Group g; } 那麼抓取粒度應該在Group的配置文件之上,見下 <class name="Group" table="group" batch-size=".."> ... </class> 對該值並無一個約定俗成的值,根據狀況而定,若是被關聯表數據比較少,則能夠設置地小一些,3-20,若是比較大則能夠設到30-50,注意的時候,並非越多越好,當其值超過50以後,對性能並無多大改善但卻無謂地消耗內存 假設有以下例子: List<User> users = query.list(); 若是有20個User,並對這20個User及其Group進行遍歷,若是不設置batch-size(即batch-size="1"),則在最糟糕的狀況 下,須要1 + 20條SQL語句,若是設置batch-size="10",則最好的狀況下只須要1 + 2條SQL語句 B.應用於對象關聯集合(默認爲1) <set name=".." batch-size="" ... > .... </set> 2.二級緩存 Hibernate 對數據的緩存包括兩個級:一級緩存,在Session的級別上進行,主要是對象緩存,以其id爲鍵保存對象,在Session的生命期間存在;二級緩存, 在SessionFactory的級別上進行,有對象緩存和查詢緩存,查詢緩存以查詢條件爲鍵保存查詢結果,在SessionFactory的生命期間存 在。默認地,Hibernate只啓用一級緩存,經過正確地使用二級緩存,每每能夠得到意想不到的性能。 1)對象緩存: 當抓取一個對象以後,Hiberate將其以id爲鍵緩存起來,當下次碰到抓取id相同的對象時,可使用以下配置 方法1:在緩存對象上配置 <class ...> <cache useage="read-only/write/...." regions="group" /> </class> useage 表示使用什麼類型的緩存,譬如只讀緩存、讀寫緩存等等(具體參見Hibernate參考指南),值得注意的時,有部分緩存在Hibernate的實現中不 支持讀寫緩存,譬如JBossCache在Hibernate的實現中只是一種只讀緩存,具體緩存實現對緩存類型的支持狀況,能夠參見 org.hibernate.cache包 regions表示緩存分塊,大部分的緩存實現每每對緩存進行分塊,該部分是可選的,詳細參見各緩存實現 方法2:在hibernate.cfg.xml中配置 <cache class=".." useage=".." regions=".."/> 我認爲第二種更好,能夠統一管理 2)查詢緩存 查詢時候將查詢結果以查詢條件爲鍵保存起來,須要配置以下 A.在hibernate.cfg.xml中配置(啓用查詢緩存) <property name="hibernate.cache.use_query_cache">true</property> (前面的屬性名可參見常量 org.hibernate.cfg.Enviroment.USE_QUERY_CACHE) B.程序 query.setCacheable(true); query.setCacheRegions(...); 須要注意的是,查詢緩存與對象緩存要結合更有效,由於查詢緩存僅緩存查詢結果列表的主鍵數據 通常狀況下在開發中,對一些比較穩定而又被頻繁引用的數據,譬如數據字典之類的,將其進行二級緩存,對一些查詢條件和查詢數據變化不頻繁而又經常被使用的 查詢,將其進行二級緩存。因爲二級緩存是放在內存中,並且Hibernate的緩存不是弱引用緩存(WeekReference),因此注意不要將大塊的 數據放入其中,不然可能會被內存形成比較大的壓力。 3.批量數據操做 當進行大批量數據操做(幾萬甚至幾十幾百萬)時,須要注意兩點,一,批量提交,二,及時清除不須要的一級緩存數據 1)所謂的批量提交,就是不要頻繁使用session的flush,每一次進行flush,Hibernate將PO數據於數據庫進行同步,對於海量級數 據操做來講是性能災難(同時提交幾千條數據和提交一條數據flush一次性能差異可能會是幾十倍的差別)。通常將數據操做放在事務中,當事務提交時 Hibernate自動幫你進行flush操做。 2)及時清除不須要的一級緩存數據:因爲Hibernate默認採用一級緩存,而在 session的生命期間,全部數據抓取以後會放入一級緩存中,而當數據規模比較龐大時,抓取到內存中的數據會讓內存壓力很是大,通常分批操做數據,被一 次操做以後將一級緩存清除,譬如 session.clear(User.class) 4.雜項 dynamic-insert,dynamic-update,動態插入和動態更新,指的是讓Hibernate插入數據時僅插入非空數據,當修改數據時只修改變化的數據,譬如對於 class User { id username password } 若是u.id=1, u.username="ayufox",u.password=null,那麼若是不設置動態插入,則其sql語句是 insert into users(id, username, password) values (1, 'ayufox', '),若是設置則其 sql語句是insert into users(username) valeus('ayufox') 在如上的狀況下,若是修改 u.password='11',那麼若是不設置動態更新,則sql語句爲update users set username='ayufox', password='11' where id = 1,若是設置則爲update user set password='11' where d = 1 設置是在class的映射文件中,以下 <class name="User" table="users" dynamic-insert="true/false" dynamic-update="true/false" ...> </class> 該設置對性能的提高比較有限