美文网首页
Hibernate二级缓存

Hibernate二级缓存

作者: BlueSkyBlue | 来源:发表于2020-03-25 17:18 被阅读0次

    缓存(Cache):计算机领域的通用概念。它介于应用程序和永久性数据存储源(如硬盘上的数据库或文件)之间。其作用是减少应用程序直接读写永久性数据存储源的频率,从而提高应用程序的运行性能。缓存中的数据是数据存储源中的数据的拷贝。缓存的物理介质通常是内存。

    Hibernate中提供了两个级别的缓存:

    • 第一级别的缓存是Session级别的缓存,它属于事务范围内的缓存。这一级别的缓存由Hibernate进行管理。
    • 第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围内的缓存。

    SessionFactory级别的缓存

    SessionFactory的缓存分为两类:

    • 内置缓存:Hibernate自带的,不可卸载。通常在Hibernate初始化阶段,Hibernate会把映射元数据和预定义的SQL语句放到SessionFactory的缓存中,映射元数据是映射文件(.hbm.xml)中的数据的复制,该内置缓存是只读的。
    • 外置缓存(二级缓存):一个可配置的缓存插件,在默认情况下,SessionFactory不会启动这个缓存插件,外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或硬盘。

    适合放入二级缓存的数据:

    • 很少被修改
    • 不是很重要的数据,允许出现偶尔的并发问题。

    不适合放入二级缓存中的数据

    • 经常被修改
    • 财务数据,决不允许出现并发问题。
    • 与其它应用程序共享的数据。
    二级缓存的架构

    二级缓存的并发访问策略

    两个并发的事务同时访问持久层的缓存的相同的数据时,也有可能出现各种各样的并发问题。

    二级缓存可以设定为以下四种类型的并发访问策略,每一种访问策略对应一种事务的隔离级别。

    • 非严格读写(Nonstrict-read-write):不保证缓存与数据库中的数据的一致性,提供Read-uncommited级别的数据库隔离级别。对于极少修改而且允许脏读的数据,可以采用这种策略。
    • 读写型(Read-write):提供Read-commited数据隔离级别。对于经常读但很少修改的数据,可以使用这种隔离类型,因为它可以防止脏读。
    • 事务性(Transactional):仅在受管理环境下适用,它提供了Repeatable-read事务隔离级别。对于经常读但很少修改的数据,可以采用这种隔离类型,因为它可以防止脏读和不可重复读。
    • 只读型(Read-only):提供Serializable数据库隔离类型,对于从来不会被修改的数据,可以采用这种访问策略。

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

    • 选择合适范围的缓存插件:EHCache(jar包和配置),并编译器配置文件。
    • 在Hibernate的配置文件中启用二级缓存,并指定EHCache对应的缓存适配器。
    //启动Hibernate的二级缓存
    <property name="cache.use_second_level_cache">true</property>
    //配置Hibernate二级缓存使用的产品
    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>
    //配置对那些类使用Hibernate的二级缓存
    <class-cache usage="read-write" class="com.hibernate.unionsubclass.Student"></class-cache>
    

    也可以在.hbm.xml类中配置二级缓存。

    • 选择需要使用二级缓存的持久化类,设置它的二级缓存的并发访问策略
      • <class>元素的cache子元素表明Hibernate会缓存对象的简单属性,但不会缓存集合属性,如果希望缓存集合属性,必须在<set>元素中加入<cache>子元素。
      • 在hibernate配置文件中通过<class-cache/>结点配置使用缓存。

    设置集合缓存

    我们看下面的代码

    SessionFactory sessionFactory = null;
    Configuration cfg = new Configuration().configure();
    sessionFactory = cfg.buildSessionFactory();
    Session session = sessionFactory.openSession();
    Transaction transaction = session.beginTransaction();
    
    Category category = session.get(Category.class, 42);
    System.out.println(category.getItems().size());
    
    transaction.commit();
    session.close();
    
    session = sessionFactory.openSession();
    transaction = session.beginTransaction();
    Category category2 = session.get(Category.class, 42);
    System.out.println(category2.getItems().size());
    
    transaction.commit();
    session.close();
    sessionFactory.close();
    

    运行后发现生成了四条SQL语句。此时我们给Category类添加入二级缓存。

    <class-cache usage="read-write" class="com.hibernate.n2n.Category"></class-cache>
    

    发现少了一条SQL语句,但在获取items的时候还是两条SQL语句,此时我们需要将Item类添加到二级缓存中。

    <collection-cache usage="read-write" collection="com.hibernate.n2n.Category.items"></collection-cache>
    

    此时发现又是四条SQL语句。因为我们仅仅缓存了items集合类,没有缓存其中的类型。

    <class-cache usage="read-write" class="com.hibernate.n2n.Item"></class-cache>
    

    这个时候控制台只输出了两条SQL语句。

    集合缓存也可以在.hbm.xml中配置

    <set name="items" table="category_item">
        <cache usage="read-write"/>
        <key>
            <column name="Category_Id"/>
        </key>
        <many-to-many class="Item" column="Item_Id"/>
    </set>
    

    注意,还需要在集合包含的类中配置缓存。

    ehcache.xml文件

    <diskStore path="java.io.tmpdir"/>
    

    diskStore表示磁盘的存储路径。当有大量的数据的时候我们需要设置临界值,如果低于这个临界值,则将数据存入到内存中。高于这个数据则放到磁盘上。该路径表示磁盘的位置。

    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
    />
    

    默认的存储策略。

    <cache name="sampleCache1"
       maxElementsInMemory="10000"
       eternal="false"
       timeToIdleSeconds="300"
       timeToLiveSeconds="600"
       overflowToDisk="true"
    />
    

    设置具体的命名缓存数据的策略,每个命名缓存代表一个缓存区域。

    缓存区域(region):一个具有名称的缓存块,可以给每个缓存块设置不同的缓存策略。如果没有设置任何缓存区域,则所有的缓存对象将使用默认的缓存策略。

    配置项解释:

    name:设置缓存的名字,它的取值为类的全限定名或类的集合名字。
    maxElementInMemory:在内存中最多可以存储对象的数量。
    eternal:设置对象在缓存中是否为永久的,true表示永不过期。此时将忽略timeToIdleSeconds和timeToLiveSecond,默认值为false。
    timeToIdleSeconds:设置对象空闲的最长时间,单位是秒。如果超过这个时间,对象将从缓冲中清除。
    timeToLiveSecond:设置对象的最长生存时间,超过这个时间,对象过期。如果值为0表示对象可以永久的存在缓存中。
    overflowToDisk:设置内存中的缓存中的对象的数量达到上限后,是否把溢出的对象写到基于硬盘的缓存中。

    查询缓存

    当执行以下的代码

    Query query = session.createQuery("from Category ");
    List<Category>categories = query.list();
    System.out.println(categories.size());
    
    categories = query.list();
    System.out.println(categories.size());
    

    我们会发现执行了两条SQL。说明缓存没有起作用。

    此时我们需要设置查询缓存。

    默认情况下设置的缓存对HQL和QBC查询是无效的。通过以下方式配置
    在Hibernate配置文件中声明开启查询缓存

    <property name="cache.use_query_cache">true</property>
    

    调用Query或Criteria的setCacheable方法,设置为true。

    query.setCacheable(true);
    

    注意:查询缓存依赖二级缓存。

    时间戳缓存

    时间戳缓存区域存放了对于查询结果相关的表进行插入,更新,删除操作的时间戳。Hibernate通过时间戳缓存区域判断缓存的查询结果是否过期,其运行过程如下:

    • T1时刻执行查询操作,把查询结果放在QueryCache区域,记录该区域的时间戳为T1。
    • T2时刻对查询结果相关的表进行更新操作,Hibernate把T2时刻放在UpdateTimestampCache区域。
    • T3时刻在执行查询结果前,先比较QueryCache区域的时间戳和UpdateTimestampCache区域的时间戳,若T2>T1,那么丢弃原先存放在QueryCache区域的查询结果,重新懂啊数据库中查询数据,再把结果存放到QueryCache区域;若T2<T1,直接从QueryCache区域取出查询结果。

    Query接口的iterator方法

    当遍历访问结果集的时候,该方法先到Session缓存及二级缓存中查看是否存在特定OID的对象,如果存在,就直接返回该对象。如果不存在该对象,则通过相应的SQL select语句到数据库中加载特定的实体类对象。

    大多数情况下应使用list方法执行查询操作。iterator方法仅在满足以下条件的场景中适合使用,可以稍微提高查询的性能:

    • 要查询的数据表中含有大量的字段
    • 启用了二级缓存,且二级缓存可能已经包含了待查询的对象。

    相关文章

      网友评论

          本文标题:Hibernate二级缓存

          本文链接:https://www.haomeiwen.com/subject/reeayhtx.html