• 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语句,说明第一次的结果被缓存了。










       


  • 相关阅读:
    GDI 设备环境句柄(2)
    GDI 像素(5)
    Api+Mvc增删查改
    sql语句全
    Mvc 导出
    触发器、事务
    计算时间戳的差
    SQL行转列经典例子(转载)
    Socket (套接字)通信
    MVC上传图片
  • 原文地址:https://www.cnblogs.com/elgin-seth/p/5293750.html
Copyright © 2020-2023  润新知