12.hibernate 二级缓存

1.配置c3p0连接池(了解)

*   引入c3p0-0.9.1.jar

*   在hibernate.cfg.xml文件中增加如下配置
    <!-- C3P0连接池设定-->
    <!-- 使用c3po连接池  配置连接池提供的供应商-->
    <property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider                                                                                                                                                     </property>
   <!--在连接池中可用的数据库连接的最少数目 -->
  <property name="c3p0.min_size">5</property>
  <!--在连接池中所有数据库连接的最大数目  -->
  <property name="c3p0.max_size">20</property>
  <!--设定数据库连接的过期时间,以秒为单位,
  如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
  <property name="c3p0.timeout">120</property>
  <!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
 <property name="c3p0.idle_test_period">3000</property>
 <!-- 设置自动提交 -->
 <property name="connection.autocommit">true</property>
*   Hibernate也有一个自带的连接池,默认的,有bug

*   实际上实在spring上面配置,这里可以配置的东西太少了

2.管理session

*   尽管让程序自主管理 Session 对象的生命周期也是可行的, 但是在实际 Java 应用中, 把管理 Session 对象的生命周期交给 Hibernate 管理, 可以简化 Java 应用程序代码和软件架构

*   Hibernate 3 自身提供了三种管理 Session 对象的方法
       Session 对象的生命周期与本地线程绑定
       Session 对象的生命周期与 JTA 事务绑定
       Hibernate 委托程序管理 Session 对象的生命周期

*   在Hibernate 的配置文件中, hibernate.current_session_context_class 属性用于指定Session 管理方式, 可选值包括

       • thread :Session 对象的生命周期与本地线程绑定
       •jta*: Session 对象的生命周期与 JTA 事务绑定
       •managed: Hibernate 委托程序来管理 Session 对象的生命周期
*   如果把 Hibernate 配置文件的 hibernate.current_session_context_class 属性值设为 thread, Hibernate 就会按照与本地线程绑定的方式来管理 Session
@Test
	public  void  testThread(){
//		   Session session=sf.openSession();
//		   Transaction tx=session.beginTransaction();
//		   Session s1=sf.openSession();
//		   Session s2=sf.openSession();
//		   System.out.println(s1==s2);//s1和s2表示不同的对象
//		   tx.commit();
//		   session.close();
//		
		   //测试使用本地线程绑定
		   //获取当前的session
		   Session s1=sf.getCurrentSession();
		   Session s2=sf.getCurrentSession();
		   System.out.println(s1==s2);//s1和s2表示相同的对象
		  
	}	
*   配置了   <property name="hibernate.current_session_context_class">thread</property>   s1==s2  没有配置   会报错 org.hibernate.HibernateException: No CurrentSessionContext configured!
*   不是调用sessionFactory.openSession().而是调用sessionFactory. getCurrentSession().获取session对象.从当前的线程提取session
*   当前线程如果存在session对象,取出直接使用       当前线程如果不存在session对象,获取一个新的session对象和当前的线程绑定
3.理解缓存定义
*   缓存(Cache): 计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存
4.理解二级缓存定义
*   Hibernate中提供了两个级别的缓存
           第一级别的缓存是 Session 级别的缓存,它是属于事务范围的缓存。这一级别的缓存由 hibernate 管理的,一般情况下无需进行干预
           第二级别的缓存是 SessionFactory 级别的缓存,它是属于进程范围的缓存   sessionFactory的缓存输入进程级别的,是所有客户端都可以访问的,注意并发问题
*   SessionFactory 的缓存可以分为两类
           内置缓存: Hibernate 自带的, 不可卸载. 通常在 Hibernate 的初始化阶段, Hibernate 会把连接数据库的信息和映射元数据和预定义的 SQL 语句放到 SessionFactory 的缓存中, 映射元数据是映射文件中数据的复制, 而预定义 SQL 语句时 Hibernate 根据映射元数据推到出来的. 该内置缓存是只读的   
            外置缓存(二级缓存): 一个可配置的缓存插件. 在默认情况下, SessionFactory 不会启用这个缓存插件. 外置缓存中的数据是数据库数据的复制, 外置缓存的物理介质可以是内存或硬盘
5.缓存中存放的数据
*   适合放入二级缓存中的数据
          很少被修改
          不是很重要的数据, 允许出现偶尔的并发问题
*   不适合放入二级缓存中的数据
           经常被修改
           财务数据, 绝对不允许出现并发问题
           与其他应用数据共享的数据
6.缓存提供的供应商
*   Hibernate 的二级缓存是进程或集群范围内的缓存, 缓存中存放的是对象的散装数据
7.配置进程范围内的二级缓存(配置ehcache缓存)
*   1 拷贝ehcache-1.5.0.jar到当前工程的lib目录下
    2 开启二级缓存
      <property name="hibernate.cache.use_second_level_cache">true</property>
     3 要指定缓存的供应商
      <property name="hibernate.cache.provider_class">
              org.hibernate.cache.EhCacheProvider</property>
      4 指定使用二级缓存的类
            * 方法一 在使用类的*.hbm.xml配置
               选择需要使用二级缓存的持久化类, 设置它的二级缓存的并发访问策略, <class> 元素的 cache 子元素表明 Hibernate 会缓存对象的简单属性, 但不会缓存集合属性, 若希望缓存集合属性中的元素, 必须在 <set> 元素中加入 <cache> 子元素
                * 方法二  在hibernate.cfg.xml文件中配置(建议)
      <!-- 指定使用二级缓存的类 放在maping下面 -->
      <!-- 配置类级别的二级缓存 -->
      <class-cache class="cn.itcast.c3p0.Customer" usage="read-write"/>
      <class-cache class="cn.itcast.c3p0.Order" usage="read-write"/>

 <!-- 配置集合级别的二级缓存 -->
 <collection-cache  collection="cn.itcast.c3p0.Customer.orders" 
                                   usage="read-write"/>
      5  配置ehcache默认的配置文件ehcache.xml(名字固定)(放在类路径下)
8.配置进程范围内的二级缓存(配置ehcache缓存)
*   没有在src下面配置ehcache.xml就使用架包下的ehcache-failsafe.xml
<diskStore path="java.io.tmpdir"/>
*   默认的缓存路径   变量Temp的值
    
<defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
            maxElementsOnDisk="10000000"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            />
*   <diskStore>:指定一个目录, 当 EHCache 把数据写到硬盘上时, 将把数据写到这个文件目录下.  默认是C:\WINDOWS\Temp 
    <defaultCache>: 设置缓存的默认数据过期策略 
    <cache> 设定具体的命名缓存的数据过期策略
    每个命名缓存代表一个缓存区域,每个缓存区域有各自的数据过期策略。命名缓存机制使得用户能够在每个类以及类的每个集合的粒度上设置数据过期策略。 
*   cache元素的属性   
    name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字 
    maxElementsInMemory :设置基于内存的缓存中可存放的对象最大数目 
    eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false 
    timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。 
    timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。 如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值 
    overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中 
    diskPersistent 当jvm结束时是否持久化对象 true false 默认是false
    diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
9.测试二级缓存和散列数据
@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
		   tx.commit();
		   session.close();
/*************************************************************************************************/
	}
*   后面两次查询得到的Customer对象不是同一个对象    cn.itcast.cache.Customer@4d28c7     c2  cn.itcast.cache.Customer@2d0483
     所以二级缓存存储的是对象的散列数据   每次从二级缓存拿到散列数据都新建一个对象,把散列数据对对象赋值
*    需要引入两个jar包
     在srping下能找到
     ..\lib\concurrent\backport-util-concurrent.jar
     ..\lib\commons-logging.jar
10.测试一级缓存更新数据会同步到二级缓存
@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();//
/*************************************************************************************************/
	}
*   执行结果为
Hibernate: select customer0_.id as id0_0_, customer0_.name as name0_0_, customer0_.age as age0_0_ from customers customer0_ where customer0_.id=?
41
Hibernate: update customers set name=?, age=? where id=?
45
    所以一级缓存更新数据会同步到二级缓存
11.测试\集合级别的二级缓存
@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();//
/*************************************************************************************************/
	}
*   Order对象没有设置类级别的缓存,第二次查询时会有对应客户的10条查询订单sql语句
*   类级别的缓存区域:(对象的实体用于在这里)
          * 放置的查询的对象(id=1的Customer对象)
*   集合级别的缓存(存放的永远是查询条件,不能存放对象的实体,二实体永远存放到类级别的缓存中)
          * 放置的查询的条件,真正的实体还是在类级别的缓存区域中
*      现在没有配置
         <class-cache class="cn.itcast.cache.Order" usage="read-write"/>
  
       下面的配置存在
         <class-cache class="cn.itcast.cache.Customer" usage="read-write"/>
        <collection-cache collection="cn.itcast.cache.Customer.orderes" usage="read-write"/>
      
        表示二级缓存中不能放置Order对象,能放置Customer对象和Customer对象关联的订单集合

        类级别的缓存
         *  id=1的Customer对象

         集合级别的缓存
         *   存放的是订单id[[1-10]

         * 从二级缓存的类级别的缓存中获取Customer对象,不产生select语句
         * 客户关联的订单集合
         * 从集合级别的缓存获取查询条件订单id[[1-10]
         * 以订单id为条件到二级缓的类级别的缓存中去查找Order对象
                 * 如果二级缓的类级别的缓存中存在Order对象,不会产生select语句
                 * 如果二级缓的类级别的缓存中不存在Order对象,会产生select语句
                         select * from orders where id=1
                         select * from orders where id=10
12.时间戳缓存区域
@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();//
/*************************************************************************************************/
	}
	
*   这种是直接执行更新语句,跟sesion的一级缓存无关,不管属性变了还是没变都会执行sql语句
*   Hibernate 提供了和查询相关的缓存区域:
    时间戳缓存区域: org.hibernate.cahce.UpdateTimestampCache
    时间戳缓存区域存放了对于查询结果相关的表进行插入, 更新或删除操作的时间戳.  Hibernate 通过时间戳缓存区域来判断被缓存的查询结果是否过期, 其运行过程如下:
    T1 时刻执行查询操作, 把查询结果存放在 QueryCache 区域, 记录该区域的时间戳为 T1
    T2 时刻对查询结果相关的表进行更新操作, Hibernate 把 T2 时刻存放在 UpdateTimestampCache 区域.
    T3 时刻执行查询结果前, 先比较 QueryCache 区域的时间戳和 UpdateTimestampCache 区域的时间戳, 若 T2 >T1, 那么就丢弃原先存放在 QueryCache 区域的查询结果, 重新到数据库中查询数据, 再把结果存放到 QueryCache 区域;     若 T2 < T1, 直接从 QueryCache 中获得查询结果
*   类级别,集合级别,查询级别都有一个时间区域,用于与时间戳的时间区域进行对比
13.查询缓存
*   对于经常使用的查询语句, 如果启用了查询缓存, 当第一次执行查询语句时, Hibernate 会把查询结果存放在查询缓存中. 以后再次执行该查询语句时, 只需从缓存中获得查询结果, 从而提高查询性能
*   查询缓存使用于如下场合:
          应用程序运行时经常使用查询语句
          很少对与查询语句检索到的数据进行插入, 删除和更新操作
*   使用查询缓存的步骤
     配置二级缓存, 因为查询缓存依赖于二级缓存
     在 hibernate 配置文件中启用查询缓存
          <property name="cache.use_query_cache">true</property>
     对于希望启用查询缓存的查询语句, 调用 Query 的 setCacheable(true) 方法
@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();//

	}
*   查询缓存 存放的永远是查询条件,不能存放对象的实体,而实体永远存放到类级别的缓存中

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值