• Hibernate缓存研究


    1. 什么是缓存?

    数据库的缓存指的是应用程序和物理数据源之间的数据。即把物理数据源的数据复制到缓存。有了缓存,可以降低应用程序对物理数据源的访问频率,从而提高效率。缓存的介质一般是内存,也可以是硬盘。

    Hibernate的缓存有三种类型:一级缓存、二级缓存和查询缓存。

    2. 一级缓存

    一级缓存即Session缓存,由Session自动进行管理,不需要程序进行干预。一级缓存根据对象的ID进行加载和缓存。如下面的代码:

        @Override
        public void testCache() {
            // TODO Auto-generated method stub
            Session session = sessionFactory.openSession();
            Transaction tx = session.beginTransaction();  
            Course c = (Course) session.get(Course.class, 1);
            System.out.println("Name:" + c.getName());
            c = (Course) session.get(Course.class, 1);
            System.out.println("Name:" + c.getName());
            tx.commit();
            session.close();
    
        }

    运行结果:

    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Name:计算机原理
    Name:计算机原理

    第1次查询时生成了SQL语句,并将查询出来的对象放在一级缓存里面,第2次查询时,在一级缓存里面直接找到了这个对象,就不需要再次生成SQL语句了。

    再看一个例子:

        @Override
        public void testCache() {
            // TODO Auto-generated method stub
            Session session = sessionFactory.openSession();
            Transaction tx = session.beginTransaction();  
            Course c = (Course) session.get(Course.class, 1);
            System.out.println("Name:" + c.getName());
            tx.commit();
            session.close();
            
            session = sessionFactory.openSession();
            tx = session.beginTransaction(); 
            c = (Course) session.get(Course.class, 1);
            System.out.println("Name:" + c.getName());
            tx.commit();
            session.close();
        }

    由于一级缓存是Session级别的缓存,所以Session关闭以后,一级缓存也就不存在了,第2次查询也要生成SQL语句:

    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Name:计算机原理
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Name:计算机原理

     3. 二级缓存

    二级缓存即SessionFactory缓存,和一级缓存类似,也是根据对象的ID进行加载和缓存,区别就在于一级缓存只在Session内有效,而二级缓存在SessionFactory内有效。在访问某个ID的对象时,先到一级缓存里面去找,如果没有找到就到二级缓存里面去找。二级缓存包括EHCache,OSCache,SwarmCache和JBossCache等。这里以EHCache作为例子。

    二级缓存需要程序进行管理。首先,配置Maven下载相关的Jar,在pom文件里面添加:

            <dependency> 
                <groupId>org.hibernate</groupId> 
                <artifactId>hibernate-ehcache</artifactId> 
                <version>4.1.0.Final</version> 
            </dependency>
    
            <dependency>  
                <groupId>net.sf.ehcache</groupId>  
                <artifactId>ehcache</artifactId>  
                <version>2.8.3</version>  
            </dependency>

    创建EHCache配置文件ehcache.xml:

    <ehcache>
        <diskStore path="E:EclipseMyWorkspaceCache"/>
        <defaultCache
            maxElementsInMemory="10000"
            eternal="true"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true"
        />
        <cache name="com.hzhi.course.entity.Course"
            maxElementsInMemory="10000"
            eternal="true"
            timeToIdleSeconds="300"
            timeToLiveSeconds="600"
            overflowToDisk="true"
        />
    </ehcache>

    defaultCache是默认的设置,下面一个cache指明了对哪一个类进行二级缓存。里面设置了最大缓存的对象数量,是否永久有效、最大空闲秒数、最大生存秒数、内存满时是否写到硬盘,写到硬盘的路径等等。

    修改需要缓存的类的hbm文件:

        <class name="com.hzhi.course.entity.Course" table="clas">
            <cache usage="read-only"/>
                    ......
        </class>

    usage设置了并发访问策略,一般设置成read-only。

    修改applicationContext.xml中的SessionFactory的配置,增加二级缓存的一些属性:

        <!-- SessionFactory -->
         <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
            <property name="dataSource" >
                <ref local="dataSource"/>
            </property>
            <!-- 配置Hibernate的属性 -->
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.show_sql">true</prop>
                    <prop key="hibernate.format_sql">true</prop>
                    <prop key="hibernate.connection.isolation">8</prop>
                    <!-- 二级缓存 -->
                    <prop key="hibernate.cache.use_second_level_cache">false</prop>
                    <prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>                
                    <prop key="hibernate.cache.provider_configuration_file_resource_path">WEB-INF/ehcache.xml</prop>                
                </props>
            </property>
         ......
    </bean>

    运行下面的例子:

        @Override
        public void testCache() {
            // TODO Auto-generated method stub
            Session session = sessionFactory.openSession();
            Transaction tx = session.beginTransaction();  
            Course c = (Course) session.get(Course.class, 1);
            System.out.println("Name:" + c.getName());
            tx.commit();
            session.close();
            
            session = sessionFactory.openSession();
            tx = session.beginTransaction(); 
            c = (Course) session.get(Course.class, 1);
            System.out.println("Name:" + c.getName());
            tx.commit();
            session.close();
        }

    结果:

    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Name:计算机原理
    Name:计算机原理

    虽然关闭了Session,但是二级缓存仍然存在,所以只生成了一次SQL语句。

    下面的例子:

        @Override
        public void testCache() {
            // TODO Auto-generated method stub
            Session session = sessionFactory.openSession();
            Transaction tx = session.beginTransaction();  
            Query query = session.createQuery("from Course");  
            Iterator iter = query.iterate();  
            while(iter.hasNext()){  
                   System.out.println(((Course)iter.next()).getName());  
            }
            tx.commit();
            session.close();
            
            session = sessionFactory.openSession();
            tx = session.beginTransaction(); 
            query = session.createQuery("from Course");  
            iter = query.iterate();  
            while(iter.hasNext()){  
                   System.out.println(((Course)iter.next()).getName());  
            }
            tx.commit();
            session.close();
        }

    结果:

    Hibernate: 
        select
            course0_.ID as col_0_0_ 
        from
            clas course0_
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    计算机原理
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    计算机网络
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    数据库原理
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    C语言
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    大学英语A
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Java
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Linux
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    高等数学
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    语文
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    大学物理
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    软件工程
    Hibernate: 
        select
            course0_.ID as col_0_0_ 
        from
            clas course0_
    计算机原理
    计算机网络
    数据库原理
    C语言
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程

    当使用Query的list()方法时,只生成一次SQL语句查询出所有的对象,使用iterate()方法时,会先得到所有对象的ID,然后根据每个ID生成一次SQL语句查询。第二个Session里面使用的也是iterate()方法,首先生成一次SQL语句,得到ID,然后根据ID查找对象,由于开启了二级缓存,在二级缓存里面找到了对象,所以就直接输出了,并没有再根据每个ID生成SQL语句。

    不论是一级缓存还是二级缓存,都只能缓存对象,不能缓存属性的值。下面的例子:

        @Override
        public void testCache() {
            // TODO Auto-generated method stub
            Session session = sessionFactory.openSession();
            Transaction tx = session.beginTransaction();  
            Query query = session.createQuery("select c.name from Course c");   
            List<String> names = query.list();  
            for(Iterator iter = names.iterator(); iter.hasNext();){  
                String name = (String) iter.next();  
                System.out.println(name);  
            }  
            System.out.println("----------");  
            query = session.createQuery("select c.name from Course c");   
            names = query.list();  
            for(Iterator iter = names.iterator(); iter.hasNext();){  
                String name = (String) iter.next();  
                System.out.println(name);  
            } 
            System.out.println("----------"); 
            tx.commit();
            session.close();
    
        }

    运行结果:

    Hibernate: 
        select
            course0_.NAME as col_0_0_ 
        from
            clas course0_
    计算机原理
    计算机网络
    数据库原理
    C语言
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程
    ----------
    Hibernate: 
        select
            course0_.NAME as col_0_0_ 
        from
            clas course0_
    计算机原理
    计算机网络
    数据库原理
    C语言
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程
    ----------

    虽然开启了二级缓存,但是查询的结果不是对象,是属性,所以并没有缓存,第2次查询仍然生成了查询语句。要解决这个问题,就需要查询缓存。

    3. 查询缓存

    在配置了二级缓存的基础上,可以设置查询缓存,在SessionFactory的设置里面加上一行:

    <prop key="hibernate.cache.use_query_cache">true</prop>

    即打开了查询缓存。查询缓存也是SessionFactory级别的缓存,在整个SessionFactory里面都是有效的。

    关闭二级缓存,运行下面的例子,在Query后面添加setCacheable(true)打开查询缓存:

        @Override
        public void testCache() {
            // TODO Auto-generated method stub
            Session session = sessionFactory.openSession();
            Transaction tx = session.beginTransaction();  
            Query query = session.createQuery("select c.name from Course c");  
            query.setCacheable(true);  
            List<String> names = query.list();  
            for(Iterator iter = names.iterator(); iter.hasNext();){  
                String name = (String) iter.next();  
                System.out.println(name);  
            }  
            System.out.println("----------");  
            query = session.createQuery("select c.name from Course c");  
            query.setCacheable(true);  
            names = query.list();  
            for(Iterator iter = names.iterator(); iter.hasNext();){  
                String name = (String) iter.next();  
                System.out.println(name);  
            } 
            System.out.println("----------"); 
            tx.commit();
            session.close();
    
        }

    结果:

    Hibernate: 
        select
            course0_.NAME as col_0_0_ 
        from
            clas course0_
    计算机原理
    计算机网络
    数据库原理
    C语言
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程
    ----------
    计算机原理
    计算机网络
    数据库原理
    C语言
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程
    ----------

    由于两次查询的HQL语句是一致的,所以只生成一次SQL语句。但是如果把第二次查询改一下:

            System.out.println("----------"); 
            query = session.createQuery("select c.name from Course c where c.id > 5");  
            query.setCacheable(true);  
            names = query.list();  
            for(Iterator iter = names.iterator(); iter.hasNext();){  
                String name = (String) iter.next();  
                System.out.println(name);  
            } 
            System.out.println("----------"); 

    结果:

    Hibernate: 
        select
            course0_.NAME as col_0_0_ 
        from
            clas course0_
    计算机原理
    计算机网络
    数据库原理
    C语言
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程
    ----------
    Hibernate: 
        select
            course0_.NAME as col_0_0_ 
        from
            clas course0_ 
        where
            course0_.ID>5
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程
    ----------

    由于HQL语句变了,所以第二次也生成了SQL语句。

    查询缓存可以缓存属性,也可以缓存对象,但是当缓存对象时,只缓存对象的ID,不会缓存整个对象。下面的例子:

        @Override
        public void testCache() {
            // TODO Auto-generated method stub
            Session session = sessionFactory.openSession();
            Transaction tx = session.beginTransaction();  
            Query query = session.createQuery("from Course");
            query.setCacheable(true);
            List<Course> list = query.list();
            for (int i=0; i<list.size(); i++){
                System.out.println(list.get(i).getName());  
            }
            System.out.println("----------"); 
            tx.commit();
            session.close();
            
            session = sessionFactory.openSession();
            tx = session.beginTransaction();        
            query = session.createQuery("from Course"); 
            query.setCacheable(true);
            list = query.list();
            for (int i=0; i<list.size(); i++){
                System.out.println(list.get(i).getName());  
            }
            System.out.println("----------"); 
    
            tx.commit();
            session.close();
        }

     结果:

    Hibernate: 
        select
            course0_.ID as ID0_,
            course0_.NAME as NAME0_,
            course0_.COMMENT as COMMENT0_ 
        from
            clas course0_
    计算机原理
    计算机网络
    数据库原理
    C语言
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程
    ----------
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    Hibernate: 
        select
            course0_.ID as ID0_0_,
            course0_.NAME as NAME0_0_,
            course0_.COMMENT as COMMENT0_0_ 
        from
            clas course0_ 
        where
            course0_.ID=?
    计算机原理
    计算机网络
    数据库原理
    C语言
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程
    ----------

    由于开了查询缓存,没有开二级缓存,虽然使用的是list()方法一次查询出了所有的对象,但是查询缓存只缓存了对象ID,没有缓存整个对象。所以在第2个Session里面"from Course"这个HQL由于和前面的相同,并没有生成SQL语句,但是由于没有开二级缓存,没有缓存整个对象,只能根据每个ID去生成一次SQL语句。虽然两次用的都是list()方法,但是第一次是生成SQL语句去一次查询出所有的对象,而第二次是根据查询缓存里面的ID一个一个的生成SQL语句。

    如果同时打开查询缓存和二级缓存,第2个Session里面就不用再根据ID去生成SQL语句了:

    Hibernate: 
        select
            course0_.ID as ID0_,
            course0_.NAME as NAME0_,
            course0_.COMMENT as COMMENT0_ 
        from
            clas course0_
    计算机原理
    计算机网络
    数据库原理
    C语言
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程
    ----------
    计算机原理
    计算机网络
    数据库原理
    C语言
    大学英语A
    Java
    Linux
    高等数学
    语文
    大学物理
    软件工程
    ----------
  • 相关阅读:
    React Native商城项目实战08
    React Native商城项目实战07
    React Native商城项目实战05
    React Native商城项目实战06
    React Native商城项目实战04
    React Native商城项目实战03
    React Native商城项目实战02
    单选框input:radio
    myDate97用法
    STRUTS2配置动态页面
  • 原文地址:https://www.cnblogs.com/mstk/p/6363351.html
Copyright © 2020-2023  润新知