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

    1. 理解二级缓存定义

    Hibernate中提供了两个级别的缓存
    第一级别的缓存是 Session 级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate 管理的,一般情况下无需进行干预
    第二级别的缓存是 SessionFactory 级别的缓存,它是属于进程范围的缓存
    SessionFactory 的缓存可以分为两类:
    内置缓存:Hibernate 自带的,不可卸载. 通常在 Hibernate 的初始化阶段, Hibernate会把映射元数据和预定义的 SQL语句放到 SessionFactory的缓存中, 映射元数据是映射文件中数据的复制,而预定义 SQL 语句时 Hibernate 根据映射元数据推到出来的.该内置缓存是只读的.
    外置缓存(二级缓存):一个可配置的缓存插件.在默认情况下, SessionFactory不会启用这个缓存插件. 外置缓存中的数据是数据库数据的复制, 外置缓存的物理介质可以是内存或硬盘
     
     

    理解二级缓存的并发访问策略

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

    缓存中存放的数据

    适合放入二级缓存中的数据:
    很少被修改
    不是很重要的数据,允许出现偶尔的并发问题
    不适合放入二级缓存中的数据:
    经常被修改
    财务数据,绝对不允许出现并发问题
    与其他应用数据共享的数据
     

    缓存提供的供应商

    Hibernate 的二级缓存是进程或集群范围内的缓存,缓存中存放的是对象的散装数据
    二级缓存是可配置的的插件, Hibernate允许选用以下类型的缓存插件:
    EHCache:可作为进程范围内的缓存, 存放数据的物理介质可以使内存或硬盘, Hibernate 的查询缓存提供了支持
    OpenSymphony OSCache:可作为进程范围内的缓存,存放数据的物理介质可以使内存或硬盘,提供了丰富的缓存数据过期策略,Hibernate 的查询缓存提供了支持
    SwarmCache: 可作为集群范围内的缓存, 但不支持Hibernate 的查询缓存
    JBossCache:可作为集群范围内的缓存,支持 Hibernate 的查询缓存

    4 种缓存插件支持的并发访问策略(x代表支持, 空白代表不支持)

    配置进程范围内的二级缓存(配置ehcache缓存)

    name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字
    maxElementsInMemory:设置基于内存的缓存中可存放的对象最大数目
    eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSecondstimeToLiveSeconds属性;默认值是false
    timeToIdleSeconds:设置对象空闲最长时间,以秒为单位,超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
    timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。
    如果此值为
    0,表示对象可以无限期地存在于缓存中.该属性值必须大于或等于 timeToIdleSeconds属性值
    overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
    diskPersistentjvm结束时是否持久化对象true false 默认是false
    diskExpiryThreadIntervalSeconds指定专门用于清除过期对象的监听线程的轮询时间

    示例步骤:

    如何在项目中配置二级缓存:
      * 引入二级缓存的插件encache-1.5.0.jar
      * 先使用encache-1.5.0.jar该缓存的默认配置,此时执行的该jar包中的ehcache-failsafe.xml文件
          * 表示缓存中的数据存不下的情况下,存到硬盘的临时目录
                  <diskStore path="java.io.tmpdir"/>  %USERPROFILE%Local SettingsTemp该目录
          * 采用默认的配置,defaultCache
                  放入缓存中的数据要采用的默认配置(针对缓存中所有对象的)
               <defaultCache
    	            maxElementsInMemory="10"
    	            eternal="false"
    	            timeToIdleSeconds="120"
    	            timeToLiveSeconds="120"
    	            overflowToDisk="true"
    	            maxElementsOnDisk="10000000"
    	            diskPersistent="false"
    	            diskExpiryThreadIntervalSeconds="120"
                />
                
       * 在hibernate.cfg.xml中增加如下配置
            * 开启二级缓存,默认是不开启的
                 <property name="hibernate.cache.use_second_level_cache">true</property>
                 
            * 配置缓存提供的供应商
                  property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
                  
                  
             * 配置缓存
                 * 方法一:在*.hbm.xml文件配置
                 * 方法二: 在ibernate.cfg.xml配置(建议),要放置mapping元素的下面
                      <!--配置类级别的二级缓存-->
    			      <class-cache class="cn.itcast.cache.Customer" usage="read-write"/>
    			      <class-cache class="cn.itcast.cache.Order" usage="read-write"/>
    			      
                      <!-- 配置集合级别的二级缓存 -->
                      <collection-cache collection="cn.itcast.cache.Customer.orderes" usage="read-write"/>
                    
                        
        * 注使用二级缓存,还需要引入两个jar包
               ..libconcurrentackport-util-concurrent.jar
               ..libcommons-logging.jar
             
             
             
       * 针对某个对象设置缓存配置
           * 在src下新建ehcache.xml文件,文件的结构和ehcache-failsafe.xml文件相同
               * 在ehcache.xml文件增加如下配置,name指定该配置针对Order类
                 <cache
    	            name="cn.itcast.cache.Order"
    	            maxElementsInMemory="1"
    	            eternal="true"
    	            overflowToDisk="true"
    	            maxElementsOnDisk="100000"
    	            diskPersistent="true"
    	            diskExpiryThreadIntervalSeconds="120"
                 />      
       
       
        * 启用查询缓存
               * 在hibernate.cfg.xml文件中增加如下配置
                  <!-- 启用查询缓存 -->
                  <property name="hibernate.cache.use_query_cache">true</property>
                  
               * 在程序代码中使用query查询,查询的条件才能放置到二级缓存(查询缓存)中
                    query=session.createQuery("from Customer");
    		        //启用查询缓存
    		        query.setCacheable(true);
    		       //直接从二级缓存中获取数据
    		        query.list();


    示例代码:

    省略 Customer.java Order.java两个 bean对象

    Customer.hbm.xml 配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
     <hibernate-mapping>
       <class name="cn.itcast.cache.Customer" table="customers">
          <!--配置类级别的二级缓存,此时二级缓存中能存放Customer对象  -->
          <!-- <cache usage="read-write"/> -->
          <id name="id" type="integer">
            <column name="id"/>
            <generator class="increment"/>
          </id>
          <property name="name" type="string">
              <column name="name"/>
          </property>
          
          <property name="age" type="integer">
              <column name="age"/>
          </property>
          
          <set name="orderes" table="orders" inverse="true" lazy="true"> 
             <!-- 配置集合级别的二级缓存,此时orderes订单集合放入到二级缓存 -->
             <!-- <cache usage="read-write"/> -->
             <key>
               <column name="customer_id"/>
             </key>
              <one-to-many class="cn.itcast.cache.Order"/>
          </set>      
       </class>
     </hibernate-mapping>


    Order.hbm.xml 配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC 
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
     <hibernate-mapping>
       <class name="cn.itcast.cache.Order" table="orders">
          <id name="id" type="integer">
            <column name="id"/>
            <generator class="increment"/>
          </id>
          <property name="orderNumber" type="string">
              <column name="orderNumber"/>
          </property>
          <property name="price" type="double">
              <column name="price"/>
          </property>
         
          <many-to-one  name="customer" class="cn.itcast.cache.Customer">
               <column name="customer_id"/>
          </many-to-one>
          
       </class>
     </hibernate-mapping>


    hibernate.cfg.xml 配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-configuration PUBLIC
    	"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    	"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
    <hibernate-configuration>
       <session-factory>
          <property name="hibernate.connection.username">root</property>
          <property name="hibernate.connection.password">root</property>
          <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
          <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property>
          <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
          <property name="hibernate.hbm2ddl.auto">update</property>
          <property name="hibernate.show_sql">true</property>
          <property name="hibernate.format_sql">false</property>
    
          <!-- 配置数据库的隔离级别  数据库中的四种隔离级别,分别对应的值
                1:Read uncommitted isolation
    			2:Read committed isolation
    			4:Repeatable read isolation
    			8:Serializable isolation
          -->
          <property name="hibernate.connection.isolation">2</property>
         
          <!-- 配置session对象的生命周期和本地线程绑定 ,值为thread-->
          <property name="hibernate.current_session_context_class">thread</property>
         
         
          <!-- 开启二级缓存(设置可以使用二级缓存),默认是不开启的 -->
          <property name="hibernate.cache.use_second_level_cache">true</property>
         
          <!-- 配置缓存提供的供应商 -->
          <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
         
          <!-- 启用查询缓存 -->
          <property name="hibernate.cache.use_query_cache">true</property>
         
          <!-- 加载映射文件-->
          <mapping resource="cn/itcast/cache/Customer.hbm.xml"/>
          <mapping resource="cn/itcast/cache/Order.hbm.xml"/>
          
          
          <!-- 配置二级缓存中存放的数据类型,要放置到mapping元素的下面 -->
          <!--配置类级别的二级缓存-->
          <class-cache class="cn.itcast.cache.Customer" usage="read-write"/>
          <class-cache class="cn.itcast.cache.Order" usage="read-write"/>
          
          <!-- 配置集合级别的二级缓存 -->
          <collection-cache collection="cn.itcast.cache.Customer.orderes" usage="read-write"/>
          
       </session-factory>
    </hibernate-configuration>	


    ehcache.xml 二级缓存配置文件, 这个文件需要在 /src目录下, 验证时 maxElementsOnDisk="100000" 属性不支持

    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
    
        <!--
        The ehcache-failsafe.xml is a default configuration for ehcache, if an ehcache.xml is not configured.
        -->
        <diskStore path="D:/temp"/>
            <defaultCache
                maxElementsInMemory="10"
                eternal="false"
                timeToIdleSeconds="120"
                timeToLiveSeconds="120"
                overflowToDisk="true"
                maxElementsOnDisk="100000"
                diskPersistent="true"
                diskExpiryThreadIntervalSeconds="120"
                />
          <cache
                name="cn.itcast.cache.Order"
                maxElementsInMemory="1"
                eternal="true"
                overflowToDisk="true"
                maxElementsOnDisk="100000"
                diskPersistent="true"
                diskExpiryThreadIntervalSeconds="120"
                />
    </ehcache>


    AppCache.java  实验代码

    package cn.itcast.cache;
    
    import org.hibernate.Query;
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.junit.Test;
    
    public class AppCache {
    	private static  SessionFactory sf=null;
    	
    	static{
    		   Configuration config=new Configuration();
    	       config.configure("cn/itcast/cache/hibernate.cfg.xml");
    	       sf=config.buildSessionFactory();
    	}
    	
    
    	/*
    	 * 知识点12:测试二级缓存和散列数据
    	 *   * 测试类级别的二级缓存(使用get load)
    	 *   * 使用query测试查询级别的二级缓存
    	 */
    	@Test
    	public  void testSecondCache(){
    /**************************************************************************************************/		 
    		   Session session=sf.openSession();
    		   Transaction tx=session.beginTransaction();
    		   /**
    		    * 由于开启二级缓存
    		    *  * 下面的查询查询出id=1的客户后,
    		    *      * 放入该对象到一级缓存一份,
    		    *      * 同时还放入该数据到二级缓存中一份,放置查出的Customer对象到类级别的缓存区域中
    		    *  * 产生select语句
    		    */
    		   Customer c=(Customer)session.get(Customer.class, 1);
    		   System.out.println(c.getAge());
    		   tx.commit();
    		   session.close();  //一级缓存消失
    /**************************************************************************************************/
    		   session=sf.openSession();  //开启一个新的session
    		   tx=session.beginTransaction();
    		   
    		   /*
    		    *下面的查询查询出id=1的客户的过程
    		    *  * 先到session的一级缓存区查找id=1的客户
    		    *      * 如果找到 直接返回
    		    *      * 如果没有找到,到sessionFactory的二级缓存中查找id=1的客户对象
    		    *          * 如果找到 直接返回
    		    *          * 如果没有找到,在查询数据库
    		    *   
    		    */
    		   
    		   //从二级缓存中获取Customer对象
    		   Customer c1=(Customer)session.get(Customer.class, 1);
    		   System.out.println(c1.getAge()); 
    		   System.out.println("c1  "+c1);   // cn.itcast.cache.Customer@14b5f4a
    		   tx.commit();
    		   session.close();//
    /************************************************************************************************/	   
    		   session=sf.openSession();
    		   tx=session.beginTransaction();
    		   
    		   //从二级缓存中获取Customer对象
    		   Customer c2=(Customer)session.get(Customer.class, 1);   
    		   System.out.println(c2.getAge());
    		   System.out.println("c2  "+c2);   // cn.itcast.cache.Customer@ae533a,
    		   //C1不等于C2,二级缓存中不是直接存放对象,而是存放散列数据
    		   tx.commit();
    		   session.close();
    /*************************************************************************************************/
    	}
    	
    	
    	//知识点13:测试一级缓存更新数据会同步到二级缓存
    	@Test
    	public  void testUpdate(){
    /**************************************************************************************************/		 
    		   Session session=sf.openSession();
    		   Transaction tx=session.beginTransaction();
    		   
    		   Customer c=(Customer)session.get(Customer.class, 1);
    		   System.out.println(c.getAge()); 
               c.setAge(45);
    		   tx.commit();
    		   session.close();  
    
    		   session=sf.openSession();  //开启一个新的session
    		   tx=session.beginTransaction();
    		   
    		   //从二级缓存中获取Customer对象
    		   Customer c1=(Customer)session.get(Customer.class, 1);
    		   System.out.println(c1.getAge()); 
    		   tx.commit();
    		   session.close();//
    /*************************************************************************************************/
    	}
    	
    	//知识点xxxx:测试集合级别的二级缓存
    	//集合级别的缓存放置的查询的条件,真正的实体还是在类级别的缓存区域中
    	@Test
    	public  void testCollectionUpdate(){
    /**************************************************************************************************/		 
    		   Session session=sf.openSession();
    		   Transaction tx=session.beginTransaction();
    		   //Order对象放置放置类级别的二级缓存中
    		   /*
    		    * 客户关联的订单集合存放到集合级别的缓存中,此时集合级别的缓存中存放的该订单集合中订单的id
    		    *       1 ,2 ,3, 4, 5, 6,7,8,9,10
    		    */
    		   Customer c=(Customer)session.get(Customer.class, 1);
    		   System.out.println(c.getAge()); 
    		   tx.commit();
    		   session.close();  
    
    		   
    		   session=sf.openSession();  //开启一个新的session
    		   tx=session.beginTransaction();
    		  
    		   
    		   /**
    		    * 从二级缓存中获取Customer对象
    		    * 再次查询客户关联的订单集合
    		    *     * 到session的一级缓存中区查找,没有找到
    		    *     * 到二级缓存中,到集合级别的二级缓存中查找订单,集合级别的二级缓存中放置到订单的id[  1 ,2 ,3, 4, 5, 6,7,8,9,10]
    		    *        获取集合中订单的时候,select * from orders where id= 1 ,2 ,3, 4, 5, 6,7,8,9,10
    		    *        所以会产生10调价语句
    		    */  
    		   Customer c1=(Customer)session.get(Customer.class, 1);
    		   System.out.println(c1.getAge()); 
    		   tx.commit();
    		   session.close();//
    /*************************************************************************************************/
    	}
    	
    	//知识点14:测试二级缓存的数据存放到临时目录
    	@Test
    	public  void testTempFile(){
    /**************************************************************************************************/		 
    		   Session session=sf.openSession();
    		   Transaction tx=session.beginTransaction();
    		  
    		   Query query=session.createQuery("from Order o");
    		   query.list();
    		   
    		   tx.commit();
    		   session.close();
    /*************************************************************************************************/
    	}
    	
    	
    	//知识点15:时间戳缓存区域,不用所任何配置
    	@Test
    	public  void testUpdateTimeStamp(){
    /**************************************************************************************************/		 
    		   Session session=sf.openSession();
    		   Transaction tx=session.beginTransaction();
    		   //查询id=1的客户,放置该客户对象到一级缓存和二级缓存,同时还要把查询的时间放置到类级别的时间戳区域T1
    		   Customer c=(Customer)session.get(Customer.class, 1);  //select
    		   System.out.println(c.getAge());
    	
    //		   //修改的时候把修改的时间记录到更新时间戳缓存区域 T2
    //		   //修改年龄(insert update delete) 
    		   Query query=session.createQuery("update Customer c set c.age=90  where c.id=1");
    		   query.executeUpdate();
    		   		   
    		   tx.commit();
    		   session.close();  
    		   
    		   session=sf.openSession();  //开启一个新的session
    		   tx=session.beginTransaction();
    		   
    		   /*
    		    * 比对T1和T2的时间
    		    *   * T1>T2  不查询数据库
    		    *   * T1<T2  查询数据库
    		    */
    		   Customer c1=(Customer)session.get(Customer.class, 1);   //
    		   System.out.println(c1.getAge()); 
    		   tx.commit();
    		   session.close();//
    /*************************************************************************************************/
    	}
    	
    	
    	
    	/*
    	 * 知识点16: 查询缓存
    	 *   * 使用query接口
    	 */
    	@Test
    	public  void testQueryCache(){
    /**************************************************************************************************/		 
    		   Session session=sf.openSession();
    		   Transaction tx=session.beginTransaction();
    	
    		   /**
    		    * 如果没有配置,则类级别的二级缓存中,不能存放Customer对象
    		    *     <class-cache class="cn.itcast.cache.Customer" usage="read-write"/>
    		    *   
    		    * * 执行query查询session.createQuery("from Customer");
    		    *     * 目的查询所有的Customer对象,则对象的id放置查询缓存【1 2 3】
    		    *     * 对象的实体类级别的二级缓存中不能存放Customer对象
    		    */
    		   Query query=session.createQuery("from Customer");
    		   //启用查询缓存
    		   query.setCacheable(true);
    		   query.list();
    		   
    		   tx.commit();
    		   session.close();//
    		   
     /*************************************************************************************************/	   
    		   session=sf.openSession();
    		   tx=session.beginTransaction();
    	
    		   /**
    		    *  执行query查询session.createQuery("from Customer");
    		    *    * 到查询缓存中获取查询条件id【1 2 3】
    		    *    * 以id为条件到类级别的缓存中,获取Customer对象
    		    *        * 如果存在  不再查询数据库
    		    *        * 如何不存在 查询数据库 select * customers where id=1...
    		    *                              select * customers where id=3
    		    */
    		   query=session.createQuery("from Customer");
    		   //启用查询缓存
    		   query.setCacheable(true);
    		   //直接从二级缓存中获取数据
    		   query.list();
    		   
    		   
    		   tx.commit();
    		   session.close();//
    
    	}
    	
    }
    


     

  • 相关阅读:
    bugku crypto 告诉你一个秘密(ISCCCTF)
    递归法求组合数C(m,n)
    bugku 逆向 take the maze
    P1118 [USACO06FEB]数字三角形`Backward Digit Su`… (dfs)
    递归 dfs 记忆化搜索 动态规划
    c++ 取整:四舍五入 向上取整 向下取整
    2019 计蒜之道 初赛 第一场 商汤的AI伴游小精灵
    华南理工大学“三七互娱杯” D HRY and array
    华南理工大学 “三七互娱杯” G HRY and tree
    2019年湘潭大学程序设计竞赛(重现赛)
  • 原文地址:https://www.cnblogs.com/xj626852095/p/3647992.html
Copyright © 2011-2022 走看看