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

    时间:2017-1-25 01:47

    ——缓存

    1、Hibernate提供的缓存有一级缓存、二级缓存,目的是为了减少对数据库的访问次数,提升程序执行效率。

    2、一级缓存
        基于Session的缓存,缓存内容只在当前Session有效,当Session关闭后,缓存内容失效。
        特点:
            作用范围小,缓存时间短,缓存效果不明显。

    3、二级缓存
        Hibernate提供了基于应用程序级别的缓存,可以跨多个Session来使用,不同的Session都可以访问缓存中的数据,这个缓存也叫二级缓存。

        Hibernate提供的二级缓存有默认的实现,并且是一种可拔插的缓存框架。
        如果用户想使用二级缓存,只需要在hibernate.cfg.xml中配置即可,如果不想使用,直接移除即可,不影响代码。
        如果用户觉得hibernate提供的缓存框架不好用,可以更换其他的缓存框架或自己实现缓存框架。

    ——使用二级缓存

        查看hibernate.properties配置文件,查看如何配置二级缓存。

        ##########################
        ### Second-level Cache ###
        ##########################

        #二级缓存默认关闭,将该属性设置为true,表示开启
        #hibernate.cache.use_second_level_cache false 


        #开启查询缓存
        #hibernate.cache.use_query_cache true

        #二级缓存框架的实现

        #hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
        #hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
        hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider
        #hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
        #hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
        #hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider


    使用步骤
        1)开启缓存
            在核心配置文件中配置:
                <property name="hibernate.cache.use_second_level_cache">true</property>
        2)指定使用哪一个缓存框架
            在核心配置文件中配置:
                <property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>

        3)指定哪些类需要加入二级缓存(通常只会放入常用类)
            在核心配置文件中配置:
                <class-cache usage="read-only" class="com.wyc.domain.Customer"/>

        4)测试二级缓存(至少需要两个Session)
            public void fun1(){
     
                Session session1 = HibernateUtils.openSession();
                Transaction tx1 = session1.beginTransaction();
     
                /*
                 * 查询一次
                 * 因为在配置文件中指定了要缓存这个类,所以会将该类对象缓存到二级缓存中
                 *  * <class-cache usage="read-only" class="com.wyc.vo.Customer" />
                 */
                Customer customer1 = (Customer) session1.get(Customer.class, 2);
     
                // 使用第二个Session
                Session session2 = HibernateUtils.openSession();
                Transaction tx2 = session2.beginTransaction();
     
                /*
                 * 查询第二次
                 * 如果没有配置二级缓存,会发送两条SQL语句
                 * 如果配置了二级缓存,当查询同一对象时,会发送一条SQL语句
                 * 当第二个session在一级缓存查不到数据之后,会到二级缓存查询,如果二级缓存依然查不到,才会访问数据库
                 */
                Customer customer2 = (Customer) session2.get(Customer.class, 2);
     
                tx2.commit();
                session2.close();
     
                tx1.commit();
                session1.close();
            }

    ——缓存策略

        1、<class-cache usage="read-only" />
            只读策略。
            放入二级缓存中的对象,只能读取。
            当其他Session从二级缓存中获取数据之后,不可以进行set操作。
        2、<class-cache usage="read-write" />
            读写策略。
            放入二级缓存中的对象,可以读写。
        3、<class-cache usage="nonstrict-read-write" />
            非严格的读写,效率相对高。
        4、<class-cache usage="transactional" />
            基于事务的策略。
            会对数据进行锁定。
            3.x版本不支持。

        5、也可以在类的映射文件中配置:<cache usage="read-only" />
            表示当前类会缓存到二级缓存中,状态为只读。
     

    ——类缓存区(测试二级缓存和散装数据)

    类缓存区缓存的是对象的散装数据。
    查询条件持有对象属性的引用。 

    public void fun3(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session1.beginTransaction();
     
     
        /*
         * 查询两个对象,测试是否同一个对象
         */
        Customer customer1 = (Customer) session.get(Customer.class, 1); // 发送SQL
        Customer customer2 = (Customer) session.get(Customer.class, 1); // 不发送SQL
        // 因为一级缓存保存的是对象的引用,所以是同一个对象
        System.out.println(customer1 == customer2);
        tx1.commit();
        session.close();

        session = HibernateUtils.openSession(); 
     
        /*
         * 使用第二个Session查询Customer cid = 1
         *
         * 因为对象已经被散装到二级缓存中,所以不会发送SQL,而是从二级缓存中组装数据
         * 
         * 散装数据:
         *  * 并不是将对象的引用保存到二级缓存中,而是将对象的数据分散开保存到二级缓存中,并维持映射关系
         *  * 例如:cid = 1, cname = 张三...
         */
        Customer customer3 = (Customer) session.get(Customer.class, 1);
        Customer customer4 = (Customer) session.get(Customer.class, 1);
        /*
         * 组装后的对象是同一个对象,结果为true
         */
        System.out.println(customer3 == customer4);
        /*
         * 此时二级缓存中的对象和一级缓存中的对象不是同一个对象,结果为false
         */
        System.out.println(customer1 == customer3);
     
     
     
        tx.commit();
        session.close();
     
    }

    ——集合缓存区

    集合缓存区需要依赖类缓存区。
    集合缓存区缓存的是集合中元素的id。

    如果配置了类缓存区,当查询到集合数据之后,会将集合元素的id保存到类缓存区,并建立集合元素id与集合元素数据的映射关系。然后再将集合元素的所有id缓存到集合缓存区。当再次查询集合时,会根据集合元素的id查询类缓存区,如果有数据,则取出。

    如果没有配置类缓存区,当查询到集合数据之后,会将集合元素的id保存到集合缓存区,此时类缓存区中没有集合元素的数据。当再次查询集合时,会到集合缓存区中获取所有集合元素的id,并根据元素的id发送SQL语句依次查询,有多少id,就发送多少SQL。

    1、如果想要缓存当前类对象中的某个集合属性,可以在配置文件中添加配置:
            <collection-cache usage="read-write" collection="com.wyc.vo.Customer.orders" />
            类名为:完整类名.集合属性的名称

            集合所对应的类也必须被缓存:
                 <collection-cache usage="read-write" collection="com.wyc.vo.Order" />

            也可以在类的映射文件中配置:
                <set> <cache usage="read-only" /> </set>
                表示当前类的集合属性会缓存到二级缓存中,状态为只读。

    2、示例代码:
            public void fun1(){
     
                Session session1 = HibernateUtils.openSession();
                Transaction tx1 = session1.beginTransaction();
     
                Customer customer1 = (Customer) session1.get(Customer.class, 2);
                /*
                 * 当使用了Customer对象中的Order集合属性时,因为Order类和orders属性设置了二级缓存,所以会缓存到二级缓存中
                 * 此时发出第一条SQL
                 */
                customer1.getOrders().size();
     
                // 使用第二个Session
                Session session2 = HibernateUtils.openSession();
                Transaction tx2 = session2.beginTransaction();
     
                Customer customer2 = (Customer) session2.get(Customer.class, 2);
                /*
                 * 因为集合被放入二级缓存中,所以不会再次查询
                 * 不会发送发送第二条SQL
                 */
                customer2.getOrders().size();
     
                tx2.commit();
                session2.close();
     
                tx1.commit();
                session1.close();
     
            }



    ——list()与iteratr()方法

    list()方法默认会将数据放入二级缓存,但是不会使用二级缓存中的数据,如果不使用二级缓存,list()方法效率要比iterate()方法效率高。
    如果使用了二级缓存,则iterate()方法要比list()方法效率高,因为iterate()方法会从二级缓存中获取数据。

     示例代码:
        1、list()方法会将查询结果保存到二级缓存,但不会使用二级缓存中的数据
            // 验证list()会将查询结果放入二级缓存
            List<Customer> list = session.createQuery("from Customer").list(); // 会发送一条SQL来查询全部记录
            System.out.println(session.get(Customer.class, 1)); // 没有发送SQL查询客户信息,所以数据是从二级缓存中获取的
     
            // list()没有使用二级缓存中的数据
            List<Customer> list2 = session.createQuery("from Customer").list(); // 会发送第二条SQL来查询全部记录
     
        2、iterate()方法会发送N+1条SQL进行查询,但是会使用二级缓存中的数据
            // 发送一条查询id的SQL
            Iterator<Customer> it = session.createQuery("from Customer").iterate();
            // 通过id查询客户的信息
            while(it.hasNext()){
                System.out.println(it.next());
            }
            tx.commit();
     
            session = HibernateUtils.openSession();
            // 发送一条查询id的SQL
            Iterator<Customer> it2 = session.createQuery("from Customer").iterate();
            // 不会发送SQL,而是直接使用二级缓存中的数据
            while(it2.hasNext()){
                System.out.println(it2.next());
            } 
     
    ——配置ehcache

    1、导入ehcachejar包
        依赖 backport-util-concurrent 和 commons-logging

    2、开启二级缓存
        <property name="hibernate.cache.use_second_level_cache">true</property>

    3、指定缓存框架
        <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
     
    4、在核心配置文件中指定使用二级缓存的类
        <class-cache usage="read-write" class="com.wyc.vo.Customer" />

    5、将ehcache的配置文件ehcache.xml放到src目录下

    6、配置信息:
        <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
     
            <diskStore path="c:/ehcache"/>
            <defaultCache
                    maxElementsInMemory="5"
                    eternal="false"
                    timeToIdleSeconds="120"
                    timeToLiveSeconds="120"
                    overflowToDisk="true"
                    maxElementsOnDisk="10000000"
                    diskPersistent="false"
                    diskExpiryThreadIntervalSeconds="120"
                    memoryStoreEvictionPolicy="LRU"
                />
        </ehcache>

    7、配置属性介绍
        diskStore:文件保存路径

        name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字。
     
        maxElementsInMemory:设置基于内存的缓存中可存放的对象最大数目。

        eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds和timeToLiveSeconds属性,默认值是false。

        timeToIdleSeconds:设置对象空闲最长时间,以秒为单位,超过这个时间,对象会过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。该属性值必须大于或等于timeToIdleSeconds 属性值。

        overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中。

        diskPersistent:当jvm结束时是否持久化对象,取值为true、false,默认是false。

        diskExpiryThreadIntervalSeconds:指定专门用于清除过期对象的监听线程的轮询时间。

        memoryStoreEvictionPolicy:当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)。
     
    ——更新时间戳区

    Hibernate通过时间戳缓存区来判断被缓存的查询结果是否过期。

    示例代码:
        // 从数据库中查询一条数据,并将对象数据散装保存到类缓存区中,并生成一个时间戳T1
        Customer customer = (Customer) session.get(Customer.class, 1);
        System.out.println(customer);
        /*
         * 使用HQL进行更新操作,可以避开二级缓存
         * 当执行了更新操作后,在时间戳缓存区生成一个时间戳T2
         */
        session.createQuery("update Customer set cname = '赵六' where cid = 1").executeUpdate();
     
        tx.commit();
        session = HibernateUtils.openSession();
        // 当再次查询时会比较T1和T2,如果T2 > T1,则表示数据已被更改,会再发送一条SQL查询数据库
        customer = (Customer) session.get(Customer.class, 1);
        System.out.println(customer);



    ——查询缓存

    查询缓存依赖二级缓存。
    二级缓存:主要针对类、对象的缓存。
    查询缓存:可以缓存类和对象,也可以缓存属性,例如:select c.cname from Customer;

    使用setCacheable()方法设置使用查询缓存。

    在核心配置文件中的配置:
        <!-- property一定要放到<class-cache><mapping>之前 -->
        <property name="hibernate.cache.use_query_cache" >true</property>

    示例代码:
        public void fun(){
            Session session = HibernateUtils.openSession();
            Transaction tx = session.beginTransaction();
     
     
            // 二级缓存无法缓存属性
            Query query = session.createQuery("select c.cname from Customer c");
            // 使用查询缓存
            query.setCacheable(true);
            // 发送第一条SQL语句
            query.list();
     
            tx.commit();
     
            session = HibernateUtils.openSession();
            tx = session.beginTransaction();
     
            query = session.createQuery("select c.cname from Customer c");
            query.setCacheable(true);
            // 不发送SQL,直接使用查询缓存中的数据
            query.list();
     
            tx.commit();
            session.close();
        }
  • 相关阅读:
    mysql lock
    yii2引入js和css
    Yii 2.x 和1.x区别以及yii2.0安装
    Curl https 访问
    boost::any 用法
    boost单元测试框架
    shared_ptr的线程安全
    nginx php fastcgi安装
    ip相关
    Design Pattern Explained 读书笔记二——设计模式序言
  • 原文地址:https://www.cnblogs.com/wwwwyc/p/6375470.html
Copyright © 2011-2022 走看看