1. 一级缓存:Session中共享
测试:可以通过查看输出的 select 语句的数目来测试Hibernate中的Session级的缓存
使用缓存的三种操作:放,取,删
会从缓存中拿数据的方法:get,load,iterate [id为native(自增长的形式的话,save方法是不会放入到一级缓存或者二级缓存中的)]
会向缓存中放入数据的方法:save,update,saveOrUpdate,get,load,iterate,lock(还没有使用过,lock是把一个没有更改过的脱管状态的对象变成持久状态)
删除缓存内容的方法:evict(清除某个数据),clear(清除所有 s.clear() 清除一级缓存中的内容)
缺点:Hibernate的缓存内容默认是不会清除的,所以,如果数据内容太大时会造成缓存溢出!
还有一个缺点是:作用范围比较小,只在session关闭之前有效
模拟缓存实现:Map
package cn.itcast.hibernate; import java.util.HashMap; import java.util.Map; import cn.itcast.hibernate.domain.User; public class CacheDemo { static Map cache = new HashMap(); public static void main(String[] args) { User u = getUser(1);//第一次是从数据库中取出来 User u1 = getUser(1);//第二次是直接从缓存中取数据 } public static void update(User user){ updateDB(user); String key = User.class.getName() + user.getId(); cache.remove(key); } public static User getUser(int id) { String key = User.class.getName() + id; User user = (User) cache.get(key); if (user != null) return user; user = getFromDB(); cache.put(key, user); return user; } private static void updateDB(User user) { // TODO Auto-generated method stub } private static User getFromDB() { // TODO Auto-generated method stub return null; } }
2. 二级缓存:SessionFactory级共享
实现为可插拔,通过修改cache.provider_class参数来改变;
hibernate内置了对EhCache,OSCache,TreeCache,SwarmCache的支持,可以通过实现CacheProvider和Cache接口来加入Hibernate不支持的缓存实现。
Hibernate默认的二级缓存支持类:hibernate.cache.provider_classorg.hibernate.cache.HashtableCacheProvider 采用的是Hashtable,但是性能并不是很好
如果要设置某个类的对象存入缓存中有两种方法:
①在hibernate.cfg.xml中加入:
<class-cache class="className" usage="read-only"/>
②在映射文件的class元素加入子元素:
<cache usage="read-write"/>
其中usage:read-only,read-write,nonstrict-read-write,transactional
配置:
1.开启二级缓存
2.设置二级缓存的第三方类
3.导入相应的包并配置
4.设置允许放入缓存的类
例如:
hibernate.cfg.xml
<!-- 开启二级缓存 --> <property name="hibernate.cache.use_second_level_cache">true</property> <!-- 开启查询语句可以使用二级缓存 --> <property name="hibernate.cache.use_query_cache">true</property> <!-- 设置二级缓存的支持类 --> <property name="hibernate.cache.provider_class">org.hibernate.cache.OSCacheProvider</property> <!-- 设置可以放入缓存中的类 --> <class-cache usage="read-only" class="com.yinger.domain.User"/>
添加 文件 oscache.properties (一般是在Hibernate下载到的etc文件夹中),可以自行设置里面的属性值
统计信息:可以得到很多的关于二级缓存的信息(例如:hit,put,miss次数)
用sessionFactory.getSatistics()获取统计信息
示例代码:
Statistics st = HibernateUtils.getSessionFactory().getStatistics(); System.out.println(st.getSecondLevelCachePutCount()); System.out.println(st.getSecondLevelCacheHitCount()); System.out.println(st.getSecondLevelCacheMissCount());
从二级缓存取数据的方法:get,load,iterator,而存数据的方法很多
Session的save(这个方法不适合native生成方式的主键),update,saveOrUpdate,list,iterator,get,load,以及Query,Criteria都会填充二级缓存,
但(在没打开查询缓存时)只有Session的iterator,get,load会从二级缓存中取数据(iterator可能存在N+1次查询,如果没有取到数据的话)。
Query,Criteria(查询缓存)由于命中率较低,所以hibernate缺省是关闭;修改cache.use_query_cache为true打开对查询的缓存,
并且调用query.setCacheable(true)或criteria.setCacheable(true)。
测试代码: N+1次查询的问题的代码 /** * @Author:胡家威 * @CreateTime:2011-8-16 上午12:52:21 * @Description: */ package com.yinger.main; import java.util.Date; import java.util.Iterator; import java.util.List; import org.hibernate.Hibernate; import org.hibernate.Query; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.stat.Statistics; import com.yinger.domain.IdCard; import com.yinger.domain.Person; import com.yinger.util.HibernateUtils; public class One2One { public static void main(String[] args) { add(); add(); System.out.println("-------------"); ncpp(); System.out.println("-------------"); iterate(); // Statistics st = HibernateUtils.getSessionFactory().getStatistics(); // System.out.println(st.getSecondLevelCachePutCount()); // System.out.println(st.getSecondLevelCacheHitCount()); // System.out.println(st.getSecondLevelCacheMissCount()); // IdCard card = queryIdCardById(1); // System.out.println(card.getPerson().getName()); } // 添加信息到数据库中 private static void add() { Person p = new Person(); p.setName("person name"); IdCard card = new IdCard(); card.setLife(new Date()); // p.setIdCard(card); // 外键一对一映射的这种情况下 id_card表中的person_id字段没有值 card.setPerson(p); Session s = null; Transaction tr = null; try { s = HibernateUtils.getSession(); tr = s.beginTransaction(); s.save(p); s.save(card); tr.commit(); } catch (Exception e) { if (tr != null) tr.rollback(); } finally { if (s != null) s.close(); } } // 根据id查询IdCard public static IdCard queryIdCardById(int id) { IdCard card = null; Session s = null; try { s = HibernateUtils.getSession(); card = (IdCard) s.get(IdCard.class, id); Hibernate.initialize(card.getPerson()); // 可以使用这个方法来初始化代理对象 // System.out.println(card.getPerson().getName()); return card; } catch (Exception e) { e.printStackTrace(); } finally { if (s != null) { s.close(); } } return card; } // 测试 N+1 次查询 @SuppressWarnings("unchecked") private static void ncpp() { Session s = HibernateUtils.getSession(); Query q = s.createQuery("from IdCard"); q.setCacheable(true); List<IdCard> ics = q.list(); for (IdCard ic : ics) { System.out.println(ic.getPerson().getName()); } s.close(); } // 测试 N+1 次查询 @SuppressWarnings("unchecked") private static void iterate() { Session s = HibernateUtils.getSession(); Query q = s.createQuery("from IdCard"); q.setCacheable(true); Iterator<IdCard> it = q.iterate(); while(it.hasNext()) { System.out.println(it.next().getPerson().getName()); } s.close(); } }
如果在hibernate.cfg.xml中配置了
<!-- 设置可以放入缓存中的类 --> <class-cache usage="read-only" class="com.yinger.domain.Person"/> <class-cache usage="read-only" class="com.yinger.domain.IdCard"/>
测试结果:
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: insert into Person (name) values (?) Hibernate: insert into id_card (life) values (?) Hibernate: insert into Person (name) values (?) Hibernate: insert into id_card (life) values (?) ------------- Hibernate: select idcard0_.id as id, idcard0_.life as life4_ from id_card idcard0_ Hibernate: select person0_.id as id0_, person0_.name as name3_0_ from Person person0_ where person0_.id=? person name Hibernate: select person0_.id as id0_, person0_.name as name3_0_ from Person person0_ where person0_.id=? person name ------------- Hibernate: select idcard0_.id as col_0_0_ from id_card idcard0_ person name person name
如果调换一下 ncpp 和 iterate 方法的调用顺序,结果是:
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment). log4j:WARN Please initialize the log4j system properly. Hibernate: insert into Person (name) values (?) Hibernate: insert into id_card (life) values (?) Hibernate: insert into Person (name) values (?) Hibernate: insert into id_card (life) values (?) ------------- Hibernate: select idcard0_.id as col_0_0_ from id_card idcard0_ Hibernate: select idcard0_.id as id0_, idcard0_.life as life4_0_ from id_card idcard0_ where idcard0_.id=? Hibernate: select person0_.id as id0_, person0_.name as name3_0_ from Person person0_ where person0_.id=? person name Hibernate: select idcard0_.id as id0_, idcard0_.life as life4_0_ from id_card idcard0_ where idcard0_.id=? Hibernate: select person0_.id as id0_, person0_.name as name3_0_ from Person person0_ where person0_.id=? person name ------------- Hibernate: select idcard0_.id as id, idcard0_.life as life4_ from id_card idcard0_ person name person name
很清楚的发现两次查询实现了SessionFactory级别(也就是二级缓存)的共享,iterate方法共需要执行5个select,ncpp需要调用3个select
如果没有在hibernate配置文件中加上那两句,是不行的,默认这两个类是不会放入二级缓存的,结果总是3+5=8条select语句
[不过奇怪的是我删掉了q.setCacheable(true);语句之后结果不变,按理应该不会存入到二级缓存中的啊]
3.其他的缓存
分布式缓存(每台服务器保存自己的缓存):如果数据更新了,并且需要更新的其他服务器很多,那么广播出去进行更新的话就很耗时 [读取次数大于更新次数]
中央缓存(利用一台服务器来进行缓存):读取数据时要从另一台服务器得到,这个比较耗时
使用缓存的条件:
1.读取大于修改。
2.数据量不能超过内存容量。
3.对数据要有独享的控制。
4.可以容忍出现无效数据。
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
优秀的文章推荐:
Hibernate3一级缓存和二级缓存的理解!
Hiberante3 一级缓存总结
1. Session 级别的缓存,它同session邦定。它的生命周期和session相同。Session消毁,它也同时消毁;管理一级缓存,一级缓存无法取消,用两个方法管理,clear(),evict()
2. 两个session 不能共享一级缓存,因它会伴随session的生命周期的创建和消毁;
3. Session缓存是实体级别的缓存,就是只有在查询对象级别的时候才使用,如果
使用HQL和SQL是查询属性级别的,是不使用一级缓存的!
4 . iterate 查询使用缓存,会发出查询Id的SQL和HQL语句,但不会发出查实体的,
它查询完会把相应的实体放到缓存里边,一些实体查询如果缓存里边有,就从缓存中查询,但还是会发出查询id的SQL和HQL语句。如果缓存中没有它会数据库中查询,然后将查询到的实体一个一个放到缓存中去,所以会有N+1问题出现。
5 . List()和iterate 查询区别:
使用iterate,list查询实体对象
*N+1问题,在默认情况下,使用query.iterate查询,有可以能出现N+1问题
所谓的N+1是在查询的时候发出了N+1条sql语句1:首先发出一条查询对象id列表的sqlN:
根据id列表到缓存中查询,如果缓存中不存在与之匹配的数据,那么会根据id发出相应的sql语句
list和iterate的区别?
list每次都会发出sql语句,list会向缓存中放入数据,而不利用缓存中的数据
iterate:在默认情况下iterate利用缓存数据,但如果缓存中不存在数据有可以能出现N+1问题
6.get()和load(),iterate方法都会使用一级缓存
7.hiberate3 session 存储对象的过程如下:
例如 object 对象
Session.save(object);
这时候不会把数据放到数据库,会先放到session缓存中去,数据库中没有相应记录,session.flush();才发SQL和HQL语句,数据库中有了相应记录,
但是数据库用select查不到,这是跟数据库事务级别有关系
Hiberante3 二级缓存总结
1.Hibernate3的(sessionFactory)二级缓存和session级别的缓存一样都只对实体对象做缓存,不对属性级别的查询做缓存;二级缓存的生命周期和sessionFactory的生命周期是一样的,sessionFactory可以管理二级缓存;
2.sessionFactory级别的缓存,需要手动配置;所有的session可以共享sessionFactory 级别的缓存;(一般把一些不经常变化的实体对象放到sessionFactory级别的缓存中)
3.Hiberante3二级缓存的配置和使用方法如下:
1. 必须把ehcache.jar包导入,然后到Hibernate3.2的etc文件下把ehcache.xml复制到工程src目录下(ehcache.xml里边的参数里边有详细英文说明);
(说明:ehcache.jar是第三方法的缓存产品,hiberante只是把它做了集成,还有好多第三方hibernate集成的缓存产品,相关说明请查阅hiberante3开发手册;ehcache支持分布应用的(这个和Hibernate3.2开发手册有出入,经过官网查证确实支持了),如果有分布式需求,请换成支持分布式的二级缓存产品,hiberate3开发手册都有相头说明。配置方法都类似);
4.Hibernate3的二级缓存默认是开起的,也可以指定开起。在hibernate.cfg.xml 文件下配置如下:
*修改hibernate.cfg.xml文件,开启二级缓存;
<property name=”hibernate.cache.use_second_level_cache”>true</property>
*指定二级缓存产品的提供商;
<property name=”hibernate.cache.provider_class”> org.hibernate.cache.EhCacheProvider
</property>
要让那些实体使用二级缓存,在hibernate.cfg.xml配置文件中加入:
<!—
让这个实体用二级缓存 也可以在实体中映射文件去配置即:
<cache usage="read-only"/>
-->
<class-cache class=”com.zzz.hibernate.ClassT” usage=”read-only”/>
Read-only一般使用这个策略,其它的hibernate3开发手册中也有详细介绍;
CacheMode去hibernate3开发手册中搜索这个关键字,可以找到一级缓存和二级缓存交互使用的问题;
相关推荐
9. **缓存机制**:Hibernate提供了第一级缓存(Session级别的)和第二级缓存(SessionFactory级别的),能有效提高数据访问性能。 10. **关联映射**:Hibernate支持一对一、一对多、多对一和多对多等多种关联关系...
理解ORM的概念是学习Hibernate的第一步。 2. **Hibernate配置**: 在使用Hibernate时,我们需要配置一个名为`hibernate.cfg.xml`的文件,其中包含了数据库连接信息、方言、缓存策略等。学习如何正确配置这些参数...
**hibernate框架学习实例** Hibernate 是一个强大的Java持久化框架,它简化了数据库与对象之间的交互,使得开发者可以更加专注于业务逻辑而不必过多地处理SQL语句。本实例是针对hibernate 3.x版本设计的教学案例,...
7. **第一级缓存与第二级缓存**:Hibernate的第一级缓存是每个会话的私有缓存,存储了会话中所有实体的最新状态。第二级缓存则是一个可选特性,允许多个会话共享数据,提高了性能,但需要谨慎使用以避免并发问题。 ...
【标题】:“Hibernate缓存”涉及的是Java持久化框架Hibernate中的一个重要特性,即数据缓存。缓存的主要目的是提高数据访问速度,减少数据库的负担,从而提升应用性能。Hibernate提供了两级缓存机制:第一级缓存是...
`FrameTest`可能包含了实现这些功能的测试代码,可以用来验证和学习Hibernate的各种特性。阅读和分析源代码是深入理解Hibernate原理的有效途径。 总之,Hibernate通过ORM简化了数据库操作,提升了开发效率,而对其...
9. **缓存机制**:探讨Hibernate的一级缓存(Session级别)和二级缓存(SessionFactory级别),以及第三方缓存插件如Ehcache的集成和使用。 10. **实体状态与生命周期**:了解Hibernate中对象的四种状态(瞬时、...
9. **性能优化**:学习Hibernate如何处理批处理、连接池等性能优化策略,并在我们的框架中应用。 10. **测试与文档**:创建单元测试以验证框架的功能,同时编写清晰的文档,帮助其他开发者理解和使用我们的框架。 ...
《Hibernate DOC中文文档》是学习Hibernate框架的重要参考资料,它详细阐述了Hibernate的核心概念、配置、对象关系映射(ORM)以及各种操作技巧。对于初学者和有经验的开发者来说,这份文档都是掌握Hibernate不可或...
这个压缩包文件“hibernate最简单的例子”提供了一个不涉及其他框架的纯净Hibernate应用实例,非常适合初学者学习和理解Hibernate的基本用法。 1. Hibernate简介: Hibernate是基于Java的开源ORM框架,它通过XML...
3. **更好的第二级缓存集成**:Hibernate 4.4.0增强了对第三方缓存提供者(如Ehcache、Infinispan等)的支持,提供了更好的二级缓存配置和使用体验,有助于减少数据库负载,提高系统响应速度。 4. ** Criteria API...
在学习Hibernate之前,需要了解ORM的基本概念。ORM允许开发者使用面向对象的编程方式来操作数据库,而无需直接编写SQL语句。Hibernate作为ORM框架,将Java类映射到数据库表,使得数据操作变得简单易行。 课程内容...
Java框架Hibernate是一个...学习Hibernate框架,不仅可以提升数据库操作的效率,还能帮助开发者更好地理解和实践面向对象的设计原则。结合实际项目练习,可以深入理解其工作原理和最佳实践,从而在软件开发中游刃有余。
《Hibernate入门:初识与实践》 ...总之,"hibernate第一个hibernate"项目是一个绝佳的起点,它将引导你了解并掌握Hibernate的基本概念和操作。通过实践,你可以逐步熟悉ORM思想,为后续的Java开发奠定坚实的基础。
8. 性能优化:分析和解决Hibernate性能问题,如缓存策略、批处理、连接池配置等,提升应用的运行效率。 9. 高级特性:探讨Hibernate的高级功能,如关联映射、集合映射、继承映射、多态查询、懒加载和代理对象等。 ...
- **Ehcache.jar:** 第三方缓存组件,用于实现Hibernate的一级缓存和二级缓存。 - **Hibernate3.jar:** Hibernate核心库,包含所有主要的Hibernate功能。 - **Jta.jar:** Java Transaction API,用于处理事务。 - **...
《精通Hibernate:Java持久化对象技术详解[第二版]》是一部深入探讨Hibernate框架的专业书籍,旨在帮助Java开发者熟练掌握和运用这一强大的ORM(Object-Relational Mapping)工具。Hibernate是Java开发领域中广泛...
6. **缓存策略**:Hibernate提供了第一级缓存(Session级别的)和第二级缓存(SessionFactory级别的)。合理利用缓存能提高性能,但需注意缓存一致性问题。 7. **性能优化**:包括延迟加载(Lazy Loading)、批处理...
**标题解析:**“hibernate的第一个例子”表明这是一个关于Hibernate框架的基础教程,主要目标是展示如何使用Hibernate进行数据持久化操作。 **描述分析:**描述提到这是一个超级简单的例子,包含一个持久化对象...