Hibernate_11

缓存的物理介质通常是内存,而永久性数据存储源的物理介质通常是硬盘和磁盘,应用程序读写内存的速度显然比读写硬盘的速度快。若缓存中存放的数据量非常大,也会用硬盘作为缓存的物理介质。缓存的实现不仅需要作为物理介质的硬件,同时还需要用于管理缓存的并发访问和过期等策略的软件。
SessionFactory的缓存可分为两类:内置缓存和外置缓存。SessionFactory的内置缓存是Hibernate自带的,不可卸载。通常在Hibernate的初始化阶段,Hibernate会把映射元数据和预定义SQL语句存放到SessionFactory的内置缓存中,映射元数据是映射文件中数据的复制,而预定义SQL语句是Hibernate根据映射元数据推导出来的。SessionFactory的内置缓存是只读缓存,应用程序不能修改换成那种的映射元数据和预定义的SQL语句。因此SessionFactory无需进行内置缓存与映射文件的同步。SessionFactory的外置缓存是一个可配置的缓存插件。在默认的情况下,SessionFactory不会启用这个缓存插件。外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或硬盘。
Hibernate的Session缓存中存放的数据是数据库中的数据的复制。在数据库中数据表现为关系数据形式,而在Session缓存中表现为相互关联的对象。在读写数据库时,Session会负责这种形式的数据映射。Session会在某些时间点会按照缓存中的数据来同步更新数据库,这一过程被称为清理缓存。

缓存的范围可以分为三类:事务范围、进程范围和集群范围。
事务范围:缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束,缓存也就结束生命周期。缓存的物理介质为内存。每个事务都有独自的缓存,缓存内的数据通常采用相互关联的对象形式。在同一个事务中,持久化类的每个对象具有唯一的OID。
进程范围:缓存被进程内的所有事务共享。这些事务有可能并发访问缓存,因此,必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖与进程的生命周期,当进程结束,缓存也就结束生命周期。进程范围的缓存可能会存放大量数据,它的物理介质可以是内存或硬盘。缓存内的数据既可以采用相互关联的对象模式,也可采用对象的散装数据形式。对象的散装数据有点类似于对象的序列化数据,但是把对象分解为散装数据的算法通常比对象的序列化的算法更快。
集群范围:在集群环境中,缓存被同一个机器或多个机器上的进程共享。缓存中的数据被复制到集群环境中的每个进程节点,进程之间通过远程通信来保证缓存中数据的一致性,缓存中的数据通常采用对象的散装数据形式。

进程范围或集群范围缓存,即第二级缓存,会出现并发问题。对第二级缓存可以设定以下4种类型的并发访问策略,每一种策略对应一种事务隔离级别。
事务型(Transactional):仅在受管理环境中适用。它提供Repeatable Read事务隔离级别。对于经常被读但是很少被修改的数据,可以采用这种隔离类型。可以防止脏读和不可重复读。
读写型(Read-write):提供Read Committed事务隔离级别。仅在非集群的环境中适用。对于经常被读但是很少被修改的数据,可以采用这种隔离类型。可以防止脏读这类并发问题。
非严格读写型(Nonstrict-read-write):不保证缓存与数据库中数据的一致性。若存在两个事务同时访问缓存中相同数据的可能,必须为该数据配置一个很短的数据过期时间,从而尽量避免脏读。对于极少被更改,并且允许偶尔脏读的数据,可以采用这种并发访问策略。
只读型(Read-only):对于从来不会被修改的数据,若参考数据,可以使用这种并发访问策略。
符合以下条件的数据适合存放到第二级缓存:
很少被修改的数据
不是很重要的数据,允许出现偶尔的并发问题
不会被并发访问的数据
参考数据
参考数据是指供应用参考的常量数据,有以下几个特点:他的实例的数目有限、每个实例会被许多其他类的实例引用、它的实例极少或者从来不会被修改。

缓存配置器(Cache Provider)用于把具体的缓存实现软件与Hibernate集成。

当应用程序调用Session的save()、update()、saveOrUpdate()、load()和get()方法及调用Query查询接口的list()、iterate()和filter()方法时,若在Session的缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。当清理缓存时,Hibernate会根据缓存中对象的状态变化来同步更新数据库。
Session为应用程序提供了两个管理缓存的方法:
evict(Object o):从缓存中清楚参数指定的持久化对象
clear():清空缓存中所有持久化对象
Session的evict()方法能够从缓存中清除特定的持久化对象,适用于一下情况:
不希望Session继续按照该对象的状态变化来同步更新数据库。
在批量更新或批量删除的场合,当更新或删除一个对象后,及时释放该对象占用的内存。

Hibernate的第二级缓存是进程或集群范围内的缓存,缓存中存放的是对象的散装数据。第二级缓存是可配置的插件,Hibernate允许选用以下类型的缓存插件:
EHCache:可作为进程范围内的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。
OpenSymphonyOSCache:可作为进程范围内的缓存,存放数据的物理介质可以是内存或硬盘,提供了更夫的缓存数据过期策略,对Hibernate的查询缓存提供了支持。
SwarmCache:可作为集群范围内的缓存,但不支持Hibernate的查询缓存。
JBossCache:可作为集群范围内的缓存,支持事务性并发访问策略,对Hibernate的查询缓存提供了支持。
各个缓存插件支持的并发访问策略
缓存插件 只读型 非严格读写型 读写型 事务型
EHCache 支持 支持 支持 否
OpenSymphonyOSCache 支持 支持 支持 否
SwarnCache 支持 支持 否 否
JBossCache 支持 否 否 否
为了把这些缓存插件集成到Hibernate中,Hibernate提供了org.hibernate.cacha.CacheProvider接口,它是缓存插件与Hibernate之间的适配器。Hibernate为以上缓存插件分别提供了内置的CacheProvider实现:
org.hibernate.cache.EhCacheProvider:EHCache插件的适配器
org.hibernate.cache.OSCacheProvider:OSCacheProvider插件的适配器
org.hibernate.cache.SwarmCacheProvider:SwarmCache插件的适配器
org.hibernate.cache.TreeCacheProvider:TreeCacheProvider插件的适配器

配置第二级缓存主要包含一下几个步骤:
1.选择需要使用第二级缓存的持久化类,设置它的第二级缓存的并发访问策略。Hibernate既允许在分散的各个映射文件中为持久化类设置第二级缓存,还允许在Hibernate的配置文件hibernate.cfg.xml中集中设置第二级缓存,后一种方式更有利于对于缓存相关的配置代码的维护。
2.选择合适的缓存插件,每一种缓存插件都有自带的配置文件,需要手动编辑该配置文件。EHCache缓存的配置文件为ehcache.xml,而JBossCache缓存的配置文件为treecache.xml。

Hibernate允许在类和集合的粒度上设置第二级缓存。在映射文件中,<class>和<set>元素下都有一个<cache>子元素,这个子元素用来配置第二级缓存。
<cache usage="transactional | read-write| nonstrict-read-write| read-only" //必须的 指定并发访问策略
region="RegionName" //可选的 指定第二级缓存的区域的名字。默认值为类或集合的名字
include="all| non-lazy" /> //可选的 是否加载延迟对象 默认为all
<cache>元素的属性
name:设置缓存的名字,取值为类的完整名字或者类的集合的名字
maxInMemory:设置基于内存的缓存可存放的对象的最大数目
eternal:若为true,表示对象永远不会过期,默认为false
timeToIdelSeconds:允许对象处于空闲状态的最长时间,以秒为单位。
timeToLiveSeconds:允许对象存在于缓存中的最长时间。
overflowToDisk:若为true,表示当基于内存的缓存中的对象数目达到了maxInMemory界限,会把溢出的对象写到基于硬盘的缓存中。

EHCache适用于Hibernate应用发布在单个机器的场合。若要把应用发布到多台机器中,可以用JBossCache作为Hibernate的第二级缓存。
1.在Hibernate配置文件中设置JBossCache适配器,并且为需要使用的第二级缓存的类和集合设置缓存的并发访问策略。
2.编辑JBossCache自身的配置文件,名为treecache.xml。这个文件必须存放于应用的classpath中。对于集群环境中的每个节点,都必须提供单独的

treecache.xml文件。

SessionFactory的evict()方法用于清除对象的散装数据,SessionFactory的getStatistics()方法用于查看第二级缓存中的数据:
Map cacheEntries = sessionFactory.getStatistics().getSecondLevelCacheStatistics(regionName).getEntries();

Session与第二级缓存交互有5种模式,分别用org.hibernate.CacheMode类的5个静态常量来表示:
CacheMode.NORMAL:正常模式(默认),Session会从第二级缓存中读取数据,也会向其中写入数据
CacheMode.IGNORAL:忽略模式,Session不会从第二级缓存中读取数据,也不会向其中写入数据
CacheMode.GET:读取模式,Session会从第二级缓存中读取数据,但不会向其中写入数据
CacheMode.PUT:写入模式,Session不会从第二级缓存中读取数据,但会向其中写入数据
CacheMode.REFRESH:刷新模式,Session不会从第二级缓存中读取数据,但会向其中写入数据。与PUT的区别是:REFRESH会忽略Hibernate配置文件中的

hibernate.cache.use_minimal_puts属性,强制刷新第二级缓存中的所有数据。

Hibernate提供了3种管理Session对象的方法:
1.Session对象的生命周期与本地线程绑定
2.Session对象的生命周期与JTA事务绑定
3.Hibernate委托程序管理Session对象的生命周期

在Hibernate的配置文件中,hibernate.current_session_context_class属性用于指定Session管理方式,可选值包括:thread:Session对象的生命周期与本地线程绑定;jta:Session的生命周期与JTA事务绑定;managed:Hibernate委托程序来管理Session对象的生命周期。

Hibernate按照以下规则把Session与本地线程绑定:
当一个线程A第一次调用SessionFactory对象的getCurrentSession()方法时,该方法会创建一个新的Session对象,把它与线程A绑定,并将SessionA对象返回。
接下来,当线程A再次调用SessionFactory对象的getCurrentSession()方法时,该方法始终返回SessionA对象。
当线程A提交与SessionA对象关联的事务时,Hibernate会自动清理SessionA对象的缓存,然后在提交事务后,关闭SessionA对象。此外,若当线程A撤销与SessionA对象关联的事务时,也会自动关闭SessionA对象。
若线程A再次调用SessionFactory对象的getCurrentSession()方法时,该方法又会创建一个新的Session对象,把它与线程A绑定。

Hibernate按照以下规则把Session与JTA绑定:
程序先通过UserTransaction接口声明开始一个JTA事务。接下来当程序第一次调用SessionFactory对象的getCurrentSession()方法时,该方法会创建一个新的Session对象,把它与当前的JTA事务绑定,并将SessionA对象返回。
接下来,当程序多次调用SessionFactory对象的getCurrentSession()方法时,该方法始终返回与当前JTA事务绑定的SessionA对象。
当程序提交当前JTA事务时,Hibernate会自动清理SessionA对象的缓存,然后在事务提交后,关闭SessionA对象。此外,若当程序撤销当前JTA事务时,也会自动关闭SessionA对象。

Hibernate提供两种实现对话的方式:
使用游离对象:一个对话包括多个短事务,并且每个短事务对应一个Session对象。事务之间通过游离对象传递业务数据
使用手工清理缓存模式下的Session对象:一个对话包括多个短事务,并且整个对话对应一个Session对象

采用手工清理缓存模式下的Session,整个对话由多个短事务构成,并且整个对话对应一个Session对象,这个Session对象的生命周期由程序自主管理。程序处理的主要流程如下:
创建完Session对象后,立即调用session.setFlushMode(FlushMode.MANUAL)方法,把缓存模式设为手工清理模式。
在手工清理模式下,当程序声明提交事务时,Hibernate不会自动清理缓存,因此不会同步更新数据库,不过,Hibernate会自动释放Session对象占用的数据库连接。
当程序声明开始事务时,假如当前Session对象不占用数据库连接,Hibernate会自动为它分配数据库连接
只有当对话块结束,在提交最后一个事务之前,程序需要调用session.flush()方法手工清理缓存,根据缓存中对象的变化去同步更新数据库。

在手工清理Session缓存的模式下,至哟当程序调用Session的flush()方法时才会清理缓存。但通过Session的save()方法执行插入操作时,若对象标识符OID生成策略为identity或select,那么save()方法会立即向数据库插入数据,然后生成对象标识符。

Hibernate委托程序来管理Session:
把Hibernate配置文件的hibernate.current_session_context_class属性设为managed。
当程序创建Session对象后或声明开始一个事务前,调用org.hibernate.context.ManagedContext类的bind()方法,该方法把Session对象与当前线程绑定。
在一个事务中,程序可通过SessionFactory的getCurrentSession()方法来获得当前Session对象。
当程序提交对话中的一个事务之前,调用org.hibernate.context.ManagedSessionContext类的unbind()方法,该方法解除当前Session对象与当前线程的绑定。

org.hibernate.context.ManagedSessionContext类提供了一下静态方法:
bind(Session session):把Session和当前线程绑定
unbind(SessionFactory factory):解除Session对象与当前的绑定
hasBind(SessionFactory factory):判断是否存在与当前线程绑定的Session对象。


本来想今天下午把Hibernate全部整理完的,结果今天不知抽了什么风,颈椎酸疼,四肢无力,还恶心想吐...真是的,才工作一个月就有颈椎病,这以后还怎么继续愉快的玩耍...真是郁闷............
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值