一、Hibernate缓存简介
1、Hibernate缓存概述
Hibernate缓存机制对Hibernate的性能发挥一直处于一个极其重要的作用,它是持久层性能提升的关键。
Hibernate缓存介于Hibernate应用和数据库之间,缓存中存放了数据库数据的拷贝。 其作用是减少访问数据库的频率,从而提高应用的运行性能。
Hibernate在进行读取数据的时候,根据缓存机制在相应的缓存中查询,如果在缓存中找到了需要的数据(我们把这称做“缓存命中”),则就直接把命中的数据作为结果加以利用,避免的了建立数据库查询的性能损耗。
2、Hibernate缓存分类
Hibernate提供了两级缓存:
一级缓存: Session级别的缓存
二级缓存: SessionFactory级别的全局缓存
Hibernate的这两级缓存都位于持久化层,存放的都是数据库数据的拷贝。
那么它们之间的区别是什么呢?
为了理解二者的区别,需要深入理解持久化层的缓存的一个特性:缓存的范围。
3、
缓存的范围(1)
缓存的范围决定了缓存的生命周期以及可以被谁访问。缓存的范围分为三类。
事务范围 缓存只能被当前事务访问。缓存的生命周期依赖于事务的生命周期,当事务结束时,缓存也就结束生命周期。缓存的介质是内存。事务可以是数据库事务或者应用事务,每个事务都有独自的缓存,缓存内的数据通常采用相互关联的的对象形式。一级缓存就属于事务范围。
缓存的范围(2)
进程范围 缓存被进程范围内的所有事务共享。这些事务有可能并发访问缓存,因此必须对缓存采取必要的事务隔离机制。缓存的生命周期依赖于进程的生命周期,进程结束时,缓存也就结束了生命周期。它的物理介质可以是内存或硬盘。
缓存的范围(3)
集群范围 在集群环境中,缓存被一个机器或者多个机器的进程共享。缓存中的数据被复制到集群环境中的每个进程节点,进程间通过远程通信来保证缓存中的数据的一致性,缓存中的数据通常采用对象的松散数据形式。
持久化层的第二级缓存就存在于进程范围或集群范围。
二、Hibernate一级缓存(Session缓存)
2-1:理解一级缓存(1)
Session具有一个缓存,是一块内存空间,在这个内存空间存放了相互关联的java对象,这种位于Session缓存内的对象也被称为持久化对象,Session负责根据持久化对象的状态变化来同步更新数据库。
Session的缓存是内置的,不能被卸除的,也被称为Hibernate的第一级缓存。在正常的情况下一级缓存是由Hibernate自动维护的,无需人工干预。
2-1:理解一级缓存(2)
(1)当应用程序调用Session接口的save()、update()、saveOrUpdate()时,如果Session缓存中还不存在相应的对象,Hibernate就会把该对象加入到第一级缓存中。
(2)当调用Session接口的load()、get()以及Query查询接口的list()、iterator()方法时,
如果Session缓存中存在相
应的对象,就不需要到数据
库中检索。
(3)当调用Session的close()时,
Session缓存就被清空。
2-2:Session接口的用法(1)
Session接口是Hibernate向应用程序提供的操纵数据库的最主要的接口,它提供了基本的保存、更新、删除和加载等方法。
Java对象在Hibernate持久化层的状态:
- 临时状态:刚用new语句创建,还没有被持久化,并且不处于session缓存中(处于临时状态的对象成为临时对象)
- 持久化状态:已经被持久化,并且加入到session缓存中。处于持久化状态的对象称为持久化对象
- 删除状态:不再处于session缓存中,并且session已经计划将其从数据库中删除
- 游离状态:已经被持久化,但不再处于session缓存中。处于游离状态的对象称为游离对象
2-2:Session接口的用法(2)
save()和persist()
save()方法把一个临时对象加入到Session缓存中,并持久化该临时对象,计划执行一个insert语句。
persist()和save()方法类似,也能把把一个临时对象转变为持久化对象。
区别:
(1)persist() 是在Hibernate3版本中才出现。在使用代理主键的情况下persist()方法不保证立即为持久化对象的ID赋值,而是有可能在Session清理缓存时才为ID赋值。
(2)如果在事务边界以外调用persist()方法,那么该方法不会计划执行insert语句,这可以提高负责长时间运行事务的程序的健壮性。
而save()方法,不管是在事务边界以外或以内调用它,都会计划执行insert语句。
2-2:Session接口的用法(3)
get()和load()
试图从数据库加载一个实体对象时,Session先判断对象是否存在,如果存在就不到数据库中检索。返回的对象都位于Session缓存中,接下来修改了持久化对象的属性后,当Session清理缓存时,会根据持久化对象的属性变化来同步更新数据库。
区别:
(1)当数据库中不存在与OID对应的记录时,load()方法抛出ObjectNotFoundException异常,而get()方法返回null.
(2)两者采用不同的检索策略。
默认情况下,load()方法采用延迟检索策略(Hibernate不会执行select语句,仅返回实体类的代理类实例,占用内存很少);而get()采用立即检索策略(Hibernate会立即执行select语句)。
使用场合:
(1)如果加载一个对象的目的是为了访问它的各个属性,可以用get();
(2)如果加载一个对象的目的是为了删除它,或者建立与别的对象的关联关系,可以用load() ;
update() 和merge()
update()方法把游离对象加入当前Session缓存中,计划执行update语句。
当update()方法关联一个游离对象时,如果session缓存中已经有一个同类型且ID相同的持久化对象,那么update()方法会抛出NonUniqueException异常.
当update()方法关联一个持久化对象时,该方法不起作用。
merge() 方法能够把游离对象的属性复制到一个持久化对象中。
2-2:Session接口的用法(4)
2-2:Session接口的用法(4)
- saveOrUpdate()
同时包含了save()和update()方法的功能。
如果传入的是临时对象,就调用save()方法;
如果传入的是游离对象,就调用update()方法
如果传入的是持久化对象,就直接返回。
- delete() :计划执行一个delete语句,把对象从Session缓存中删除。
- close() :清空session缓存。
2-3:Session清理缓存
清理缓存是指Session按照缓存中对象的属性变化来同步更新数据库。
Session在清理缓存的时候会自动进行脏检查(dirty-check),如果发现Session缓存中的对象与数据库中相应的记录不一致,就会同步数据库。
S
ession是如何进行脏检查的呢?
当一个对象被加入到Session缓存时,Session 会为该对象的值类型的属性复制 一份快照。当Session清理缓存的时候,会进行脏检查,即比较对象的当前属性与它的快照,来判断对象的属性是否发生变化,如果发生变化,就称这个对象是“脏对象”,Session会根据脏对象的最新属性来执行相关的SQL语句,从而同步更新数据库。
2-4:Session脏检查及同步数据库的过程
2-5:Session何时清理缓存(1)
session缓存中对象的属性每次发生变化,Session不会立即清理缓存及执行相关的update语句,而是在特定的时间点才清理缓存,这使得Session能够把几条相关的sql语句合并为一条sql语句,以便减少访问数据库的次数。
以下代码对dept对象的deptName属性修改了两次:
2-5:Session何时清理缓存(2)
session会在下面的时间点清理缓存:
(1)当应用程序调用org.hibernate.Transaction的commit()方法的时候 commit()方法先清理缓存,然后再向数据库提交事务。
(2) 当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生变化,就会先清理缓存,使得Session缓存与数据库进行了同步,从而保证查询结果返回的是正确的数据。
(3) 当应用程序显式调用Session的flush()方法的时候
2-6:Sesion缓存的管理
第一级缓存在正常的情况下是由Hibernate自动维护的。
在特殊的情况下需要我们进行手动维护,Hibernate就提供了两个管理Session缓存的方法:
(1)Session.evict(Object o)
将某个特定的对象从缓存中清除,
使用此方法有两种适用情形,一是在特定的操作(如批量处理),需要及时释放对象占用的内存。二是不希望当前Session继续运用此对象的状态变化来同步更新数据库。
(2)Session.clear()
清除缓存中的所有持久化对象。
1、在多数情况下并不提倡通过evit()和clear()来管理一级缓存。
2、管理一级缓存最有效的方法是采用合理的检索策略和检索方式来节省内存的开销。
2-7:Session缓存的作用(1)
2-7:Session缓存的作用(2)
2-7:Session缓存的作用(3)
(2)保证缓存中的对象与数据库中的相关记录保持同步。
当缓存中的持久化对象的属性发生变化时,Session并不会立即执行相关的update语句,