zoukankan      html  css  js  c++  java
  • Hibernate学习笔记--------3.缓存

    一、一级缓存

    一级缓存又称为“Session缓存”或者“会话级缓存”,通过Session从数据库查询实体时,会把实体在内存中存储起来,下一次查询同一实体时不再再次执行sql语句查询数据库,而是从内存中获取。一级缓存的生命周期和Session相同。一级缓存是无法取消的。

    1.一级缓存中的数据可适用范围在当前会话之类,例如用同一个session查询两次user表和同两个session查询俩次user表,查询数据库的次数是不一样的。首先,像这样查询两次,可以看见打印的sql语句只有一次,因为第二次是直接从当前session缓存中取的。

    但是把注释去掉,就会发现,sql打印了两次,因为他执行了两次数据库的查询操作。

        /**
         * 一级缓存
         */
        @Test
        public void cacheTest() {
            Tb_User u = session.get(Tb_User.class, "1");
            System.out.println(u.getName());
            //session = sessionFactory.openSession();
            u = session.get(Tb_User.class, "1");
            System.out.println(u.getName());
        }

    2.evict 、clear

    evict方法:清除一级缓存中的指定对象;

    clear方法:清除一级缓存的所有对象。

    这段代码打印的sql语句打印了两次,结果很明显。

      @Test
        public void cacheTest() {
            Tb_User u = session.get(Tb_User.class, "1");
            
            //清除一级缓存中的指定对象
            //session.evict(u);
            
            //清楚一级缓存中的所有对象
            session.clear();
            
            u = session.get(Tb_User.class, "1");
        }

    3.query.list | query.getResultList

    这两个方法是一样的,但是现在我看提示说query.list是废弃的。他们都不使用缓存,执行下面这段代码,可以很明确的看见打印了两次sql,这两个方法都是不使用缓存的,但是会把查询出来的数据存入缓存中。

        /**
         * 一级缓存
         */
        @Test
        public void cacheTest() {
            
            Query query = session.createQuery("From Tb_User");
            List<Tb_User> list = query.getResultList();
            for(Tb_User u : list){
                System.out.println(u.getName());
            }
            
            list = query.getResultList();
            for(Tb_User u : list){
                System.out.println(u.getName());
            }
            
        }

    4.query.iterate()

    若用迭代器Iterator,从打印的sql来看,他只查询了id,然后还是打印了Name,因为query.iterate()用到了缓存,他也会把查询出来的数据存入缓存中。

        /**
         * 一级缓存
         */
        @Test
        public void cacheTest() {
            
            Query query = session.createQuery("From Tb_User");
            List<Tb_User> list = query.getResultList();
            for(Tb_User u : list){
                System.out.println(u.getName());
            }
            
            Iterator it = query.iterate();
            while(it.hasNext()){
                Tb_User u = (Tb_User)it.next();
                System.out.println(u.getName());
            }
            
        }

    query.iterate()怎么使用缓存?

    把代码query.getResultList()相关的几行注释掉,执行后,可以看见打印的sql语句,先查询了id,然后根据id用where去数据挨着查询出来,我的数据库里有3条数据,所有一共查询了4次数据库。

    query.iterator会先去查询数据库中的id,然后根据id,先在缓存中查找是否有相应的数据,有则直接用,没有则从数据库中找。

    解释下前面的代码结果:

    若query.iterator遍历方法之前执行了list方法,list方法查询出来的数据被缓存了,但list方法不使用缓存,因此再次执行list时会重新查询数据库,而iterator方法只从数据库查询id,这时每个id在内存中都有对应的值,所以Name属性是从内存中取出来的。

    若query.iterator遍历方法之前没有执行了list方法,那么此时数据并没有缓存在内存中,那么iterator方法依然会先查询id,在遍历时,发现内存中根据id找不到该数据,于是就发送sql到数据库中找,此时Name属性就是真的从数据库中现场查找出来的了。

    若query.iterator遍历方法之前执行了query.iterator遍历方法,那么结果和第一种情况类似,他会先依次在数据库中查找,缓存到内存,然后第二次执行时,只查找了id值,其余的在内存中找到。

        @Test
        public void cacheTest() {
            
            Query query = session.createQuery("From Tb_User");
    //        List<Tb_User> list = query.getResultList();
    //        for(Tb_User u : list){
    //            System.out.println(u.getName());
    //        }
            
            Iterator it = query.iterate();
            while(it.hasNext()){
                Tb_User u = (Tb_User)it.next();
                System.out.println(u.getName());
            }
            
    //        it = query.iterate();
    //        while(it.hasNext()){
    //            Tb_User u = (Tb_User)it.next();
    //            System.out.println(u.getName());
    //        }
        }

    二、二级缓存

     hibernate的二级缓存又称为“全局缓存”,“应用级缓存”,二级缓存中的数据可适用方位是当前应用的所有会话,不随session关闭而关闭,他是可插拔式的缓存。二级缓存需要其他的jar包,自己去配置。配置步骤:

    ①.添加二级缓存对应的jar包 

    jar包在 hibernate-release-5.2.1.Finalliboptionalehcache 中有三个ehcache-2.10.1.jar,hibernate-ehcache-5.2.1.Final.jar,slf4j-api-1.7.7.jar都需要添加进去

    ②.在hibernate的配置文件中添加Provider类的描述,即hibernate.cfg.xml中 添加属性

            <!-- 开启二级缓存 -->
            <property name="cache.use_second_level_cache">true</property>
            <!-- 开启查询缓存 -->
            <property name="hibernate.cache.use_query_cache">true</property>
            <!-- 配置RegionFactory为Ehcache的RegionFactory -->
            <property name="cache.region.factory_class">org.hibernate.cache.EhCacheRegionFactory</property>

    ③.添加二级缓存的属性配置文件

    在 hibernate-release-5.2.1.Finalprojecthibernate-ehcachesrc est esources 目录下,可以找到ehcache.xml的配置文件,复制粘贴到hibernate.cfg.xml统一目录下,打开xml文件看到这么一段,注释写得很清楚,每个属性干嘛的。这个配置是默认的缓存策略。

        <!--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 beforeQuery 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 beforeQuery 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"
            />

    ④.在需要被缓存的表所对应的映射文件中添加<cache />标签,作为class的子节点。usage属性表示的是事务模式,还有include属性,设置是否加载延迟加载的属性,region属性是Ehcache配置中,可以为表单独增加缓存策略,否则全部都是默认策略。

    <cache usage="read-only" region="Tb_User"/>

    若需要单独配置缓存策略,需要在ehcache.xml中加上一段,表示Tb_User使用当前的缓存策略,不用默认的。(不是必须配置的)

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

    此时基本的配置就完成了,依然用第一个例子做测试

        /**
         * 一级缓存
         */
        @Test
        public void cacheTest() {
            Tb_User u = session.get(Tb_User.class, "1");
            System.out.println(u.getName());
            //session = sessionFactory.openSession();
            u = session.get(Tb_User.class, "1");
            System.out.println(u.getName());
        }

    此时,无论是否打开一个新的session,都只会打印一条sql语句,若把cache.use_second_level_cache 属性值设为false,那么就可以关闭二级缓存。

    三、一级缓存和二级缓存比较

    补充:在映射文件中的cache标签的usage有四种属性

    read-only: 对于永远不会被修改的数据可以采用这种并发访问策略,它的并发性能是最高的。但必须保证数据不会被修改,否则就会出错,使用场景可以是OA系统中比较常用的字典表/枚举表
    nonstrict-read-write: 非严格读写不能保证缓存与数据库中数据的一致性,如果存在两个事务并发地访问缓存数据的可能,则应该为该数据配置一个很短的过期时间,以减少读脏数据的可能。对于极少被修改,并且可以容忍偶尔脏读的数据可以采用这种并发策略。
    read-write: 对于经常被读但很少修改的数据可以采用这种策略,它可以防止读脏数据。
    transactional:它可以防止脏读和不可重复读这类的并发问题。

    transactional策略是事务隔离级别最高,read-only的隔离级别最低。事务隔离级别越高,并发性能就越低。

  • 相关阅读:
    使用内部单向链表实现的一个简单堆栈
    通过反射调用内部类的隐藏方法
    动态代理生成空对象
    通过使用java.lang.reflect.Proxy实现动态代理
    简单代理模式
    暗色CSS,适用与Stylish, IE, FF, OPERA等.
    CWnd派生的控件处理MouseMove, MouseHover, MouseLeave
    _tcscpy_s的size应至少为src的长度+1(要把计算在内)
    用INET(CHttpFile)下载有重定向链接时获取最终URL的方法.
    GetDlgItem以及其他获得CWnd相关的函数要注意。。
  • 原文地址:https://www.cnblogs.com/lzq1065763582/p/5700991.html
Copyright © 2011-2022 走看看