Ehcache学习

Ehcache学习

1. 介绍

ehcache可以作为Hibernate和mybatis的二级缓存,并且可以很好的与spring结合。

Hibernate的缓存有两级

  1. 一级缓存:Session级别的缓存,每个session都有自己的cache,当前操作对象都会被保留在cache中,事务提交或回滚了,这个session也就关闭,cache就没有了,所以一级缓存生命周期非常短暂。
  2. 二级缓存:SessionFactory范围的缓存,可以被来自同一个*SessionFactory中的Session所共享*,这种缓存使用的范围更广,有不同的实现,比如Ehcache,OsCache。

缓存的方式有四种:

  1. CacheConcurrencyStrategy.READ_ONLY ,只读模式,在此模式下,如果对数据进行更新操作,会有异常;
  2. CacheConcurrencyStrategy.READ_WRITE ,读写模式在更新缓存的时候会把缓存里面的数据换成一个锁,其它事务如果去取相应的缓存数据,发现被锁了,直接就去数据库查询;
  3. CacheConcurrencyStrategy.NONSTRICT_READ_WRITE ,不严格的读写模式则不会的缓存数据加锁;
  4. CacheConcurrencyStrategy.TRANSACTIONAL ,事务模式指缓存支持事务,当事务回滚时,缓存也能回滚,只支持 JTA 环境。

缓存的注释写法如下,加在 Entity 的 java 类上:

  @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)

2.配置

ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <diskStore path="java.io.tmpdir" />
    <defaultCache maxElementsInMemory="1000" eternal="false"
        timeToIdleSeconds="1200" timeToLiveSeconds="1200"
        overflowToDisk="true" clearOnFlush="true"
        memoryStoreEvictionPolicy="LFU">
    </defaultCache>
    <cache name="SimplePageFragmentCachingFilter" maxElementsInMemory="10"
        maxElementsOnDisk="10" eternal="false" overflowToDisk="true"
        diskSpoolBufferSizeMB="20" timeToIdleSeconds="120"
        timeToLiveSeconds="120" memoryStoreEvictionPolicy="LFU" />
    <!-- 单独对某个entity的缓存策略设置
        <cache name="com.shop.domain.User"
        maxElementsInMemory="100" eternal="false" timeToIdleSeconds="1200"
        timeToLiveSeconds="1200" overflowToDisk="true" clearOnFlush="true">
        </cache>
    -->
</ehcache>

persistence.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
          http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
    version="1.0">
    <persistence-unit name="fmcgshop" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <class>com.iskyshop.foundation.domain.ItemSort</class>
        <properties>
            <property name="hibernate.show_sql" value="false" />
            <property name="hibernate.hbm2ddl.auto" value="update" />
            <!--Ehcache二级缓存配置-->
            <property name="hibernate.cache.provider_class"
                value="org.hibernate.cache.EhCacheProvider" />
            <property name="hibernate.cache.provider_configuration"
                value="/ehcache.xml" />
            <property name="hibernate.cache.use_second_level_cache"
                value="true" />
            <property name="hibernate.cache.use_query_cache"
                value="true" />
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/shop"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
        </properties>
    </persistence-unit>
</persistence>

entityManagerFactory

<bean name="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="persistenceXmlLocation" value="classpath:persistence.xml" />
        <property name="dataSource" ref="dataSource" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="database" value="MYSQL" />
                <property name="showSql" value="false" />
                <property name="generateDdl" value="false" />
            </bean>
        </property>
        <property name="jpaProperties">
            <props>
                <!-- 自定义方言 -->
                <prop key="hibernate.dialect">
                    com.iskyshop.core.dialect.SystemMySQL5Dialect
                </prop>
                <!-- 二级缓存配置 -->
                <prop key="hibernate.cache.provider_class">
                    net.sf.ehcache.hibernate.SingletonEhCacheProvider
                </prop>
                <prop key="hibernate.cache.use_query_cache">true</prop>
                <prop key="hibernate.cache.use_second_level_cache">
                    true
                </prop>
                <prop key="hibernate.use_sql_comments">false</prop>
                <prop key="hibernate.format_sql">false</prop>
                <prop key="hibernate.generate_statistics">true</prop>
            </props>
        </property>
    </bean>

配置页面缓存

页面缓存主要用Filter过滤器对请求的url进行过滤,如果该url在缓存中出现。那么页面数据就从缓存对象中获取,并以gzip压缩后返回。其速度是没有压缩缓存时速度的3-5倍,效率相当之高!其中页面缓存的过滤器有CachingFilter,一般要扩展filter或是自定义Filter都继承该CachingFilter。
CachingFilter功能可以对HTTP响应的内容进行缓存。这种方式缓存数据的粒度比较粗,例如缓存整张页面。它的优点是使用简单、效率高,缺点是不够灵活,可重用程度不高。

EHCache使用SimplePageCachingFilter类实现Filter缓存。该类继承自CachingFilter,有默认产生cache key的calculateKey()方法,该方法使用HTTP请求的URI和查询条件来组成key。也可以自己实现一个Filter,同样继承CachingFilter类,然后覆写calculateKey()方法,生成自定义的key。

CachingFilter输出的数据会根据浏览器发送的Accept-Encoding头信息进行Gzip压缩。

/**
 * 页面缓存,主要用filter过滤器对请求的url进行过滤,如果该url在缓存中出现,那么页面数据就从缓存中取,并以GZIP压缩后返回
 * Page caching, primarily using the filter filter to filter the requested url,
 * if the url appears in the cache,the page data is fetched from the cache and returned with GZIP compression
 */
public class PageCacheFiler extends SimplePageFragmentCachingFilter {
    private static final String FILTER_URL_PATTERNS = "patterns";
    private static String[] cacheURLs;

    private void init() throws CacheException {
        String patterns = this.filterConfig.getInitParameter("patterns");
        patterns = "/head.htm,/floor.htm,/advert_invoke.htm,public.css,index.css,goods.css,integral.css,user.css,window.css,jquery-1.6.2.js,jquery.lazyload.js,jquery-ui-1.8.21.js,jquery.shop.common.js,jquery.validate.min.js,jquery.jqzoom-core.js,jquery.metadata.js,jquery.rating.pack.js";
        PageCacheFiler.cacheURLs = StringUtils.split(patterns, ",");
    }

    protected void doFilter(final HttpServletRequest request, final HttpServletResponse response, final FilterChain chain) throws Exception {
        if (PageCacheFiler.cacheURLs == null) {
            this.init();
        }
        final String url = request.getRequestURI();
        final String include_url = CommUtil.null2String(request.getAttribute("javax.servlet.include.request_uri"));
        boolean flag = false;
        if (PageCacheFiler.cacheURLs != null && PageCacheFiler.cacheURLs.length > 0) {
            String[] cacheURLs;
            for (int length = (cacheURLs = PageCacheFiler.cacheURLs).length, i = 0; i < length; ++i) {
                final String cacheURL = cacheURLs[i];
                if (include_url.trim().equals("")) {
                    if (url.contains(cacheURL.trim())) {
                        //如果请求的url 在缓存pattern中
                        flag = true;
                        break;
                    }
                } else if (include_url.contains(cacheURL.trim())) {
                    flag = true;
                    break;
                }
            }
        }
        // 如果包含我们要缓存的url 就缓存该页面,否则执行正常的页面转向
        if (flag) {
            super.doFilter(request, response, chain);
        } else {
            chain.doFilter(request, response);
        }
    }

    private boolean headerContains(final HttpServletRequest request, final String header, final String value) {
        this.logRequestHeaders(request);
        final Enumeration accepted = request.getHeaders(header);
        while (accepted.hasMoreElements()) {
            final String headerValue = (String) accepted.nextElement();
            if (headerValue.contains(value)) {
                return true;
            }
        }
        return false;
    }

    protected boolean acceptsGzipEncoding(final HttpServletRequest request) {
        final boolean ie6 = this.headerContains(request, "User-Agent", "MSIE 6.0");
        final boolean ie2 = this.headerContains(request, "User-Agent", "MSIE 7.0");
        return this.acceptsEncoding(request, "gzip") || ie6 || ie2;
    }

    /**
     * 这个方法非常重要,重写计算缓存key的方法,没有该方法include的url值会出错
     */
    protected String calculateKey(final HttpServletRequest httpRequest) {
        final String include_url = CommUtil.null2String(httpRequest.getAttribute("javax.servlet.include.request_uri"));
        final StringBuilder stringBuffer = new StringBuilder();
        if (include_url.equals("")) {
            stringBuffer.append(httpRequest.getRequestURI()).append(httpRequest.getQueryString());
            return stringBuffer.toString();
        }
        stringBuffer.append(CommUtil.null2String(httpRequest.getAttribute("javax.servlet.include.request_uri"))).append(CommUtil.null2String(httpRequest.getAttribute("javax.serlvet.include.query_string")));
        return stringBuffer.toString();
    }
}

web.xml

<!--页面缓存配置-->
    <filter>
        <filter-name>SimplePageFragmentCachingFilter</filter-name>
        <filter-class>
            com.fmcg.core.ehcache.PageCacheFiler
        </filter-class>
        <init-param>
            <param-name>patterns</param-name>
            <param-value>
            </param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>SimplePageFragmentCachingFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>request</dispatcher>
        <dispatcher>forward</dispatcher>
        <dispatcher>include</dispatcher>
    </filter-mapping>

3.清除缓存

当数据发生变化时,需要清除缓存

        CacheManager manager = CacheManager.create();   
        String[] names = manager.getCacheNames();
        for (String name : names) {
          //清除所有对象缓存
            if (!name.equalsIgnoreCase("SimplePageFragmentCachingFilter")) {
              manager.clearAllStartingWith(name);
            }
        }
            //清除页面缓存
            Ehcache cache = manager.getEhcache("SimplePageCachingFilter");
            manager.clearAllStartingWith("SimplePageFragmentCachingFilter");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值