Hibernate性能調(diào)優(yōu)
1 Hibernate 連接池
hibernate自帶一個(gè)連接池,但是這個(gè)連接池的性能不好,BUG也比較多,所以hibernate借助第三方的連接池來(lái)配置。通常用的比較多的是prxool。目前在j2ee中最通用的框架ssh中,hibernate的配置如下:
在spring 的applicationContext.xm.配置文件里配置數(shù)據(jù)源連接信息:
<!-- 配置數(shù)據(jù)源 -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>org.logicalcobwebs.proxool.ProxoolDriver</value>
</property>
<property name="url">
<value>proxool.spring_user</value>
</property>
</bean>
在web.xml中做如下配置
<!-- 制定proxool.xml放置的位置 -->
<param-value>WEB-INF/proxool.xml</param-value>
</context-param>
<listener>
將proxool.xml連接池配置文件放入到工程里WEB-INF目錄下,其中proxool.xml的內(nèi)容如下:
<?xml version="1.0" encoding="utf-8"?>
<!-- the proxool configuration can be embedded within your own application's.
Anything outside the "proxool" tag is ignored. -->
<something-else-entirely>
<proxool>
<alias>spring_user</alias>
<driver-url>jdbc:mysql://192.168.1.150:3306/chiyuweb</driver-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<driver-properties>
<property name="user" value="root" />
<property name="password" value="123456" />
<property name="useUnicode" value="true" />
<property name="characterEncoding" value="GBK" />
</driver-properties>
<minimum-connection-count>5</minimum-connection-count> <maximum-connection-count>50</maximum-connection-count> <maximum-active-time>60000</maximum-active-time>
<house-keeping-test-sql>select 1</house-keeping-test-sql>
<prototype-count>5</prototype-count>
<house-keeping-sleep-time>60000</house-keeping-sleep-time>
</proxool>
</something-else-entirely>
注:在prxool這個(gè)文件里<alias>spring_user</alias> 表示連接的別名,一個(gè)連接池可以配置多個(gè)連接。
2 Hibernate 多條件分頁(yè)查詢
Hibernate的多條件查詢的方法有很多種,比較常用的有兩種,一種的創(chuàng)建Query對(duì)象,一種是創(chuàng)建Criteria對(duì)象。
Query對(duì)象查詢:Hibernate上下文對(duì)象query= session.createQuery(HQL); query.setParameters(Object[] params,Type[] types)返回一個(gè)根據(jù)參數(shù)查詢的List集合。分頁(yè)方法query.setFirstResult(START),START參數(shù)表示從查詢到的結(jié)果集里第幾條記錄開始取query.setMaxResults(ROWS),ROWS表示分頁(yè)信息中每頁(yè)查詢的記錄條數(shù)。根據(jù)如上方法即可以實(shí)現(xiàn)hibernate多條件分頁(yè)查詢。其中query.setParameters方法的兩個(gè)參數(shù),params,types。他們是一一對(duì)應(yīng),成對(duì)出現(xiàn)的,所以最后用順序表來(lái)保存他們的值,即兩個(gè)用ArrayList來(lái)成對(duì)存儲(chǔ)兩個(gè)參數(shù),然后將他們轉(zhuǎn)化成數(shù)組。Type類型是org.hibernate中的Type類。如果參數(shù)是String類型,則參數(shù)需要設(shè)置成Hibernate.STRING,如他類型依次類推。時(shí)間類型需要注意如果比較精確到天,類型可設(shè)置成Hibernate.DATE,如果時(shí)間要精確到秒,則時(shí)間類型需要設(shè)置成Hibernate.TIMESTAMPS.
Criteria對(duì)象查詢,調(diào)用session.createCriteria(Type type).add() type是反射包里面的Type ,(若查User類 ,則傳入U(xiǎn)ser.class),add()方法能夠傳入字段的名稱 addOrder方法能夠傳入排序規(guī)則,如此實(shí)現(xiàn)多條件查詢。分頁(yè)方法同query。
3 Hibernate關(guān)聯(lián)關(guān)系
Hibernate通常用的關(guān)聯(lián)關(guān)系有三種:一對(duì)一,一對(duì)多和多對(duì)多。
一對(duì)一的方式有兩種:一種是聯(lián)合主鍵;一種是通過其他列來(lái)關(guān)聯(lián),即多對(duì)一的特殊形式,將多對(duì)一unique屬性設(shè)置為true。配置方法網(wǎng)上都有這里就不啰嗦。
多對(duì)多關(guān)系并不提倡用many-to-many方式來(lái)配,通常用兩個(gè)一對(duì)多的關(guān)系來(lái)實(shí)現(xiàn)。
一對(duì)多關(guān)系,這個(gè)關(guān)系的配的時(shí)候要特別注意,在多的一方<many-to-one/> 這邊的lazy屬性一般不要設(shè)置為false。這樣配會(huì)大大的降低程序的性能。還有就是關(guān)聯(lián)的列最好不要設(shè)置成外鍵關(guān)系,以為這樣做加了外鍵會(huì)使數(shù)據(jù)庫(kù)不易維護(hù)。關(guān)聯(lián)列單獨(dú)設(shè)置,在實(shí)體配置文件中這個(gè)列的約束這樣設(shè)置 insert=”false” update=”false” 即可。
對(duì)于一對(duì)多自連接關(guān)系舉一個(gè)實(shí)例來(lái)說(shuō)明:
比如一個(gè)Person類里有老師和學(xué)生的關(guān)系
則Person.hbm.xml配置如下
<property name=”teacher_id” type=”java.lang.Integer” insert=”false” update=”false” />
<set name="students" cascade="save-update" inverse="true" lazy="true">
<key column="teacher_id" />
<one-to-many class="Person" />
</set>
<many-to-one name="student" column="teacher_id"
class="Person" cascade="save-update" />
2.二級(jí)緩存
Hibernate對(duì)數(shù)據(jù)的緩存包括兩個(gè)級(jí):一級(jí)緩存,在Session的級(jí)別上進(jìn)行,主要是對(duì)象緩存,以其id為鍵保存對(duì)象,在Session的生命期間存在;二級(jí)緩存,在SessionFactory的級(jí)別上進(jìn)行,有對(duì)象緩存和查詢緩存,查詢緩存以查詢條件為鍵保存查詢結(jié)果,在SessionFactory的生命期間存在。默認(rèn)地,Hibernate只啟用一級(jí)緩存,通過正確地使用二級(jí)緩存,往往可以獲得意想不到的性能。
1)對(duì)象緩存:
當(dāng)抓取一個(gè)對(duì)象之后,Hiberate將其以id為鍵緩存起來(lái),當(dāng)下次碰到抓取id相同的對(duì)象時(shí),可以使用如下配置
方法1:在緩存對(duì)象上配置
<class ...>
<cache useage="read-only/write/...." regions="group" />
</class>
useage表示使用什么類型的緩存,譬如只讀緩
存、讀寫緩存等等(具體參見Hibernate參考指南),值得注意的時(shí),有部分緩存在Hibernate的實(shí)現(xiàn)中不支持讀寫緩存,譬如 JBossCache在Hibernate的實(shí)現(xiàn)中只是一種只讀緩存,具體緩存實(shí)現(xiàn)對(duì)緩存類型的支持情況,可以參見
org.hibernate.cache包
regions表示緩存分塊,大部分的緩存實(shí)現(xiàn)往往對(duì)緩存進(jìn)行分塊,該部分是可選的,詳細(xì)參見各緩存實(shí)現(xiàn)
方法2:在hibernate.cfg.xml中配置
<cache class=".." useage=".." regions=".."/>
我認(rèn)為第二種更好,可以統(tǒng)一管理
2)查詢緩存
查詢時(shí)候?qū)⒉樵兘Y(jié)果以查詢條件為鍵保存起來(lái),需要配置如下
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(...);
需要注意的是,查詢緩存與對(duì)象緩存要結(jié)合更有效,因?yàn)椴樵兙彺鎯H緩存查詢結(jié)果列表的主鍵數(shù)據(jù)
一 般情況下在開發(fā)中,對(duì)一些比較穩(wěn)定而又被頻繁引用的數(shù)據(jù),譬如數(shù)據(jù)字典之類的,將其進(jìn)行二級(jí)緩存,對(duì)一些查詢條件和查詢數(shù)據(jù)變化不頻繁而又常常被使用的查
詢,將其進(jìn)行二級(jí)緩存。由于二級(jí)緩存是放在內(nèi)存中,而且Hibernate的緩存不是弱引用緩存(WeekReference),所以注意不要將大塊的數(shù) 據(jù)放入其中,否則可能會(huì)被內(nèi)存造成比較大的壓力。
3.批量數(shù)據(jù)操作
當(dāng)進(jìn)行大批量數(shù)據(jù)操作(幾萬(wàn)甚至幾十幾百萬(wàn))時(shí),需要注意兩點(diǎn),一,批量提交,二,及時(shí)清除不需要的一級(jí)緩存數(shù)據(jù)
1) 所謂的批量提交,就是不要頻繁使用session的flush,每一次進(jìn)行flush,Hibernate將PO數(shù)據(jù)于數(shù)據(jù)庫(kù)進(jìn)行同步,對(duì)于海量級(jí)數(shù)據(jù)操 作來(lái)說(shuō)是性能災(zāi)難(同時(shí)提交幾千條數(shù)據(jù)和提交一條數(shù)據(jù)flush一次性能差別可能會(huì)是幾十倍的差異)。一般將數(shù)據(jù)操作放在事務(wù)中,當(dāng)事務(wù)提交時(shí)
Hibernate自動(dòng)幫你進(jìn)行flush操作。
2)及時(shí)清除不需要的一級(jí)緩存數(shù)據(jù):由于Hibernate默認(rèn)采用一級(jí)緩存,而在session的生命期間,所有數(shù)據(jù)抓取之后會(huì)放入一級(jí)緩存中,而當(dāng)數(shù)據(jù)規(guī)模比較龐大時(shí),抓取到內(nèi)存中的數(shù)據(jù)會(huì)讓內(nèi)存壓力非常大,一般分批操作數(shù)據(jù),被一次操作之后將一級(jí)緩存清除,譬如
session.clear(User.class)