zoukankan      html  css  js  c++  java
  • Hibernate的二级缓存

    一、缓存(Cache):计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存或者硬盘
    二、Hibernate中提供了两个级别的缓存
           1、第一级别的缓存是 Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的
           2、第二级别的缓存是 SessionFactory级别的缓存,它是属于进程范围的缓存

    注意本篇的测试环境使用--HIbernate之HQL所搭建的环境

    一级缓存:

    缓存范围:缓存只能被当前Session对象访问。缓存的生命周期依赖于Session的生命周期,当Session被关闭后,缓存也就结束生命周期。
    @Test
    	public void testHibernateFirstLevelCache(){
    		Employee emp=(Employee) session.get(Employee.class, 8);
    		System.out.println(emp.getName());
    		Employee emp1=(Employee) session.get(Employee.class, 8);
    		System.out.println(emp1.getName());
    	}
    控制台信息:
    Hibernate: 
        select
            employee0_.ID as ID1_1_0_,
            employee0_.NAME as NAME2_1_0_,
            employee0_.SALARY as SALARY3_1_0_,
            employee0_.EMAIL as EMAIL4_1_0_,
            employee0_.DEPT_ID as DEPT_ID5_1_0_ 
        from
            EMPLOYEE employee0_ 
        where
            employee0_.ID=?
    name6
    name6
    在执行第二个查询的时候并没有发出SQL语句到数据库查询,而是直接从session缓存中读取。

    Hibernate一些与一级缓存相关的操作(时间点):

    数据放入缓存:

    1. save()。当session对象调用save()方法保存一个对象后,该对象会被放入到session的缓存中。

    2. get()load()。当session对象调用get()load()方法从数据库取出一个对象后,该对象也会被放入到session的缓存中。

    3. 使用HQLQBC等从数据库中查询数据。

    二级缓存:

    1.SessionFactory级别的缓存,可以跨越Session存在,可以被多个Session所共享
    2.SessionFactory的缓存可以分为两类:
      –内置缓存: Hibernate 自带的, 不可卸载.通常在Hibernate的初始化阶段,Hibernate 会把映射元数据和预定义的 SQL语句放到SessionFactory的缓存中,映射元数据是映射文件中数据(.hbm.xml文件中的数据)的复制.该内置缓存是只读的.
      –外置缓存(二级缓存):一个可配置的缓存插件.在默认情况下,SessionFactory不会启用这个缓存插件.外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或硬盘

    3.适合放到二级缓存中:

    1)经常被访问

    2)改动不大

    3)数量有限

    4不是很重要的数据,允许出现偶尔并发的数据。 

    这样的数据非常适合放到二级缓存中的。

    4. 二级缓存的并发访问策略:
    -两个并发的事务同时访问持久层的缓存的相同数据时,也有可能出现各类并发问题.
    -二级缓存可以设定以下 4 种类型的并发访问策略,每一种访问策略对应一种事务隔离级别:
    –非严格读写(Nonstrict-read-write):不保证缓存与数据库中数据的一致性. 提供Read Uncommited事务隔离级别,对于极少被修改,而且允许脏读的数据,可以采用这种策略
    读写型(Read-write):提供ReadCommited数据隔离级别.对于经常读但是很少被修改的数据,可以采用这种隔离类型,因为它可以防止脏读
    –事务型(Transactional):仅在受管理环境下适用.它提供了 RepeatableRead事务隔离级别. 对于经常读但是很少被修改的数据,可以采用这种隔离类型,因为它可以防止脏读和不可重复读
    –只读型(Read-Only):提供 Serializable数据隔离级别,对于从来不会被修改的数据,可以采用这种访问策略

    使用Hibernate的二级缓存:

     1.选择合适的缓存插件: EHCache(jar包和配置文件), 并编译器配置文件
        a、将hibernatehibernate-release-4.2.5.Finalhibernate-release-4.2.5.Finalliboptionalehcache路径下的所有jar包加入到工程的lib目录,并BuildPath!
              ehcache-core-2.4.3,jar
              hibernate-ehcache-4.2.5.Final.jar
              slf4j-api-1.6.1.jar
        b、将hibernatehibernate-release-4.2.5.Finalhibernate-release-4.2.5.Finalprojectetc路径下的ehcache.xml配置文件加入到工程的类路径下
     2. 修改Hibernate的配置文件:hibernate.cfg.xml  ,加入如下配置:
         1、 开启二级缓存:    
    <!-- 启用二级缓存 -->
           <property name="cache.use_second_level_cache">true</property> 
         2、指定使用的二级缓存产品:
    <!-- 配置使用的二级缓存产品 -->
           <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
        3、指定使用二级缓存的持久化类
    <!-- 对那个持久化类启用二级缓存 -->
           <class-cache usage="read-write" class="com.elgin.hibernate.entity.Employee"/>
    配置这一步的时候需要注意:class-cache元素最好放到mapping元素的下面,否则会报xml解析错误:元素类型为 "session-factory" 的内容必须匹配 "(property*,mapping*,(class-cache|collection-cache)*,event*,listener*)"
    实际上这个配置也可以配置在持久化类对应的hbm配置文件中。
    *******************************二级缓存测试开始**************************
    未使用二级缓存的测试:
       @Test
    	public void testHibernateSecondLevelCache(){
    		Employee emp=(Employee) session.get(Employee.class, 8);
    		System.out.println(emp.getName());
    		transcation.commit();
    		session.close();
    		session=sessionFactory.openSession();
    		transcation=session.beginTransaction();
    		Employee emp1=(Employee) session.get(Employee.class, 8);
    		System.out.println(emp1.getName());
    	}
    运行之后,控制台显示:
    Hibernate: 
        select
            employee0_.ID as ID1_1_0_,
            employee0_.NAME as NAME2_1_0_,
            employee0_.SALARY as SALARY3_1_0_,
            employee0_.EMAIL as EMAIL4_1_0_,
            employee0_.DEPT_ID as DEPT_ID5_1_0_ 
        from
            EMPLOYEE employee0_ 
        where
            employee0_.ID=?
    name6
    Hibernate: 
        select
            employee0_.ID as ID1_1_0_,
            employee0_.NAME as NAME2_1_0_,
            employee0_.SALARY as SALARY3_1_0_,
            employee0_.EMAIL as EMAIL4_1_0_,
            employee0_.DEPT_ID as DEPT_ID5_1_0_ 
        from
            EMPLOYEE employee0_ 
        where
            employee0_.ID=?
    name6
    通过控制台结果可以看出:由于第二次查询之前使用了新的session,缓存中无数据,并且未开启二级缓存,所以重新发出了SQL语句进行了查询。
    使用二级缓存的测试:
       @Test
    	public void testHibernateSecondLevelCache(){
    		Employee emp=(Employee) session.get(Employee.class, 8);
    		System.out.println(emp.getName());
    		transcation.commit();
    		session.close();
    		session=sessionFactory.openSession();
    		transcation=session.beginTransaction();
    		Employee emp1=(Employee) session.get(Employee.class, 8);
    		System.out.println(emp1.getName());
    	}
    控制台打印显示:
    Hibernate: 
        select
            employee0_.ID as ID1_1_0_,
            employee0_.NAME as NAME2_1_0_,
            employee0_.SALARY as SALARY3_1_0_,
            employee0_.EMAIL as EMAIL4_1_0_,
            employee0_.DEPT_ID as DEPT_ID5_1_0_ 
        from
            EMPLOYEE employee0_ 
        where
            employee0_.ID=?
    name6
    name6
    通过与未使用的结果对比发现,第二次并没有发出SQL语句,而是直接使用,这就是Hibernate的二级缓存起作用了!

    Hibernate集合级别的二级缓存配置:

    1. 在上述hibernate配置文件的基础上新增如下配置:    
    <class-cache usage="read-write" class="com.elgin.hibernate.entity.Department"/>
    <collection-cache usage="read-write" collection="com.elgin.hibernate.entity.Department.emps"/>
    很明显上面的一句是开启持久化类Department的二级缓存,下面是开启Department下名为emps的集合属性开启二级缓存
    注意:开启集合属性二级缓存的同时,一定要为集合中保存的持久化类开启二级缓存(本例中为Employee类,上述配置已开启,直接测试)
    @Test
    	public void testCollectionSecondLevelCache(){
    		Department dept=(Department) session.get(Department.class, 6);
    		System.out.println(dept.getName());
    		System.out.println(dept.getEmps().size());
    		transcation.commit();
    		session.close();
    		session=sessionFactory.openSession();
    		transcation=session.beginTransaction();
    		Department dept1=(Department) session.get(Department.class, 6);
    		System.out.println(dept1.getName());
    		System.out.println(dept1.getEmps().size());
    	}
    控制台信息:
    Hibernate: 
        select
            department0_.ID as ID1_0_0_,
            department0_.NAME as NAME2_0_0_ 
        from
            DEPARTMENT department0_ 
        where
            department0_.ID=?
    测试部
    Hibernate: 
        select
            emps0_.DEPT_ID as DEPT_ID5_0_1_,
            emps0_.ID as ID1_1_1_,
            emps0_.ID as ID1_1_0_,
            emps0_.NAME as NAME2_1_0_,
            emps0_.SALARY as SALARY3_1_0_,
            emps0_.EMAIL as EMAIL4_1_0_,
            emps0_.DEPT_ID as DEPT_ID5_1_0_ 
        from
            EMPLOYEE emps0_ 
        where
            emps0_.DEPT_ID=?
    2
    测试部
    2
    从上述信息可以看出先查询dept,并且dept中的emps属性使用了延迟加载。在使用新的session查询时,并没有发出SQL语句,而是直接使用,这就是配置的集合缓存的作用!

    二级缓存配置文件ehcache.xml的配置

    <ehcache>
        <!-- Sets the path to the directory where cache .data files are created.
    
             If the path is a Java System Property it is replaced by
             its value in the running VM.
    
             The following properties are translated:
             user.home - User's home directory
             user.dir - User's current working directory
             java.io.tmpdir - Default temp file path 
             指定一个目录,当 EHCache 把数据写到硬盘上时, 将把数据写到这个目录下.
         -->
        <diskStore path="java.io.tmpdir"/>
        <!--Default Cache configuration. These will applied to caches programmatically created through
            the CacheManager.
    
            The following attributes are required for defaultCache:
    
            maxInMemory       - Sets the maximum number of objects that will be created in memory
            eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                                is never expired.
            timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                                if the element is not eternal. Idle time is now - last accessed time
            timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                                if the element is not eternal. TTL is now - creation time
            overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                                has reached the maxInMemory limit.
             设置缓存的默认数据过期策略 
            -->
        <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            />
    
    
        <!-- 
                    设定具体的命名缓存的数据过期策略。每个命名缓存代表一个缓存区域
                    缓存区域(region):一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。
                                    如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。即:<defaultCache.../>
                    Hibernate在不同的缓存区域保存不同的类/集合。
                                 对于类而言,区域的名称是类名。如:com.elgin.hibernate.entity.Employee
                                 对于集合而言,区域的名称是类名加属性名。如com.elgin.hibernate.entity.Department.emps 
         -->
         <!-- 
                    name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字 
    		  maxElementsInMemory:设置基于内存的缓存中可存放的对象最大数目 
    		
    		  eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false 
    		  timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。
    		                  如果此值为0,表示对象可以无限期地处于空闲状态。 
    		  timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。如果此值为0,表示对象可以无限期地存在于缓存中.
    		                  该属性值必须大于或等于 timeToIdleSeconds 属性值 
    		
    		  overflowToDisk:设置基于内存的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中 
          -->
        <cache name="com.elgin.hibernate.entity.Employee"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="300"
            timeToLiveSeconds="600"
            overflowToDisk="true"
            />
    
        <cache name="com.elgin.hibernate.entity.Department.emps"
            maxElementsInMemory="1000"
            eternal="true"
            timeToIdleSeconds="0"
            timeToLiveSeconds="0"
            overflowToDisk="false"
            /> 
    
    </ehcache>

    Hibernate二级缓存的查询缓存

    1. 默认情况下,设置的二级缓存对HQL查询和QBC查询是无效的, 可以通过如下设置使其生效:
        a、在hibernate的配置文件hibernate.cfg.xml中增加如下配置,声明开启查询缓存
    <property name="cache.use_query_cache">true</property>
       b、在代码中 调用Query或者Criteria的setCacheable(true)方法
    2. 查询缓存依赖于二级缓存,如果想要使用,必须先配置Hibernate的二级缓存
    测试代码:
    @Test
    	public void testQueryCache(){
    		String hql="from Employee";
    		Query query=session.createQuery(hql);
    		query.setCacheable(true);
    		List<Employee> emps=query.list();
    		System.out.println(emps.size());
    		
    		List<Employee> emps1=query.list();
    		System.out.println(emps1.size());
    	}
    控制台显示:
    Hibernate: 
        select
            employee0_.ID as ID1_1_,
            employee0_.NAME as NAME2_1_,
            employee0_.SALARY as SALARY3_1_,
            employee0_.EMAIL as EMAIL4_1_,
            employee0_.DEPT_ID as DEPT_ID5_1_ 
        from
            EMPLOYEE employee0_
    20
    20
    可以看出,第二次查询并没有发出SQL语句,说明第一次的结果被缓存了。










       


  • 相关阅读:
    CodeForces gym Nasta Rabbara lct
    bzoj 4025 二分图 lct
    CodeForces 785E Anton and Permutation
    bzoj 3669 魔法森林
    模板汇总——快读 fread
    bzoj2049 Cave 洞穴勘测 lct
    bzoj 2002 弹飞绵羊 lct裸题
    HDU 6394 Tree 分块 || lct
    HDU 6364 Ringland
    nyoj221_Tree_subsequent_traversal
  • 原文地址:https://www.cnblogs.com/elgin-seth/p/5293750.html
Copyright © 2011-2022 走看看