Hibernate缓存详解

 更新时间:2017年02月04日 10:28:51   作者:MSTK  
本文主要介绍了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:\Eclipse\MyWorkspace\Cache"/>
  <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
高等数学
语文
大学物理
软件工程
----------

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持脚本之家!

相关文章

  • Java中的指令重排详解

    Java中的指令重排详解

    在 Java 中,指令重排是一种性能优化技术,它涉及到编译器和处理器对程序中指令的执行顺序进行调整,以提高执行效率,本文给大家详细介绍了Java中的指令重排,需要的朋友可以参考下
    2023-12-12
  • SpringBoot临时属性设置方法

    SpringBoot临时属性设置方法

    这篇文章主要介绍了SpringBoot临时属性设置方法,SpringBoot工程可以基于java环境独立进行jar文件启动服务,文中给大家提到了命令行启动常见问题以及解决方案,需要的朋友可以参考下
    2022-09-09
  • Android应用开发之将SQLite和APK一起打包的方法

    Android应用开发之将SQLite和APK一起打包的方法

    这篇文章主要介绍了Android应用开发之将SQLite和APK一起打包的方法,文章时间较早,尽管现在开发环境已大都迁移至Android Studio上,但打包原理依然相同,需要的朋友可以参考下
    2015-08-08
  • 一篇文章告诉你JAVA Mybatis框架的核心原理到底有多重要

    一篇文章告诉你JAVA Mybatis框架的核心原理到底有多重要

    yBatis的底层操作封装了JDBC的API,MyBatis的工作原理以及核心流程与JDBC的使用步骤一脉相承,MyBatis的核心对象(SqlSession,Executor)与JDBC的核心对象(Connection,Statement)相互对应
    2021-06-06
  • Java面试题冲刺第二十五天--并发编程3

    Java面试题冲刺第二十五天--并发编程3

    这篇文章主要为大家分享了最有价值的三道关于并发编程的面试题,涵盖内容全面,包括数据结构和算法相关的题目、经典面试编程题等,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Java之Set 交集,差集,并集的用法

    Java之Set 交集,差集,并集的用法

    这篇文章主要介绍了Java之Set 交集,差集,并集的用法,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • JavaWeb实现同一帐号同一时间只能一个地点登陆(类似QQ登录的功能)

    JavaWeb实现同一帐号同一时间只能一个地点登陆(类似QQ登录的功能)

    最近做了企业项目,其中有这样的需求要求同一帐号同一时间只能一个地点登陆类似QQ登录的功能。下面小编通过本文给大家分享实现思路,感兴趣的朋友参考下吧
    2016-11-11
  • Mybatis中的延迟加载,以及原理分析

    Mybatis中的延迟加载,以及原理分析

    这篇文章主要介绍了Mybatis中的延迟加载以及原理,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-04-04
  • Spring boot热部署devtools过程解析

    Spring boot热部署devtools过程解析

    这篇文章主要介绍了Spring boot热部署devtools过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • java中逻辑控制举例具体讲解

    java中逻辑控制举例具体讲解

    Java程序逻辑控制通俗说就是对代码执行顺序的控制,这篇文章主要给大家介绍了关于java中逻辑控制的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01

最新评论