`

hibernate缓存学习记录

阅读更多

缓存是位于应用程序与物理数据源之间,用于临时存放复制数据的内存区域,
目的是为了减少应用程序对物理数据源访问的次数,从而提高应用程序的运行性能.
  Hibernate在查询数据时,首先到缓存中去查找,如果找到就直接使用,找不到的时候
就会从物理数据源中检索,所以,把频繁使用的数据加载到缓存区后,就可以大大减少应
用程序对物理数据源的访问,使得程序的运行性能明显的提升.

缓存分两级,一级session缓存,就是常说的一级缓存;二级应用缓存(二级缓存);
一级缓存,一级缓存依赖于session,在一个session中就是一个缓存,当session失效时,缓存消失。

/**两个session两次加载**/

Java代码 复制代码 收藏代码
  1.  public void loadBookAgain(){   
  2.   Session session = HibernateSessionFactory.getSession();   
  3.   Book book1 = (Book) session.get(Book.class6);   
  4.   Book book2 = (Book) session.get(Book.class6);   
  5.   session.close();   
  6.      
  7. //  Session session1 = HibernateSessionFactory.getSession();   
  8. //  Book book2 = (Book) session1.get(Book.class, 6);   
  9. //  session1.close();   
  10.  }  
 public void loadBookAgain(){
  Session session = HibernateSessionFactory.getSession();
  Book book1 = (Book) session.get(Book.class, 6);
  Book book2 = (Book) session.get(Book.class, 6);
  session.close();
  
//  Session session1 = HibernateSessionFactory.getSession();
//  Book book2 = (Book) session1.get(Book.class, 6);
//  session1.close();
 }



在一个session里面查询两次相同的book,只会执行一次sql。
但若放在不同的session中,将会执行两次数据库查询。
解决问题的办法就是用二级缓存。
二级缓存是SessionFactory级别的全局缓存,它底下可以使用不同的缓存类库,比如ehcache、oscache等。
  参考http://www.javaeye.com/topic/249465
  不是所有的数据都适合放在二级缓存中
  下面这几种情况就不适合加载到二级缓存中:
  1.经常被修改的数据
  2.绝对不允许出现并发访问的数据
  3.与其他应用共享的数据
  下面这己种情况合适加载到二级缓存中:
  1.数据更新频率低
  2.允许偶尔出现并发问题的非重要数据
  3.不会被并发访问的数据
  4.常量数据
  5.不会被第三方修改的数据

配置二级缓存比较简单,以ehcache为例:

添加缓存文件ehcache-hibernate-local.xml

Java代码 复制代码 收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>   
  2. <ehcache>   
  3.   
  4.  <diskStore path="java.io.tmpdir/hibernate/book" />   
  5.  <defaultCache maxElementsInMemory="10000" overflowToDisk="true" eternal="false"  
  6.   memoryStoreEvictionPolicy="LRU" maxElementsOnDisk="10000000"  
  7.   diskExpiryThreadIntervalSeconds="600"  
  8.   timeToIdleSeconds="3600" timeToLiveSeconds="100000" diskPersistent="false" />   
  9.  <!-- Special objects setting. -->   
  10.  <cache name="bean.entity.Book" maxElementsInMemory="500" overflowToDisk="true"  
  11.   eternal="true">   
  12.  </cache>   
  13. </ehcache>  
<?xml version="1.0" encoding="UTF-8"?>
<ehcache>

 <diskStore path="java.io.tmpdir/hibernate/book" />
 <defaultCache maxElementsInMemory="10000" overflowToDisk="true" eternal="false"
  memoryStoreEvictionPolicy="LRU" maxElementsOnDisk="10000000"
  diskExpiryThreadIntervalSeconds="600"
  timeToIdleSeconds="3600" timeToLiveSeconds="100000" diskPersistent="false" />
 <!-- Special objects setting. -->
 <cache name="bean.entity.Book" maxElementsInMemory="500" overflowToDisk="true"
  eternal="true">
 </cache>
</ehcache>


  maxElementsInMemory为缓存对象的最大数目,
  eternal设置是否永远不过期,
  timeToIdleSeconds对象处于空闲状态的最多秒数,
  timeToLiveSeconds对象处于缓存状态的最多秒数 。

  在实体bean的hbm.xml文件中加上缓存配置:

Java代码 复制代码 收藏代码
  1. <!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write   
  2.  nonstrict-read-write transactional-->   
  3.  <cache usage="read-write" />  
 <!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write
  nonstrict-read-write transactional-->
  <cache usage="read-write" />


 
  现在大部分的hibernate应用不再写实体映射配置文件,那么就在实体bean中加上
  //默认的缓存策略.

Java代码 复制代码 收藏代码
  1. @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)  
 @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)


 
  在hibernate定义sessionFactory中加上查询缓存配置:
    <!-- 设置二级缓存插件EHCache的Provider类-->
 

Java代码 复制代码 收藏代码
  1. <property name="hibernate.cache.provider_class">   
  2.    org.hibernate.cache.EhCacheProvider   
  3.   </property>  
<property name="hibernate.cache.provider_class">
   org.hibernate.cache.EhCacheProvider
  </property>


  <!-- 启动"查询缓存" -->
 

Java代码 复制代码 收藏代码
  1. <property name="hibernate.cache.use_query_cache">true</property>   
  2.   <property   
  3.    name="hibernate.cache.provider_configuration_file_resource_path">   
  4.    /ehcache-hibernate-local.xml   
  5.   </property>  
<property name="hibernate.cache.use_query_cache">true</property>
  <property
   name="hibernate.cache.provider_configuration_file_resource_path">
   /ehcache-hibernate-local.xml
  </property>


  如果项目试用了spring,那么相应配置为:
 

Java代码 复制代码 收藏代码
  1. <property name="hibernateProperties">   
  2.    <props>   
  3.     <prop key="hibernate.dialect">${hibernate.dialect}</prop>   
  4.     <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>   
  5.     <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>   
  6.     <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>   
  7.     <prop key="hibernate.cache.provider_configuration_file_resource_path">/ehcache-hibernate-local.xml</prop>   
  8.    </props>   
  9.   </property>  
<property name="hibernateProperties">
   <props>
    <prop key="hibernate.dialect">${hibernate.dialect}</prop>
    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
    <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>
    <prop key="hibernate.cache.provider_configuration_file_resource_path">/ehcache-hibernate-local.xml</prop>
   </props>
  </property>


 
两种配置基本一致。

这个时候在按实体的ID来查询的时候即使不在一个session中,hibernate也只是执行一次sql。
查询缓存做到现在,有一点效果,但基本还是个摆设。
看下面代码:

Java代码 复制代码 收藏代码
  1. public void listBookTwice(){   
  2. String hql = "from Book";   
  3. Session session = HibernateSessionFactory.getSession();   
  4. Query q = session.createQuery(hql);   
  5. List<Book> list1 = q.list();   
  6. List<Book> list2 = q.list();   
  7. session.close();   
  public void listBookTwice(){
  String hql = "from Book";
  Session session = HibernateSessionFactory.getSession();
  Query q = session.createQuery(hql);
  List<Book> list1 = q.list();
  List<Book> list2 = q.list();
  session.close();
 }


同一个query。list了两次,按照之前的效果,应该是执行一个sql。事实是,他要去查两次,
在一个query尚且如此,两个不用说,肯定也是没有用到缓存了。
难道缓存失效了?呵呵,其实是因为我们虽然配置了缓存,但是在query级却没有设置缓存,如果需要query缓存,
则需要手工写入:
q.setCacheable(true);来激活查询缓存。
修改代码如下:

Java代码 复制代码 收藏代码
  1. public void listBookTwice(){   
  2.   String hql = "from Book";   
  3.   Session session = HibernateSessionFactory.getSession();   
  4.   Query q = session.createQuery(hql);   
  5.   q.setCacheable(true);   
  6.   List<Book> list1 = q.list();   
  7.   for(Book b : list1){   
  8.    System.out.println(b.getBname()+"--------->list1");   
  9.   }   
  10. //  List<Book> list2 = q.list();   
  11.   session.close();   
  12.      
  13.   Session session2 = HibernateSessionFactory.getSession();   
  14.   Query q2 = session2.createQuery(hql);   
  15.   q2.setCacheable(true);   
  16.   List<Book> list2 = q2.list();   
  17.   for(Book b : list2){   
  18.    System.out.println(b.getBname()+"--------->list2");   
  19.   }   
  20.   session2.close();   
  21.  }  
public void listBookTwice(){
  String hql = "from Book";
  Session session = HibernateSessionFactory.getSession();
  Query q = session.createQuery(hql);
  q.setCacheable(true);
  List<Book> list1 = q.list();
  for(Book b : list1){
   System.out.println(b.getBname()+"--------->list1");
  }
//  List<Book> list2 = q.list();
  session.close();
  
  Session session2 = HibernateSessionFactory.getSession();
  Query q2 = session2.createQuery(hql);
  q2.setCacheable(true);
  List<Book> list2 = q2.list();
  for(Book b : list2){
   System.out.println(b.getBname()+"--------->list2");
  }
  session2.close();
 }



在两个session立分别list查询,ok,只输出一条sql。说明二级缓存在list查询的时候也起作用了。
那hibernate是根据什么来缓存list呢,参考:http://www.javaeye.com/topic/18904

对于查询缓存来说,缓存的key是根据hql生成的sql,再加上参数,分页等信息(可以通过日志输出看到,不过它的输出不是很可读,最好改一下它的代码)。
比如hql:
from Cat c where c.name like ?
生成大致如下的sql:
select * from cat c where c.name like ?
参数是"tiger%",那么查询缓存的key*大约*是这样的字符串(我是凭记忆写的,并不精确,不过看了也该明白了):
select * from cat c where c.name like ? , parameter:tiger%
这样,保证了同样的查询、同样的参数等条件下具有一样的key。
现在说说缓存的value,如果是list方式的话,value在这里并不是整个结果集,而是查询出来的这一串ID。
也就是说,不管是list方法还是iterate方法,第一次查询的时候,它们的查询方式很它们平时的方式是一样的,
list执行一条sql,iterate执行1+N条,多出来的行为是它们填充了缓存。但是到同样条件第二次查询的时候,就都和iterate的行为一样了,
根据缓存的key去缓存里面查到了value,value是一串id,然后在到class的缓存里面去一个一个的load出来。这样做是为了节约内存。
可以看出来,查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候,都是既填充查询缓存又填充class缓存的。
这里还有一个很容易被忽视的重要问题,即打开查询缓存以后,即使是list方法也可能遇到1+N的问题!相同条件第一次list的时候,
因为查询缓存中找不到,不管class缓存是否存在数据,总是发送一条sql语句到数据库获取全部数据,然后填充查询缓存和class缓存。
但是第二次执行的时候,问题就来了,如果你的class缓存的超时时间比较短,现在class缓存都超时了,但是查询缓存还在,
那么list方法在获取id串以后,将会一个一个去数据库load!因此,class缓存的超时时间一定不能短于查询缓存设置的超时时间
!如果还设置了发呆时间的话,保证class缓存的发呆时间也大于查询的缓存的生存时间。这里还有其他情况,比如class缓存被程序强制evict了,
这种情况就请自己注意了。
另外,如果hql查询包含select字句,那么查询缓存里面的value就是整个结果集了。


可以理解为hibernate缓存了每次查询的hql语句作为缓存map的key,将对应对象的id作为value缓存,每次遇到相同的hql,就将id取出来,
如果在缓存里面有对象,就从缓存取,没有的话就去数据库load

缓存什么时候更新呢?

看代码:

Java代码 复制代码 收藏代码
  1. public void update(){   
  2.   Session session = HibernateSessionFactory.getSession();   
  3.   Transaction tran = session.beginTransaction();   
  4.   tran.begin();   
  5.   Book b = (Book) session.get(Book.class7);   
  6.   b.setIsbn("567890");   
  7.   session.saveOrUpdate(b);   
  8.   tran.commit();   
  9.   session.close();   
  10.  }   
  11. public void listBookTwice(){   
  12.   String hql = "from Book";   
  13.   Session session = HibernateSessionFactory.getSession();   
  14.   Query q = session.createQuery(hql);   
  15.   q.setCacheable(true);   
  16.   List<Book> list1 = q.list();   
  17.   for(Book b : list1){   
  18.    System.out.println(b.getBname()+"----"+b.getIsbn()+"--------->list1");   
  19.   }   
  20.   session.close();   
  21.  }   
  22.     
  23.  public void listBookAgain(){   
  24.   String hql = "from Book";   
  25.   Session session2 = HibernateSessionFactory.getSession();   
  26.   Query q2 = session2.createQuery(hql);   
  27.   q2.setCacheable(true);   
  28.   List<Book> list2 = q2.list();   
  29.   for(Book b : list2){   
  30.    System.out.println(b.getBname()+"----"+b.getIsbn()+"--------->list3");   
  31.   }   
  32.   session2.close();   
  33.  }   
  34.  public static void main(String[] args) {   
  35.    BookDao dao = new BookDao();   
  36.    dao.listBookTwice();   
  37.    dao.update();   
  38.    dao.listBookAgain();   
  39.  }  
public void update(){
  Session session = HibernateSessionFactory.getSession();
  Transaction tran = session.beginTransaction();
  tran.begin();
  Book b = (Book) session.get(Book.class, 7);
  b.setIsbn("567890");
  session.saveOrUpdate(b);
  tran.commit();
  session.close();
 }
public void listBookTwice(){
  String hql = "from Book";
  Session session = HibernateSessionFactory.getSession();
  Query q = session.createQuery(hql);
  q.setCacheable(true);
  List<Book> list1 = q.list();
  for(Book b : list1){
   System.out.println(b.getBname()+"----"+b.getIsbn()+"--------->list1");
  }
  session.close();
 }
 
 public void listBookAgain(){
  String hql = "from Book";
  Session session2 = HibernateSessionFactory.getSession();
  Query q2 = session2.createQuery(hql);
  q2.setCacheable(true);
  List<Book> list2 = q2.list();
  for(Book b : list2){
   System.out.println(b.getBname()+"----"+b.getIsbn()+"--------->list3");
  }
  session2.close();
 }
 public static void main(String[] args) {
   BookDao dao = new BookDao();
   dao.listBookTwice();
   dao.update();
   dao.listBookAgain();
 }



  先list一次,之间更新一个book,第二次list,输出:

Java代码 复制代码 收藏代码
  1. Hibernate: select book0_.id as id0_, book0_.bname as bname0_, book0_.isbn as isbn0_, book0_.price as price0_ from hibernate_test.dbo.book book0_   
  2.  book1250672666171----123456--------->list1   
  3.  book1250672666203----123456--------->list1   
  4.  book1250672666203----123456--------->list1   
  5.  book1250672666203----123456--------->list1   
  6.  book1250672666203----123456--------->list1   
  7. Hibernate: update hibernate_test.dbo.book set bname=?, isbn=?, price=? where id=?   
  8. Hibernate: select book0_.id as id0_, book0_.bname as bname0_, book0_.isbn as isbn0_, book0_.price as price0_ from hibernate_test.dbo.book book0_   
  9. book1250672666171----123456--------->list3   
  10. book1250672666203----567890--------->list3   
  11. book1250672666203----123456--------->list3   
  12. book1250672666203----123456--------->list3   
  13. book1250672666203----123456--------->list3  
 Hibernate: select book0_.id as id0_, book0_.bname as bname0_, book0_.isbn as isbn0_, book0_.price as price0_ from hibernate_test.dbo.book book0_
  book1250672666171----123456--------->list1
  book1250672666203----123456--------->list1
  book1250672666203----123456--------->list1
  book1250672666203----123456--------->list1
  book1250672666203----123456--------->list1
 Hibernate: update hibernate_test.dbo.book set bname=?, isbn=?, price=? where id=?
 Hibernate: select book0_.id as id0_, book0_.bname as bname0_, book0_.isbn as isbn0_, book0_.price as price0_ from hibernate_test.dbo.book book0_
 book1250672666171----123456--------->list3
 book1250672666203----567890--------->list3
 book1250672666203----123456--------->list3
 book1250672666203----123456--------->list3
 book1250672666203----123456--------->list3


可见,当数据库有更新的时候,缓存就失效了。

分享到:
评论

相关推荐

    Hibernate学习笔记整理

    Hibernate学习笔记整理 以下是 Hibernate 框架的详细知识点: Hibernate 介绍 Hibernate 是一个 ORM(Object-Relational Mapping)框架,用于将 Java 对象映射到数据库表中。它提供了一个简洁的方式来访问和操作...

    hibernate个人学习笔记完整版

    【hibernate个人学习笔记完整版】是一份详尽的资料,涵盖了Hibernate框架的基础到高级应用,旨在帮助学习者深入理解和掌握这一强大的Java对象关系映射(ORM)工具。Hibernate作为Java开发中的主流ORM框架,它极大地...

    Hibernate学习笔记特别详细

    《Hibernate学习笔记特别详细》 Hibernate,作为一款开源的Object-Relational Mapping(ORM)框架,为Java开发者提供了强大的数据库操作支持。它简化了数据库访问的复杂性,使得开发人员可以像操作对象一样操作...

    hibernate入门学习笔记+源码

    **hibernate入门学习笔记+源码** **一、Hibernate简介** Hibernate是一个开源的对象关系映射(ORM)框架,它简化了Java应用与数据库之间的交互。通过提供对象化的数据访问方式,Hibernate消除了传统JDBC代码中的...

    传智播客2016hibernate框架学习笔记

    《传智播客2016 Hibernate框架学习笔记》是一份详实的教程,旨在帮助初学者和进阶者深入理解和掌握Hibernate框架。该资源包含了四天的学习内容,分别是day01、day02、day03和day04,涵盖了从基础到高级的多个主题。...

    Hibernate3 学习笔记.ppt

    【一、O/R Mapping】 O/R Mapping,即对象关系映射,是将对象模型与关系数据库之间进行映射的技术。在Hibernate中,O/R Mapping允许开发者以面向对象的方式...学习和掌握Hibernate有助于提升Java应用的数据管理能力。

    Hibernate3.2学习笔记

    《Hibernate3.2学习笔记详解》 在Java开发中,ORM(Object-Relational Mapping)框架如Hibernate极大地简化了数据库操作,使得开发者可以更加专注于业务逻辑而不是底层的数据存取。本篇学习笔记将深入探讨Hibernate...

    hibernate框架开发2016版视频 四天学习笔记完整版

    【hibernate框架开发2016版视频 四天学习笔记完整版】 在软件开发领域,特别是Java Web开发中,Hibernate是一个非常重要的对象关系映射(ORM)框架,它极大地简化了数据库操作,使开发者可以更专注于业务逻辑而不是...

    hibernate 3.3学习笔记

    **hibernate 3.3学习笔记** 在深入探讨Hibernate 3.3的学习笔记之前,首先需要理解Hibernate是什么。Hibernate是一个强大的开源Java持久化框架,它简化了数据库操作,允许开发者将精力集中在业务逻辑上而不是数据库...

    邹波老师Hibernate教程学习笔记

    6. **缓存机制**:Hibernate的缓存分为一级缓存(Session级别)和二级缓存(SessionFactory级别),笔记会介绍它们的工作原理和优化策略,如查询缓存、集合缓存等。 7. **一对多、多对一、一对一和多对多关系映射**...

    Hibernate学习笔记和资料

    hibernate概述,hibernate入门Demo,hibernate配置文件详解(全局配置,实体类映射配置),配置实体规则,核心API详解(Configuration,sessionFactory,session,Transaction),hibernate中的对象状态以及刷新能缓存机制 ...

    Hibernate Annotation 学习笔记

    《Hibernate Annotation 学习笔记》 在Java的持久化框架中,Hibernate以其强大的功能和易用性成为开发者首选之一。而Hibernate Annotation则是Hibernate提供的一种基于注解的实体映射方式,它极大地简化了传统XML...

    hibernate-学习笔记

    【hibernate-学习笔记】 Hibernate 是一个强大的Java对象关系映射(ORM)框架,它为开发者提供了在Java应用程序中管理关系数据库数据的便捷方式。本学习笔记将深入探讨Hibernate的核心概念、配置、实体映射、查询...

    hibernate框架学习笔记整理

    ### hibernate框架学习笔记整理 #### 一、Hibernate框架简介 **Hibernate框架**是一种用于Java应用的**对象关系映射**(Object-Relational Mapping, ORM)解决方案,它允许开发者使用面向对象的方式操作数据库中的表...

    Hibernat一级缓存(源码)

    Hibernate 是一个流行的 Java ORM(对象关系映射)框架,它允许开发者使用面向对象的方式来操作数据库。...提供的 "student" 文件可能包含了关于 Hibernate 一级缓存实现的示例代码,可以进一步帮助理解和学习。

    Hibernate4学习笔记1

    在这个学习笔记中,我们将深入探讨Hibernate4的二级缓存机制、访问策略以及一些主要的新特性。 **1. 二级缓存分类** Hibernate的二级缓存将不同类型的实体分开存储,这有助于优化性能和内存管理: - **单个实体**...

    域模型的种状态与hibernate缓存PPT学习教案.pptx

    【域模型的状态与Hibernate缓存理解】 在Java的持久化框架Hibernate中,域模型的状态管理是优化数据库操作的关键。域模型通常指的是实体类,如这里的`Customer`类,它们代表数据库中的表记录。域模型有以下几种状态...

    Hibernate学习笔记与总结

    **Hibernate学习笔记与总结** Hibernate 是一款开源的对象关系映射(ORM)框架,它为Java开发者提供了一种在关系数据库上操作对象数据的便捷方式。本文将深入探讨Hibernate的核心概念、配置、实体类、映射文件、...

    Hibernate+学习笔记

    【Hibernate 学习笔记】 Hibernate 是一款开源的对象关系映射(ORM)框架,它为 Java 开发者提供了在关系数据库和面向对象编程之间的一个抽象层。这个框架使得开发者可以使用面向对象的方式来操作数据库,而无需...

Global site tag (gtag.js) - Google Analytics