1. 程式人生 > >Hibernate的二級快取技術 EhCache

Hibernate的二級快取技術 EhCache

一、Ehcache簡介
      EhCache是Hibernate的二級快取技術之一,可以把查詢出來的資料儲存在記憶體或者磁碟,節省下次同樣查詢語句再次查詢資料庫,大幅減輕資料庫壓力;
      當用Hibernate對關係型資料庫表進行更改時(DELETE/UPDATE),這時EhCache會自動把快取中關於此表的所有快取全部刪除掉,以此來達到同步效果。基於這一點來說,ehcache不適合那種經常修改資料庫表的情形。
      Ehcache適用場合:

    1)對資料庫表很少修改;
    2)對併發要求不是很嚴格。


      對於工業感測器實時資料,程式對其儲存後,利用二級快取技術檢視歷史資料。我本人也是基於這種場景才用ehcache的
二、準備工作
[list]
ehcache-core.jar ehcache核心包,在maven專案中,類似於如下引入
Xml程式碼  收藏程式碼

    <dependency>  
        <groupId>net.sf.ehcache</groupId>  
        <artifactId>ehcache-core</artifactId>  
        <version>2.4.3</version>  
    </dependency>  

ehcache-spring-annotations 基於spring註解ehcache,在maven專案中,類似於如下引入
Xml程式碼  收藏程式碼

    <groupId>com.googlecode.ehcache-spring-annotations</groupId>  
        <artifactId>ehcache-spring-annotations</artifactId>  
        <version>1.1.2</version>  
        <type>jar</type>  
        <scope>compile</scope>  
    </dependency>  

其他jar包(例如hibernate系列、spring web系列,commons系列)
[/list]
三、Ehcache.xml配置
      Ehcache.xml檔案時ehcache二級快取最主要的我配置檔案,它定義了對哪些實體物件進行快取,以及快取策略,直接上一個配置片段把
Xml程式碼  收藏程式碼

    <?xml version="1.0" encoding="UTF-8"?>  
    <ehcache>  
        <cache name="entity.HistoryData"  
               maxElementsInMemory="5000" eternal="false" timeToIdleSeconds="300"  
               timeToLiveSeconds="7200" overflowToDisk="false" />  
    </ehcache>  



name    要快取的實體類的名稱
maxElementsInMemory    設定該快取實體的最大個數
eternal    物件是否永久有效
timeToIdleSeconds    設定物件在失效前的允許閒置時間(單位:s)
timeToLiveSeconds    設定物件在失效前允許存活時間(單位:s)
overflowToDisk    當快取中物件達到maxElementsInMemory時,存入磁碟
diskSpoolBufferSizeMB    設定磁碟緩衝區大小
maxElementsOnDisk    設定磁碟能快取最大物件個數
diskPersistent    是否快取虛擬機器重啟期數
diskExpiryThreadIntervalSeconds    磁碟失效執行緒執行時間間隔,預設是120秒。
memoryStoreEvictionPolicy    當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
clearOnFlush    記憶體數量最大時是否清除。

兩個問題:

    該檔案放置在那裡?該檔案可以放置在工程的任意位置
    該檔案如何引入?或者說ehcache.xml路徑是如何確定的?


    我們可以通過查閱相關原始碼,可知
在hibernate.cache.provider所指定的類net.sf.ehcache.hibernate.EhCacheRegionFactory類的抽象類AbstractEhcacheRegionFactory中,對NET_SF_EHCACHE_CONFIGURATION_RESOURCE_NAME中有明確的解釋:
Java程式碼  收藏程式碼

    /**
    The Hibernate system property specifying the location of the ehcache configuration file name.
        If not set, ehcache.xml will be looked for in the root of the classpath.
    If set to say ehcache-1.xml, ehcache-1.xml will be looked for in the root of the classpath.
    **/  


[/list]
四、整合到spring
      這一個步驟分為兩部分,一部分是對ehcache開啟註解功能,另一部分是整合hibernate sesionFactory中
[list]
配置註解,新建spring-ehcache.xml,並在web.xml param中引入,如下:
Xml程式碼  收藏程式碼

    <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"  
        xsi:schemaLocation="    
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    
            http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd">  
        <ehcache:annotation-driven />  
        <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
            <property name="configLocation" value="classpath:ehcache.xml" />  
        </bean>  
        <ehcache:config cache-manager="cacheManager"/>  
    </beans>  


注:對於名稱空間,前提是引入ehcache-spring-annotations.jar包,否則提示無法解析該名稱空間。ConfigLocation指定ehcache.xml配置檔案所在的路徑。
hibernate sessionFactory配置,新建spring-sessionFactory.xml,並在web.xml中引入,如下
Xml程式碼  收藏程式碼

    <?xml version="1.0" encoding="utf-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xmlns:util="http://www.springframework.org/schema/util" xmlns:jee="http://www.springframework.org/schema/jee"  
        xmlns:lang="http://www.springframework.org/schema/lang" xmlns:jms="http://www.springframework.org/schema/jms"  
        xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"  
        xmlns:context="http://www.springframework.org/schema/context"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
                               http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd  
                               http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd  
                               http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd  
                               http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd  
                               http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
                               http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
                               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
        
       <context:property-placeholder location="classpath:/resource.properties" />  
         
       <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
            <property name="driverClass" value="${jdbc.driverClassName}"/>  
            <property name="jdbcUrl" value="${jdbc.url}"/>  
            <property name="user" value="${jdbc.username}"/>  
            <property name="password" value="${jdbc.password}"/>  
            <property name="idleConnectionTestPeriod" value="120"/>  
        </bean>  
        <bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">  
            <property name="dataSource" ref="dataSource"/>  
            <property name="schemaUpdate" value="${hibernate.schemaUpdate}"/>  
            <property name="packagesToScan" value="${hibernate.packageScan}"/>  
            <property name="hibernateProperties">  
                <value>  
                    hibernate.dialect ${hibernate.dialect}  
                    hibernate.show_sql ${hibernate.show_sql}  
                    hibernate.cache.region.factory_class ${hibernate.cache.provider}  
                    hibernate.cache.use_second_level_cache ${hiberante.second.cache}  
            [color=red]Hibernate.cache.use_query_cache true [/color]      
                </value>  
            </property>  
        </bean>  
    </beans>  


其中,

    hibernate.cache.provider 選擇二級快取實現的類
    Hibernate.cache.use_second_level_cache:選擇是否開啟二級快取
    Hibernate.cache.use_query_cache 是否開啟查詢快取(重要,後面講到)


[/list]
五、測試
新建一個實體類
Java程式碼  收藏程式碼

    package entity;  
      
    import java.io.Serializable;  
    import java.util.Date;  
      
    import javax.persistence.Column;  
    import javax.persistence.Entity;  
    import javax.persistence.GeneratedValue;  
    import javax.persistence.Id;  
    import javax.persistence.Table;  
      
    import org.hibernate.annotations.Cache;  
    import org.hibernate.annotations.CacheConcurrencyStrategy;  
      
    @Entity  
    @Table(name="history_data")  
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)  
    public class HistoryData implements Serializable{  
        //省略  
    }  


把這個類對映到資料庫表中。並且設定快取方式

快取方式有四種
參考資料 寫道
CacheConcurrencyStrategy.NONE
   CacheConcurrencyStrategy.READ_ONLY ,只讀模式,在此模式下,如果對資料進行更新操作,會有異常;
   CacheConcurrencyStrategy.READ_WRITE ,讀寫模式在更新快取的時候會把快取裡面的資料換成一個鎖,其它事務如果去取相應的快取資料,發現被鎖了,直接就去資料庫查詢;
   CacheConcurrencyStrategy.NONSTRICT_READ_WRITE ,不嚴格的讀寫模式則不會的快取資料加鎖;
   CacheConcurrencyStrategy.TRANSACTIONAL ,事務模式指快取支援事務,當事務回滾時,快取也能回滾,只支援 JTA 環境。

快取的註釋寫法如下,加在 Entity 的 java 類上:
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

    如果我們要對hql查詢設定快取,可以通過設定this.getHibernateTemplate().setCacheQueries(boolean)來實現,前提是開啟查詢快取。下面是一個例子:
Java程式碼  收藏程式碼

    public List<HistoryData> list(String name,String location,Date startTime,  
                Date endTime, boolean alarm,int start,int limit){  
            String hql = "from HistoryData hd where 1=1 ";  
            if(StringUtils.isNotBlank(name)){  
                hql+= " and hd.name like '%"+name+"%' ";  
            }  
            if(StringUtils.isNotBlank(location)){  
                hql+= " and hd.location like '"+location+"' ";  
            }  
            this.getHibernateTemplate().setCacheQueries(true);  
            List<HistoryData> hds =  this.findByNamedParam(hql, null, null, start, limit);  
            this.getHibernateTemplate().setCacheQueries(false);  
            return hds;  
        }  



    呼叫這個方法,針對同一sql來講,只發送一條sql,下次查詢直接從快取中獲取。
注:this.getHibernateTemplate().setCacheQueries(true);後最好是要this.getHibernateTemplate().setCacheQueries(false),來關閉查詢快取,因為實際應用中,不可能所有的查詢我們都得設定快取。