缓存,介于应用程序和永久数据存储源之间,作用是为了降低应用程序对物理数据源访问的频率,从而提高应用的运行性能。
例如我们cpu执行效率每秒处理的数据高达上千兆,而我们的硬盘读取速度却没那么高,读取几百兆,这时候我们使用缓存来存储数据,存储满后一次性交由cpu处理。
Hibernate中的缓存,同样是为了提高效率。Hibernate的缓存包括Session的缓存和SessionFactory的缓存。
Session的缓存是内置的,不能被卸载,也被称为Hibertnate的一级缓存。
SessionFactory有一个内置缓存和外置缓存。SessionFactory的外置缓存是一个可配置的缓存插件。默认情况下,Hibernate不会启用这个缓存插件。被称为Hibernate的二级缓存。
缓存的范围
缓存的范围决定了缓存的生命周期以及可以被谁访问。
事务范围:缓存只能被当前事务访问。一级缓存是Session的缓存,Session对象生命周期通常对应一个事务,因此是事务范围的缓存。
进程范围:缓存被进程内的所有事务共享。二级缓存是可配置的缓存插件,由SessionFactory管理,SessionFactory生命周期和应用程序的进程对应,因此是进程范围的缓存。
集群范围:在集群环境中,缓存被同一个机器或者多个机器上的多个进程共享。
Hibernate一级缓存:Session缓存
Session缓存是Hibernate的一级缓存。Session对象中具有一个缓存。Session的缓存是一块内存空间,存放的是持久化对象。
当Session通过save方法持久化一个对象时,该对象被加入到Session缓存中。
当Session通过get方法获取一个持久化对象时,Session会先判断Session缓存中是否存在这个对象,如果存在,就不需要再从数据库中查找。
我们来测试一下缓存的存在:
//开启事务 ???????Transaction ts=session.beginTransaction(); ???????//加上断点,当我们执行完这一步,会打印select语句,而后面的都不会打印,说明并没有从数据库中获取 ???????User user1=session.get(User.class, 5); ??????????//这次get方法会先从session缓存中查找,由于已经存在,直接返回引用 ????????User user2=session.get(User.class, 5); ???????User user3=session.get(User.class, 5); ???????System.out.println(user1==user2);//true ???????System.out.println(user1==user3);//true ???????session.close();
脏检查及清理缓存的机制
我们先来看下面的例子
Transaction ts=session.beginTransaction(); ???????User user1=session.get(User.class, 5); ???????user1.setName("swaggy"); ???????ts.commit();
我们发现我们改变了Name属性,这时候session缓存中的对象的name属性和数据库表中的NAME字段不一致了。但是我们并没有进行更新操作,而是直接提交了事务。
幸运的是,Session中在清理缓存的时候,会自动进行脏检查。如果发现Session缓存中的持久化对象和数据库中的记录不一致,就会根据对象的最新属性去更新数据库。
所以在本例中,Session会自动提交一个update语句对数据库进行更新。
Session是怎样进行脏检查的呢?
当一个对象被加入到Sesion缓存中时,Session会为该对象复制一份快照。当Session清理缓存时,会比较当前对象的属性和快照来判断是否发生变化,如果发生变化,就会根据最新属性来执行相关的更新操作。
我们看下面一个例子加深对快照的理解
???????//我们从数据库中取出 id为5,name为tom,password为123456的对象 ???????Transaction ts=session.beginTransaction(); ???????User user1=session.get(User.class, 5); ???????session.update(user1); ???????session.close(); ????????
过程:获取了持久化对象,放入缓存中,并创建了快照,我们执行更新,Session缓存中的对象会和快照进行比较,没有任何变化,所以不会执行update语句。
???????//我们自己设置一个和数据库中一模一样的对象,这时候会打印update语句 ???????Transaction ts=session.beginTransaction(); ???????User user=new User(); ???????user.setId(5); ???????user.setName("tom"); ???????user.setPassword("123456"); ???????session.update(user); ???????ts.commit(); ???????session.close();
过程:因为此时我们执行update语句时会将对象直接放入缓存中,但是没有持久化对象的快照,所以进行对比结果就是不一致,所以尽管什么都没更改,还是会执行update语句,在控制台打印。
什么时候会清理缓存呢?
- -默认情况下,在调用commit()方法时会先清理缓存。再向数据库提交事务。
- -当执行查询操作时,如果缓存中的持久化对象属性已经发生了改变,就会先清理缓存,同步数据,保证查询到的是正确的结果。
- -当应用程序显式调用Session的flush()方法时
- -Session清理缓存的例外情况,如果对象使用的是native生成策略生成OID,那么调用Session的save()方法来保存该对象时,会立刻执行向数据库的插入语句。
如果不希望Session在以上默认的时间点清理缓存,可以通过Session的setFlushMode()方法来设定清理缓存的时间点。
FlushMode类定义了三种清理模式。
???????????????????????????各种查询方法 ?????????commit()方法 ???????flush()方法 -FlushMode.AUTO(默认模式) ???????清理 ????????????????清理 ???????????????清理-FlushMode.COMMIT ?????????????不清理 ???????????????清理 ???????????????清理 -FlushMode.NEVER ??????????????不清理 ???????????????不清理 ?????????????清理 ??例如Session.setFlushMode(FlushMode.AUTO)
Hibernate的二级缓存:SessionFactory
二级缓存简介
二级缓存是一个可插拔的缓存插件,由SessionFactory管理,是进程范围的缓存。
???二级缓存有可能出现并发问题,因此需要采用适当的并发访问策略。
???该策略为缓存中的数据提供了事务隔离级别。
???Hibernate还提供了查询缓存,依赖于二级缓存。
二级缓存中存放什么?
符合以下条件的数据适合存放在二级缓存中
- ???-很少被修改的数据
- ???-不是很重要的数据,允许偶然出现的并发问题
- ???-参考数据(指供应用程序参考的常量数据)
???以下数据不适合存放到二级缓存中
- ???-经常被修改的数据
- ???-财务数据,绝对不允许出现并发文日
- ???-与其他应用共享的数据
???二级缓存中缓存的并不是对象,而是对象的散装数据。
常用二级缓存插件
二级缓存是可配置的插件,Hibernate允许选用以下的缓存插件
- -EHCache:可作为进程范围内的缓存。存放数据的物理介质可以是硬盘或者内存,支持hibernate的查询缓存。
- -OSCache:可作为进程范围内的缓存,存放数据的物理介质可以是硬盘或者内存,支持hibernate的查询缓存,提供了丰富的缓存数据过期策略。
- -SwarmCache:可作为集群范围内的缓存,不支持Hibernate的查询缓存。
- -JBossCache:可作为集群范围内的缓存,支持事务并发访问策略。支持Hibernate的查询缓存。
二级缓存的事务隔离级别
???transactional(事务型): ???????仅在受管理的环境中适用 ???????提供Repeatable Read事务隔离级别 ???????适用经常被读,很少修改的数据 ???????可以防止脏读和不可重复读的并发问题 ???????缓存支持事务,发生异常的时候,缓存也能够回滚 ???read-write(读写型); ???????提供Read Committed事务隔离级别 ???????在非集群的环境中适用 ???????适用经常被读,很少修改的数据 ???????可以防止脏读 ???????更新缓存的时候会锁定缓存中的数据 ???nonstrict-read-write(非严格读写型): ???????适用极少被修改,偶尔允许脏读的数据(两个事务同时修改数据的情况很少见) ???????不保证缓存和数据库中数据的一致性 ???????为缓存数据设置很短的过期时间,从而尽量避免脏读 ???????不锁定缓存中的数据 ???read-only(只读型): ???????适用从来不会被修改的数据(如参考数据) ???????在此模式下,如果对数据进行更新操作,会有异常 ???????事务隔离级别低,并发性能高 ???????在集群环境中也能完美运作
为了把这些第三方缓存插件集成到Hibernate中,Hibernate提供了org.hibernate.cache.CacheProvider接口
它是缓存插件与Hibernate之间的适配器。Hibernate为以上四个缓存插件提供了内置的适配器实现类。
如果需要使用其他的缓存插件,只需要为这个插件提供实现了接口的类即可。
使用二级缓存
配置二级缓存
???1.打开二级缓存
???2.选择需要使用的二级缓存的持久化类,设置二级缓存的并发访问策略。
???3.选择合适的缓存插件,配置缓存插件的配置文件。
我们演示使用EHCache插件
(1)先导包
???(2)在hibernate.cfg.xml中配置使用二级缓存
<property name="hibernate.cache.use_second_level_cache">true</property>
(3)配置使用EHcache的实现类
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.EhCacheRegionFactory</property>
Hibernate允许配置类和集合上设置二级缓存。还可以设置查询缓存。
(一)在类上设置二级缓存
在hibernate.cfg.xml的<mapping>元素后面配置
<!--usage设置隔离级别,class设置哪个类--> ???????<class-cache usage="read-only" class="com.cad.domain.Customer"/>
测试一下是否对象存到了二级缓存
public class Demo { ???????????private Session session; ???????????@Test ???????????public void test() { ???????????????//读取配置文件 ???????????????Configuration conf=new Configuration().configure(); ???????????????//根据配置创建factory ???????????????SessionFactory sessionfactory=conf.buildSessionFactory(); ???????????????session = sessionfactory.openSession(); ?????????????????Transaction ts=session.beginTransaction(); ?????????????????//获取对象,打印select语句 ????????????????Customer c1=session.get(Customer.class, 7); ????????????????//清除一级缓存 ????????????????session.clear(); ?????????????????//再获取对象,没有打印select语句,说明对象存放在了二级缓存中 ????????????????Customer c2=session.get(Customer.class, 7); ???????????????ts.commit(); ???????????????session.close(); ???????????????sessionfactory.close(); ???????????} ???????}
(二) 在集合上设置二级缓存区
要把集合中的对象也给设置二级缓存区。
<class-cache usage="read-only" class="com.cad.domain.Customer"/> ?????????<class-cache usage="read-only" class="com.cad.domain.Order"/> ??????????<!--collection设置对象中的集合--> ?????????<collection-cache usage="read-only" collection="com.cad.domain.Customer.orders"/>
测试一下
public class Demo { ???????????????private Session session; ???????????????@Test ???????????????public void test() { ???????????????????//读取配置文件 ???????????????????Configuration conf=new Configuration().configure(); ???????????????????//根据配置创建factory ???????????????????SessionFactory sessionfactory=conf.buildSessionFactory(); ???????????????????session = sessionfactory.openSession(); ?????????????????????Transaction ts=session.beginTransaction(); ?????????????????????//打印select语句 ????????????????????Customer c1=session.get(Customer.class, 7); ????????????????????for(com.cad.domain.Order o:c1.getOrders()){ ????????????????????????System.out.println(o.getName()); ????????????????????} ?????????????????????//清空缓冲区 ????????????????????session.clear(); ?????????????????????//再查找,不打印,说明集合中的对象都被放到了二级缓存中 ????????????????????Customer c2=session.get(Customer.class, 7); ????????????????????for(com.cad.domain.Order o:c2.getOrders()){ ????????????????????????System.out.println(o.getName()); ????????????????????} ???????????????????ts.commit(); ???????????????????session.close(); ???????????????????sessionfactory.close(); ???????????????} ???????????}
hibernate学习(缓存)
原文地址:https://www.cnblogs.com/qinjf/p/9060916.html