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

    Hibernate 二级缓存

    二级缓存需要sessionFactory来管理,它是进初级的缓存,所有人都可以使用,它是共享的。

     当Hibernate根据ID访问数据对象的时候,首先从Session一级缓存中查;查不到,如果配置了二级缓存,那么从二级缓存中查;查不到,再查询数据库,把结果按照ID放入到缓存。

    二级缓存比较复杂,一般用第三方产品。

    hibernate提供了一个简单实现,用Hashtable做的,只能作为我们的测试使用,商用还是需要第三方产品。

    使用缓存,肯定是长时间不改变的数据,如果经常变化的数据放到缓存里就没有太大意义了。因为经常变化,还是需要经常到数据库里查询,那就没有必要用缓存了。

    hibernate做了一些优化,和一些第三方的缓存产品做了集成。

     在默认情况下,Hibernate会使用EHCache作为二级缓存组件。但是,可以通过设置 ibernate.cache.provider_class属性,指定其他的缓存策略,该缓存策略必须实现 org.hibernate.cache.CacheProvider接口。

           通过实现org.hibernate.cache.CacheProvider接口可以提供对不同二级缓存组件的支持。

    Hibernate内置支持的二级缓存组件如表

    组件

    Provider类

    类型

    集群

    查询缓存

    Hashtable

    org.hibernate.cache.HashtableCacheProvider

    内存

    不支持

    支持

    EHCache

    org.hibernate.cache.EhCacheProvider

    内存,硬盘

    最新支持

    支持

    OSCache

    org.hibernate.cache.OSCacheProvider

    内存,硬盘

    不支持

    支持

    SwarmCache

    org.hibernate.cache.SwarmCacheProvider

    集群

    支持

    不支持

    JBoss TreeCache

    org.hibernate.cache.TreeCacheProvider

    集群

    支持

    支持

    这里测试以EHCache继承实例

    EHCache的jar文件在hibernate的lib里,我们还需要设置一系列的缓存使用策略,需要一个配置文件ehcache.xml来配置。

    配置ehcache.xml(这个文件放在类路径下)

    //默认配置,所有的类都遵循这个配置
    <defaultCache
            //缓存里可以放10000个对象
            maxElementsInMemory="10000"
            //过不过期,如果是true就是永远不过期
            eternal="false"
            //一个对象被访问后多长时间还没有访问就失效(120秒还没有再次访问就失效)
            timeToIdleSeconds="120"
            //对象存活时间(120秒),如果设置永不过期,这个就没有必要设了
            timeToLiveSeconds="120"
            //溢出的问题,如果设成true,缓存里超过10000个对象就保存到磁盘里
            overflowToDisk="true"
            />

    也可以对某个对象单独配置:

    <cache name="com.bjpowernode.hibernate.Student"
    
            maxElementsInMemory="100"
    
            eternal="false"
    
            timeToIdleSeconds="10000"
    
            timeToLiveSeconds="10000"
    
            overflowToDisk="true"
    
            />

    hibernate需要配置引入插件

    hibernate.cfg.xml配置文件配置缓存,让hibernate知道我们使用的是那个二级缓存。

    <!-- 配置缓存提供商 -->
    
    <property name="hibernate.cache.provider_class">
    org.hibernate.cache.EhCacheProvider
    </property>
    <!-- 启用二级缓存,这也是它的默认配置,启用二级缓存的配置可以不写的,因为默认就是true开启二级缓存。 --> <property name="hibernate.cache.use_second_level_cache"> true </property>

    hibernate需要制定哪些使用二级缓存

    必须还手动指定那些实体类的对象放到缓存里在hibernate.cfg.xml里:

    //在<sessionfactory>标签里,在<mapping>标签后配置
    
    <class-cache class="com.bjpowernode.hibernate.Student"
    
    usage="read-only"/>

    或者在实体类映射文件.hbm.xml里:

    //在<class>标签里,<id>标签前配置
    
    <cache usage="read-only"/>

    usage属性表示使用缓存的策略,一般优先使用read-only,表示如果这个数据放到缓存里了,则不允许修改,如果修改就会报错。这就要注意我们放入缓存的数据不允许修改。因为放缓存里的数据经常修改,也就没有必要放到缓存里。

    二级缓存策略

      只读缓存(read-only)

        表示如果这个数据放到缓存里了,则不允许修改,如果修改就会报错

        使用read-only策略效率好,因为不能改缓存。但是可能会出现脏数据的问题,这个问题解决方法只能依赖缓存的超时,比如上面我们设置了超时为120秒,120后就可以对缓存里对象进行修改,而在120秒之内访问这个对象可能会查询脏数据的问题,因为我们修改对象后数据库里改变了,而缓存却不能改变,这样造成数据不同步,也就是脏数据的问题。

      读/写缓存(read-write)

        当持久对象发生变化,缓存里就会跟着变化,数据库中也改变了。这种方式需要加解锁,效率要比第一种慢

    则是最常用的两种策略

      

       不严格的读/写缓存(nonstrict-read-write)

        如果程序偶尔需要更新数据(也就是说,出现两个事务同时更新同一个条目的现象很不常见),也不需要十分严格的事务隔离,可能适用nonstrict-read-write缓存。

      对于极少被修改,并且允许偶尔脏读的数据,可以采用这种并发访问策略。

       事务缓存(transactional)

         transactional缓存策略提供了对全事务的缓存,仅仅在受管理环境中使用。它提供了Repeatable Read事务隔离级别。对于经常被读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读和不可重复读这类的并发问题。

           在上面所介绍的隔离级别中,事务型并发访问策略的隔离级别最高,然后依次是读/写型和不严格读写型,只读型的隔离级别最低。事务的隔离级别越高,并发性能就越低。

     二级缓存实例

     当两个session分别调用load方法查询相同的数据,第二个session的load方法还是发了sql语句到数据库查询数据,这是因为一级缓存只在当前session中共享,也就是说一级缓存不能跨session访问。

     那么二级缓存呢?

    //开启二级缓存,二级缓存是进程级的缓存,可以共享
    
    //两个session分别调用load方法查询相同的实体对象
    
    try {
      session = HibernateUtils.getSession();
      session.beginTransaction();
      Student student = (Student)session.load(Student.class, 1);
      System.out.println("student.name=" + student.getName());
      session.getTransaction().commit();
    
    }catch(Exception e) {
      e.printStackTrace();
      session.getTransaction().rollback();
    
    }finally {
      HibernateUtils.closeSession(session);
    
    }
    
    try {
      session = HibernateUtils.getSession();
      session.beginTransaction();
      Student student = (Student)session.load(Student.class, 1);
      //不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据
      //二级缓存是进程级的缓存
      System.out.println("student.name=" + student.getName());
      session.getTransaction().commit();
    
    }catch(Exception e) {
      e.printStackTrace();
      session.getTransaction().rollback();
    
    }finally {
      HibernateUtils.closeSession(session);
    
    }

     如果开启了二级缓存,那么第二个session调用的load方法查询第一次查询的数据,是不会发出sql语句查询数据库的,而是去二级缓存中取数据。

    开启二级缓存后在次执行

    注意:二级缓存必须让sessionfactory管理,让sessionfactory来清除二级缓存。

    sessionFactory.evict(Student.class);//清除二级缓存中所有student对象,sessionFactory.evict(Student.class,1);//清除二级缓存中id为1的student对象。

       如果在第一个session调用load或get方法查询数据后,把二级缓存清除了,那么第二个session调用load或get方法查询相同的数据时,还是会发出sql语句查询数据库的,因为缓存里没有数据只能到数据库里查询。

    我们查询数据后会默认自动的放到二级和一级缓存里,如果我们想查询的数据不放到缓存里,也是可以的。也就是说我们可以控制一级缓存和二级缓存的交换。

    session.setCacheMode(CacheMode.IGNORE);禁止将一级缓存中的数据往二级缓存里放。

    还是用上面代码测试,在第一个session调用load方法前,执行session.setCacheMode(CacheMode.IGNORE);这样load方法查询的数据不会放到二级缓存里。那么第二个session执行load方法查询相同的数据,会发出sql语句到数据库中查询,因为二级缓存里没有数据,一级缓存因为不同的session不能共享,所以只能到数据库里查询。

    上面我们讲过大批量的数据添加时可能会出现溢出,解决办法是每当天就20个对象后就清理一次一级缓存。如果我们使用了二级缓存,光清理一级缓存是不够的,还要禁止一二级缓存交互,在save方法前调用session.setCacheMode(CacheMode.IGNORE)。

    二级缓存也不会存放普通属性的查询数据,这和一级缓存是一样的,只存放实体对象。session级的缓存对性能的提高没有太大的意义,因为生命周期太短了。

  • 相关阅读:
    ACM成长之路
    洛谷P1047 校门外的树
    洛谷P1046 陶陶摘苹果
    2017 ACM-ICPC 亚洲区(南宁赛区)网络赛 F题
    图论:POJ2186-Popular Cows (求强连通分量)
    DFS:POJ1562-Oil Deposits(求连通块个数)
    DFS:POJ3620-Avoid The Lakes(求最基本的联通块)
    map函数的应用:UVa156-Ananagrams
    set的应用:UVa10815-Andy's First Dictionary
    水题:UVa253-Cube painting
  • 原文地址:https://www.cnblogs.com/hwaggLee/p/4502236.html
Copyright © 2011-2022 走看看