- 浏览: 50078 次
- 性别:
- 来自: 厦门
文章分类
最新评论
缓存是位于应用程序与物理数据源之间,用于临时存放复制数据的内存区域,
目的是为了减少应用程序对物理数据源访问的次数,从而提高应用程序的运行性能.
Hibernate在查询数据时,首先到缓存中去查找,如果找到就直接使用,找不到的时候
就会从物理数据源中检索,所以,把频繁使用的数据加载到缓存区后,就可以大大减少应
用程序对物理数据源的访问,使得程序的运行性能明显的提升.
缓存分两级,一级session缓存,就是常说的一级缓存;二级应用缓存(二级缓存);
一级缓存,一级缓存依赖于session,在一个session中就是一个缓存,当session失效时,缓存消失。
/**两个session两次加载**/
- 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();
- }
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
- <?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>
<?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文件中加上缓存配置:
- <!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write
- nonstrict-read-write transactional-->
- <cache usage="read-write" />
<!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write nonstrict-read-write transactional--> <cache usage="read-write" />
现在大部分的hibernate应用不再写实体映射配置文件,那么就在实体bean中加上
//默认的缓存策略.
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
在hibernate定义sessionFactory中加上查询缓存配置:
<!-- 设置二级缓存插件EHCache的Provider类-->
- <property name="hibernate.cache.provider_class">
- org.hibernate.cache.EhCacheProvider
- </property>
<property name="hibernate.cache.provider_class"> org.hibernate.cache.EhCacheProvider </property>
<!-- 启动"查询缓存" -->
- <property name="hibernate.cache.use_query_cache">true</property>
- <property
- name="hibernate.cache.provider_configuration_file_resource_path">
- /ehcache-hibernate-local.xml
- </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,那么相应配置为:
- <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>
<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。
查询缓存做到现在,有一点效果,但基本还是个摆设。
看下面代码:
- 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();
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);来激活查询缓存。
修改代码如下:
- 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();
- }
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
缓存什么时候更新呢?
看代码:
- 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();
- }
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,输出:
- 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: 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 学习笔记 get和load的区别 一对一,一对多,多对多 悲观锁,乐观锁 hibernate查询语言hql hibernate一级缓存、二级缓存 抓取策略
hibernate概述,hibernate入门Demo,hibernate配置文件详解(全局配置,实体类映射配置),配置实体规则,核心API详解(Configuration,sessionFactory,session,Transaction),hibernate中的对象状态以及刷新能缓存机制 ...
目录 课程内容 1 HelloWorld 2 Hibernate原理模拟 - 什么是O/R Mapping以及为什么... 4 一级缓存和二级缓存和査询缓存(面试题)(详见hibernate_3000_Hibernate_3KindsOf_Cache) 5 事务并发处理(面试的意义更大)
Hibernate 学习笔记 Hibernate 学习笔记 1 第一个hibernate项目(hibernate_first) 2 测试实体对象的生命周期(hibernate_session) 3 hibernate基本映射(hibernate_basemapping) 4 class实体类---表 4 标签id 5 ...
NULL 博文链接:https://microjava.iteye.com/blog/525981
数据记录(Metrics) 20. 工具箱指南 20.1. Schema自动生成(Automatic schema generation) 20.1.1. 对schema定制化(Customizing the schema) 20.1.2. 运行该工具 20.1.3. 属性(Properties) 20.1.4. 使用Ant...
这是我在学习时记录的hibernate笔记,比较详细。对一些比较琐碎的例如一级二级缓存有较为详细的讲解。
1)Demo 学习要点简介: 1.通过google ehcache-spring-annotatios.jar自动注解方式实现整合Spring+Ehcache。 2.Action里通过struts2-spring-plugin.jar插件自动根据名字注入。 3.Ajax无刷新异步调用Struts2,返回...
用sf4j及logback(log4j的升级版)记录日志;proxool(据说是dbcp和c3p0三者中最优秀的)做连接池;使用jquery的ajax实现仿google人名自动补全;头像上传剪切压缩处理。 包含有完整的jar包和源代码,可以直接下载...
其中包含apache的log4j记录日志信息,spring管理组件,springmvc分层,springaop配置数据库事务控制,hibernate二级缓存配置,实现了查询,用户登录注册,请求验证是否登录等基础功能Demo,基于后台测试,使用前台...
门槛高在怎么设计O/R映射,在性能和对象模型之间如何权衡取得平衡,以及怎样用好Hibernate缓存与数据加载策略方面需要你的经验和能力都很强才行。国内目前前的情况精通hibernate技术大牛非常少。 3. sql优化方面,...
其中包含apache的log4j记录日志信息,spring管理组件,springmvc分层,springaop配置数据库事务控制,hibernate二级缓存配置,实现了查询,用户登录注册,请求验证是否登录等基础功能Demo,基于后台测试,使用前台...
用sf4j及logback(log4j的升级版)记录日志;proxool(据说是dbcp和c3p0三者中最优秀的)做连接池;使用jquery的ajax实现仿google人名自动补全;头像上传剪切压缩处理。 包含有完整的jar包和源代码,可以直接下载...
用sf4j及logback(log4j的升级版)记录日志;proxool(据说是dbcp和c3p0三者中最优秀的)做连接池;使用jquery的ajax实现仿google人名自动补全;头像上传剪切压缩处理。 包含有完整的jar包和源代码,可以直接下载...
用sf4j及logback(log4j的升级版)记录日志;proxool(据说是dbcp和c3p0三者中最优秀的)做连接池;使用jquery的ajax实现仿google人名自动补全;头像上传剪切压缩处理。 包含有完整的jar包和源代码,可以直接下载...
\contentsline {chapter}{Contents}{2}{section*.1} {1}Java基础}{17}{chapter.1} {1.1}基本语法}{17}{section.1.1} {1.2}数字表达方式}{17}{section.1.2} {1.3}补码}{19}{section.1.3} {1.3.1}总结}{23}{...
第1章 Hibernate_day01 笔记 1.1 Hibernate 框架的学习 路线 第一天:Hibernate 的入门(Hibernate 的环境搭建、Hibernate 的API、Hibernate 的 CRUD) 第二天:Hibernate 的一级缓存、其他的 API 第三天:Hibernate...
1.6.9 与hibernate不同 16 2 Dao开发方法 16 2.1 需求 16 2.2 SqlSession的使用范围 17 2.2.1 SqlSessionFactoryBuilder 17 2.2.2 SqlSessionFactory 17 2.2.3 SqlSession 17 2.3 原始Dao开发方式 18 2.3.1 映射文件...