Hibernate之缓存管理

来源:互联网 发布:淘宝低价引流违规吗 编辑:程序博客网 时间:2024/04/29 21:43

一,Hibernate缓存介绍

Hibernate的Session缓存是一块内存空间,缓存中存放了相互关联的持久化java对象,

Session通过持久化对象的状态变化来同步更新数据库。

Session的缓存是内置的,不能被卸载,也被称为Hibernate的第一级缓存,此外,

SessionFactory有一个内置缓存和一个外置缓存,而外置缓存是可插拔的缓存插件,

也被称为二级缓存。二级缓存实现复杂,需实现并发访问和数据过期策略等扥。

二,缓存的基本原理

缓存是计算机领域非常通用的概念,它介于应用程序和永久性数据存储源(数据库,硬盘上存储的文件)之间,

作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用程序的运行性能。为保证数据的一致性,

缓存中的数据是永久性数据存储源的拷贝,应用程序每次都从缓存中读取数据,只有在发生改变时才从数据库

读取数据,或者根据缓存中的数据同步更新数据源。下图显示缓存在软件系统中的位置:

缓存的物理介质是内存,而永久性数据存储源的介质是硬盘或磁盘,应用程序读写内存的数据比读写硬盘的速度快。

缓存数据量太大时,也会用硬盘作为缓存的物理介质。缓存的实现不仅需要作为物理介质的硬件,同时还需要用于,

管理缓存并发访问和过期数据策略的软件。所以,缓存是通过软件和硬件共同实现的。


以下为关于缓存的举例解释:

Hibernate的Session中缓存的数据是数据库中拷贝的数据。在数据库中数据表现为关系形式,而在Session中

表现为相互关联的java对象。在读写数据时,Session会负责这两种形式的数据映射。Session会根据缓存中持久化

对象来同步更新数据库,这个过程称为清理缓存。也就是Session调用close()方法时。


三,Hibernate一级缓存和二级缓存区别理解

持久化层缓存的范围决定缓存的生命周期和能被谁访问:

(1)事务范围

缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存的生命周期也就结束。

缓存的物理介质为内存。这里的事务可以指数据库事务或应用程序事务。每个事务都有独立的缓存,缓存中的

数据采用相互关联的对象形式。

(2)进程范围

缓存被进程范围内的所有事务共享。这些事务可能出现并发访问缓存,有必要采取事务隔离机制。

缓存的生命周期依赖于进程的生命周期,当进程结束时,缓存的生命周期也就结束。缓存中可能

存放大量的数据,物理介质可以用内存或硬盘。缓存数据可以采用相互关联的对象形式,也可以

采用对象散装的形式。一般采用对象散装形式,避免并发出现死锁。

(3)群集范围

在群集环境中缓存被一个或多个机器的一个或多个进程共享。缓存中的数据被复制到群集环境中

的每个进程节点,进程间通过远程通信来保证数据的一致性,缓存中的数据通常采用对象的散装

数据形式。

持久化层可以提供多种范围的缓存。当从事务范围缓存中没有查询到相应数据时,可以从进程范围

或群集范围缓存中查询,如果还没查到,只能去查询数据库了。事务范围的缓存是第一级缓存,一般

情况下是必须的缓存,而进程范围和群集范围的缓存是可选的缓存。群集范围的缓存慎用。


持久化层缓存的并发访问策略:

并发问题归为5类,使用事务隔离或锁进行控制。在先前的事务和并发控制中有讲解并发归类和隔离级别。

参考:http://blog.csdn.net/yhl_jxy/article/details/50897894

对于二级缓存设置以下四种类型的访问策略,每一种策略对应一种事务隔离级别:

(1)事务型

它提供了Repeatable Read事务隔离级别。它可以防止脏读和不可重复读这类的并发问题

(2)读写型

读写策略提供了“read committed"数据库隔离级别。对于经常被读但很少修改的数据可以采用这种策略,它可以防止读脏数据

(3)非严格读写型

非严格读写不能保证缓存与数据库中数据的一致性,如果存在两个事务并发地访问缓存数据的可能,

则应该为该数据配置一个很短的过期时间,以减少读脏数据的可能。对于极少被修改,并且可以容忍偶尔脏读的数据可以采用这种并发策略

(4)只读型

对于永远不会被修改的数据可以采用这种并发访问策略,它的并发性能是最高的。但必须保证数据不会被修改,否则就会出错

以下数据适合存在二级缓存中:

很少被修改;

不是很重要的数据,允许偶尔出现并发;

不会被并发访问的数据;

参考数据;

四,Hibernate二级缓存的结构

Hibernate提供了两级缓存:

第一级缓存:一级缓存是Session的缓存。由于Session的生命周期通常对应一个数据库事务或应用事务,所以,一级缓存是事务

范围的缓存。一级缓存是必需的,不能被卸载,也不允许被卸载。在一级缓存中,持久化类的每一个实例都有一个惟一的OID。

第二级缓存:

二级缓存是一个可以插拔的缓存插件,它由SessionFactory负责管理。SessionFactory的生命周期对应整个应用程序的进程,

所以,二级缓存是进程范围或群集范围的缓存。这个缓存中存放的是对象的散装数据。一个进程中可能包含多个线程,多线程

可能访问相同的缓存,可能触发并发问题,针对这种情况,需要采取上面介绍的‘持久化层的缓存并发策略’,这些策略为缓存数据

提供不同隔离级别的事务隔离机制。缓存适配器用于把具体的软件与Hibernate集成。第二级缓存是可选的,可以在每个类或每个

集合粒度上配置二级缓存。Hibernate有一个查询缓存,依赖于二级缓存。

关于一二级缓存结构简图如下:


五,Hibernate第一级缓存管理

当应用程序调用Session的save(),update(),saveOrUpdate(),load(),get()以及调用Query,Criteria查询接口的list(),iterator()或filter()

时,如果在Session缓存中还不存在相应的对象,Hibernate就会把持久化的对象加入第一级缓存中。当清理缓存时,Hibernate

会根据缓存中对象的状态变化来同步更新数据库。

Session为应用程序提供了两个管理缓存的方法:

evict(Object o):从Session缓存中清理掉参数指定的缓存对象

clear():清理掉Session中所有的持久化对象

evict(Object o)的适用情况:

不希望Session继续按照该对象的变化状态来同步更新数据库;

在批量更新和删除数据时,当更新或删除一个数据后,及时释放该对象占用的内存。

evict(Object o)实例:

对象映射文件配置成立即检索,lazy="false"

package com.lanhuigu.hibernate.test;import org.hibernate.LockMode;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.Transaction;import org.hibernate.cfg.Configuration;import com.lanhuigu.hibernate.entity.Customer;public class TestHibernate {public static void main(String[] args) {Configuration configuration = new Configuration().configure();SessionFactory sessionFactory = configuration.buildSessionFactory();Session session = sessionFactory.openSession();Transaction tr = session.beginTransaction();//加载一个OID为1的对象Customer customer1 = (Customer) session.get(Customer.class, new Long(1));//使用evict(Object o)从第一级缓存中移出session.evict(customer1);//加载同样OID的对象,根据OID去缓存中查询,没有查到缓存去数据库查询Customer customer2 = (Customer) session.get(Customer.class, new Long(1));//将两个对象进行比较System.out.println(customer1 == customer2);//从执行结果为false,可以知道customer1被清除,customer2是新创建的tr.commit();session.close();}}
控制台输出:

Hibernate: select customer0_.ID as ID1_0_0_, customer0_.NAME as NAME2_0_0_, customer0_.EMAIL as EMAIL3_0_0_, customer0_.PASSWORD as PASSWORD4_0_0_, customer0_.PHONE as PHONE5_0_0_, customer0_.ADDRESS as ADDRESS6_0_0_, customer0_.SEX as SEX7_0_0_, customer0_.IS_MARRIED as IS8_0_0_, customer0_.DESCRIPTION as DESCRIPT9_0_0_, customer0_.IMAGE as IMAGE10_0_0_, customer0_.BIRTHDAY as BIRTHDA11_0_0_, customer0_.REGISTERED_TIME as REGISTE12_0_0_, customer0_.HOME_PROVINCE as HOME13_0_0_, customer0_.HOME_CITY as HOME14_0_0_, customer0_.HOME_STREET as HOME15_0_0_, customer0_.HOME_ZIPCODE as HOME16_0_0_, customer0_.COMP_PROVINCE as COMP17_0_0_, customer0_.COMP_CITY as COMP18_0_0_, customer0_.COMP_STREET as COMP19_0_0_, customer0_.COMP_ZIPCODE as COMP20_0_0_ from CUSTOMERS customer0_ where customer0_.ID=?Hibernate: select customer0_.ID as ID1_0_0_, customer0_.NAME as NAME2_0_0_, customer0_.EMAIL as EMAIL3_0_0_, customer0_.PASSWORD as PASSWORD4_0_0_, customer0_.PHONE as PHONE5_0_0_, customer0_.ADDRESS as ADDRESS6_0_0_, customer0_.SEX as SEX7_0_0_, customer0_.IS_MARRIED as IS8_0_0_, customer0_.DESCRIPTION as DESCRIPT9_0_0_, customer0_.IMAGE as IMAGE10_0_0_, customer0_.BIRTHDAY as BIRTHDA11_0_0_, customer0_.REGISTERED_TIME as REGISTE12_0_0_, customer0_.HOME_PROVINCE as HOME13_0_0_, customer0_.HOME_CITY as HOME14_0_0_, customer0_.HOME_STREET as HOME15_0_0_, customer0_.HOME_ZIPCODE as HOME16_0_0_, customer0_.COMP_PROVINCE as COMP17_0_0_, customer0_.COMP_CITY as COMP18_0_0_, customer0_.COMP_STREET as COMP19_0_0_, customer0_.COMP_ZIPCODE as COMP20_0_0_ from CUSTOMERS customer0_ where customer0_.ID=?false
总结:

从执行结果可以看出,去数据库访问了两次,有两条select,同时两个对象不等,说明customer1被从缓存中清除,customer2是新建的。

如果将session.evict(customer1);注释掉,可以看到控制台打印一条sql,比较结果为true,去掉evict后的控制台输出结果:

Hibernate: select customer0_.ID as ID1_0_0_, customer0_.NAME as NAME2_0_0_, customer0_.EMAIL as EMAIL3_0_0_, customer0_.PASSWORD as PASSWORD4_0_0_, customer0_.PHONE as PHONE5_0_0_, customer0_.ADDRESS as ADDRESS6_0_0_, customer0_.SEX as SEX7_0_0_, customer0_.IS_MARRIED as IS8_0_0_, customer0_.DESCRIPTION as DESCRIPT9_0_0_, customer0_.IMAGE as IMAGE10_0_0_, customer0_.BIRTHDAY as BIRTHDA11_0_0_, customer0_.REGISTERED_TIME as REGISTE12_0_0_, customer0_.HOME_PROVINCE as HOME13_0_0_, customer0_.HOME_CITY as HOME14_0_0_, customer0_.HOME_STREET as HOME15_0_0_, customer0_.HOME_ZIPCODE as HOME16_0_0_, customer0_.COMP_PROVINCE as COMP17_0_0_, customer0_.COMP_CITY as COMP18_0_0_, customer0_.COMP_STREET as COMP19_0_0_, customer0_.COMP_ZIPCODE as COMP20_0_0_ from CUSTOMERS customer0_ where customer0_.ID=?true
事实证明,evict(Object o)将持久化对象从一级缓存中清除,如果映射文件中配置了级联关系,会把相关联的对象缓存清除。

clear()使用实例:

package com.lanhuigu.hibernate.test;import org.hibernate.LockMode;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.Transaction;import org.hibernate.cfg.Configuration;import com.lanhuigu.hibernate.entity.Customer;public class TestHibernate {public static void main(String[] args) {Configuration configuration = new Configuration().configure();SessionFactory sessionFactory = configuration.buildSessionFactory();Session session = sessionFactory.openSession();Transaction tr = session.beginTransaction();//加载一个OID为1的对象Customer customer1 = (Customer) session.get(Customer.class, new Long(1));//加载同样OID为2的对象Customer customer2 = (Customer) session.get(Customer.class, new Long(2));//使用clear()清除所有缓存session.clear();//从新加载Customer customer3 = (Customer) session.get(Customer.class, new Long(1));Customer customer4 = (Customer) session.get(Customer.class, new Long(2));tr.commit();session.close();}}
控制台输出结果:

Hibernate: select customer0_.ID as ID1_0_0_, customer0_.NAME as NAME2_0_0_, customer0_.EMAIL as EMAIL3_0_0_, customer0_.PASSWORD as PASSWORD4_0_0_, customer0_.PHONE as PHONE5_0_0_, customer0_.ADDRESS as ADDRESS6_0_0_, customer0_.SEX as SEX7_0_0_, customer0_.IS_MARRIED as IS8_0_0_, customer0_.DESCRIPTION as DESCRIPT9_0_0_, customer0_.IMAGE as IMAGE10_0_0_, customer0_.BIRTHDAY as BIRTHDA11_0_0_, customer0_.REGISTERED_TIME as REGISTE12_0_0_, customer0_.HOME_PROVINCE as HOME13_0_0_, customer0_.HOME_CITY as HOME14_0_0_, customer0_.HOME_STREET as HOME15_0_0_, customer0_.HOME_ZIPCODE as HOME16_0_0_, customer0_.COMP_PROVINCE as COMP17_0_0_, customer0_.COMP_CITY as COMP18_0_0_, customer0_.COMP_STREET as COMP19_0_0_, customer0_.COMP_ZIPCODE as COMP20_0_0_ from CUSTOMERS customer0_ where customer0_.ID=?Hibernate: select customer0_.ID as ID1_0_0_, customer0_.NAME as NAME2_0_0_, customer0_.EMAIL as EMAIL3_0_0_, customer0_.PASSWORD as PASSWORD4_0_0_, customer0_.PHONE as PHONE5_0_0_, customer0_.ADDRESS as ADDRESS6_0_0_, customer0_.SEX as SEX7_0_0_, customer0_.IS_MARRIED as IS8_0_0_, customer0_.DESCRIPTION as DESCRIPT9_0_0_, customer0_.IMAGE as IMAGE10_0_0_, customer0_.BIRTHDAY as BIRTHDA11_0_0_, customer0_.REGISTERED_TIME as REGISTE12_0_0_, customer0_.HOME_PROVINCE as HOME13_0_0_, customer0_.HOME_CITY as HOME14_0_0_, customer0_.HOME_STREET as HOME15_0_0_, customer0_.HOME_ZIPCODE as HOME16_0_0_, customer0_.COMP_PROVINCE as COMP17_0_0_, customer0_.COMP_CITY as COMP18_0_0_, customer0_.COMP_STREET as COMP19_0_0_, customer0_.COMP_ZIPCODE as COMP20_0_0_ from CUSTOMERS customer0_ where customer0_.ID=?Hibernate: select customer0_.ID as ID1_0_0_, customer0_.NAME as NAME2_0_0_, customer0_.EMAIL as EMAIL3_0_0_, customer0_.PASSWORD as PASSWORD4_0_0_, customer0_.PHONE as PHONE5_0_0_, customer0_.ADDRESS as ADDRESS6_0_0_, customer0_.SEX as SEX7_0_0_, customer0_.IS_MARRIED as IS8_0_0_, customer0_.DESCRIPTION as DESCRIPT9_0_0_, customer0_.IMAGE as IMAGE10_0_0_, customer0_.BIRTHDAY as BIRTHDA11_0_0_, customer0_.REGISTERED_TIME as REGISTE12_0_0_, customer0_.HOME_PROVINCE as HOME13_0_0_, customer0_.HOME_CITY as HOME14_0_0_, customer0_.HOME_STREET as HOME15_0_0_, customer0_.HOME_ZIPCODE as HOME16_0_0_, customer0_.COMP_PROVINCE as COMP17_0_0_, customer0_.COMP_CITY as COMP18_0_0_, customer0_.COMP_STREET as COMP19_0_0_, customer0_.COMP_ZIPCODE as COMP20_0_0_ from CUSTOMERS customer0_ where customer0_.ID=?Hibernate: select customer0_.ID as ID1_0_0_, customer0_.NAME as NAME2_0_0_, customer0_.EMAIL as EMAIL3_0_0_, customer0_.PASSWORD as PASSWORD4_0_0_, customer0_.PHONE as PHONE5_0_0_, customer0_.ADDRESS as ADDRESS6_0_0_, customer0_.SEX as SEX7_0_0_, customer0_.IS_MARRIED as IS8_0_0_, customer0_.DESCRIPTION as DESCRIPT9_0_0_, customer0_.IMAGE as IMAGE10_0_0_, customer0_.BIRTHDAY as BIRTHDA11_0_0_, customer0_.REGISTERED_TIME as REGISTE12_0_0_, customer0_.HOME_PROVINCE as HOME13_0_0_, customer0_.HOME_CITY as HOME14_0_0_, customer0_.HOME_STREET as HOME15_0_0_, customer0_.HOME_ZIPCODE as HOME16_0_0_, customer0_.COMP_PROVINCE as COMP17_0_0_, customer0_.COMP_CITY as COMP18_0_0_, customer0_.COMP_STREET as COMP19_0_0_, customer0_.COMP_ZIPCODE as COMP20_0_0_ from CUSTOMERS customer0_ where customer0_.ID=?
从4条sql可以知道访问了4次数据库,去掉session.clear()可以看到执行2次。说明session中的缓存被clear()清除掉。


一级缓存管理总结:

一般不提倡用evict(),clear()管理缓存,占用内存空间大,一般用检索策略,投影查询,集合过滤等合理控制缓存,节省内存开销。

六,Hibernate第二级缓存管理

Hibernate的第二级缓存是进程范围或群集范围内的缓存,缓存中存放的是对象的散装数据。第二级缓存是可以配置的插件,Hibernate

允许选用以下类型的缓存插件:

(1)EHCache:是一个纯java进程范围内的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate提供了查询缓存的支持。

(2)OpenSymphony OSCache:可以作为进程范围内的缓存,存放数据的物理介质可以是内存或硬盘,提供了丰富的缓存数据

过期策略,对Hibernate提供了查询缓存的支持。

(3)SwarmCache:可作为群集范围内的缓存,但是,不能支持Hibernate的查询缓存。

(4)JBosssCache:可作为群集范围内的缓存,支持事务型并发访问策略,对Hibernate提供了查询缓存的支持。


各个缓存插件支持的并发访问策略如下:

Hibernate使用缓存插件,靠Hibernate的缓存插件适配器提供对应的实现类,关系图如下:

二级缓存配置的步骤:

(1)选择需要使用二级缓存的持久化类,设置它的命名缓存的并发访问策略。Hibernate既允许在分散的各个映射文件中为持久化类

设置二级缓存,还允许在Hibernate的配置文件hibernate.cfg.xml中集中设置二级缓存,后一种方式有利于和缓存相关的配置代码的维护。

(2)选择合适的缓存插件,每一种缓存插件都有自带的配置文件,需要手动编辑缓存文件。EHCache的缓存文件为encache.xml,JBossCache

的缓存配置文件为treecache.xml。在缓存的配置文件中需要为每个命名缓存设置数据过期策略。


下一篇单独讲解二级缓存的配置使用......


0 0
原创粉丝点击