对
于Hibernate
这类
ORM
而言,
缓
存
显
的尤
为
重要,
它
是持久
层
性能提升的
关键
.
简单来讲
Hibernate
就是
对
JDBC
进
行封装,以
实现内
部
状态
的管理,OR
关
系的映射等,但
随
之
带来
的就是
数
据
访问
效率的降低,和性能的下降,而
缓
存就是弥
补这
一缺点的重要方法.
缓 存就是 数 据 库数 据在 内 存中的 临时 容器,包括 数 据 库数 据在 内 存中的 临时 拷 贝 , 它 位于 数 据 库与数 据 库访问层 中 间 .ORM 在 查询数 据 时 首先 会 根据自身的 缓 存管理策略,在 缓 存中 查 找相 关数 据,如 发现 所需的 数 据, 则 直接 将 此 数 据作 为结 果加以利用, 从 而避免了 数 据 库调 用性能的 开销 . 而相 对内 存操作而言, 数 据 库调 用是一 个 代价高 昂 的 过 程.
一般 来讲 ORM 中的 缓 存分 为 以下几 类 :
1. 事 务级缓 存:即在 当 前事 务 范 围内 的 数 据 缓 存.就Hibernate 来讲 , 事 务级缓 存是基于Session的生命周期 实现 的,每 个 Session 内 部 会 存在一 个数 据 缓 存, 它随 着Session的 创 建而存在, 随 着Session的 销 毁而 灭 亡,因此也 称为 Session Level Cache.
2. 应 用 级缓 存:即在某 个应 用中或 应 用中某 个独 立 数 据 库访问 子集中的共享 缓 存,此 缓 存可由多 个 事 务 共享( 数 据 库 事 务 或 应 用事 务 ), 事 务 之 间 的 缓 存共享策略 与应 用的事 务 隔离机制密切相 关 . 在Hibernate中, 应 用 级缓 存由SessionFactory 实现 , 所有由一 个 SessionFactory 创 建的Session 实 例共享此 缓 存,因此也 称为 SessionFactory Level Cache.
3. 分布式 缓 存:即在多 个应 用 实 例,多 个 JVM 间 共享的 缓 存策略.分布式 缓 存由多 个应 用 级缓 存 实 例 组 成,通 过 某 种远 程机制(RMI,JMS) 实现 各 个缓 存 实 例 间 的 数 据同步,任何一 个实 例的 数 据修改, 将导 致整 个 集群 间 的 数 据 状态 同步.
Hibernate 数 据 缓 存:
1. 内 部 缓 存(Session Level Cache也 称 一 级缓 存):
举 例 说 明:
java 代 码
public class Test {
public void get(){
Session session = HibernateSessionFactory.getSession();
TUser t = (TUser)session.get("hibernate.TUser", 2);
System.out.println(t.getName());
session.close();
}
}
进 行 测试 : 在控制台打印出一 条 SQL 语 句:Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.*** as ***0_0_ from test.t_user tuser0_ where tuser0_.id=? 说 明 进 行了一次 数 据 库 的 调 用.
代 码 更改如下:
public class Test {
public void get(){
Session session = HibernateSessionFactory.getSession();
TUser t = (TUser)session.get("hibernate.TUser", 2);
System.out.println(t.getName());
TUser tt = (TUser)session.get("hibernate.TUser", 2);
System.out.println(tt.getName());
session.close();
}
}
再 进 行 测试 : 进 行了 两 次 查询 , 控制台仍然只打出一 条 SQL 语 句:Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.*** as ***0_0_ from test.t_user tuser0_ where tuser0_.id=? 说 明 还 是只 进 行了一次 数 据 库 的 调 用.
再 将 代 码 更改如下:
public class Test {
public void get(){
Session session = HibernateSessionFactory.getSession();
TUser t = (TUser)session.get("hibernate.TUser", 2);
System.out.println(t.getName());
session.close();
Session session1 = HibernateSessionFactory.getSession();
TUser tt = (TUser)session1.get("hibernate.TUser", 2);
System.out.println(tt.getName());
session1.close();
}
}
继续测试 : 进 行 两 次 查询 控制台打印 两条 SQL 语 句:Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.*** as ***0_0_ from test.t_user tuser0_ where tuser0_.id=?
Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.*** as ***0_0_ from test.t_user tuser0_ where tuser0_.id=?
结论 :Hibernate 进 行 查询时总 是先在 缓 存中 进 行 查询 , 如 缓 存中 没 有所需 数 据才 进 行 数 据 库 的 查询 .Hibernate 的 内 部 缓 存是基于Session的生命周期的,也就是 说 存在于每 个 Session 内 部, 它随 着Session的 创 建而存在, 随 着Session的 销 毁而 灭 亡, 内 部 缓 存一般由Hibernate自 动维护 , 不需要人 为 干 预 , 当 然我 们 也可以根据需要 进 行相 应 操作:Session.evict(Object)( 将 指定 对 象 从内 部 缓 存 清 除),Session.clear()( 清 空 内 部 缓 存).(如在 两 次 查询间 加入Session.clear() 将会清 空 内 部 缓 存,使得一 个 Sesion 内 部的 两 次相同的 查询 要 对数 据 库进 行 两 次操作).
2.二 级缓 存:(有 时称为 SessionFactory Level Cache)
Hibernate 本身 并 未提供二 级缓 存的 产 品化 实现 ( 只提供了一 个 基于HashTable的 简单缓 存以供 调试 ), 这 里我使用的是第三方 缓 存 组 件:EHcache.Hibernate的二 级缓 存 实现 需要 进 行以下配置(Hibernate3):
首先在hibernate.cfg.xml 内 添加:
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="hibernate.cache.use_query_cache">true</property>
然后在映射文件中添加:
<cache usage="read-only"/>
测试 上面代 码 : 控制台 输 出多了 这样 一句[ WARN] (CacheFactory.java:43) - read-only cache configured for mutable class: hibernate.TUser,二 级缓 存 启 用成功!!
java 代 码
public class Test {
public void executeQuery(){
List list = new ArrayList();
Session session = HibernateSessionFactory.getSession();
Query query = session.createQuery("from TUser t");
query.setCacheable(true);// 激活 查询缓 存
list = query.list();
session.close();
}
public void get(){
Session session = HibernateSessionFactory.getSession();
TUser t = (TUser)session.get("hibernate.TUser", 2);
System.out.println(t.getName());
session.close();
}
}
测试 : 控制台只 输 出一 条 SQL 语 句:Hibernate: select tuser0_.id as id0_, tuser0_.name as name0_, tuser0_.*** as ***0_ from test.t_user tuser0_(即Query query = session.createQuery("from TUser t") 这 句代 码 所 对应 的SQL). executeQuery()方法 与 get() 方法使用的是不同的Session!!可是executeQuery()方法 与 get() 方法只 对数 据 库进 行了一次操作, 这 就是二 级缓 存在起作用了.
结论 :Hibernate 二 级缓 存是SessionFactory 级 的 缓 存, 它 允 许 多 个 Session 间 共享,使用 时 需要使用第三方的 缓 存 组 件,新版Hibernate 将 EHcache 作 为 默 认 的二 级缓 存 实现 .
缓 存同步策略: 缓 存同步策略 决 定了 数 据 对 象在 缓 存中的存取 规则 , 我 们 必 须为 每 个实 体 类 指定相 应 的 缓 存同步策略.Hibernate中提供了4 种 不同的 缓 存同步策略:( 暂时 只 记个概 念 吧 )
1.read-only: 只 读 . 对 于不 会发 生改 变 的 数 据可使用 (对数据只能查询,其他的增删改都会报错不关是1或2 缓存中 ) .
2.nonstrict-read-write: 如果程序 对并发访问 下的 数 据同步要求不 严 格,且 数 据更新 频 率 较 低,采用本 缓 存同步策略可 获 得 较 好性能. ( 不能在二级缓存进行 增删改都会报错 )
3.read-write: 严 格的 读写缓 存.基于 时间 戳判定机制, 实现 了"read committed"事 务 隔离等 级 . 用于 对数 据同步要求的情 况 , 但不支持分布式 缓 存, 实际应 用中使用最多的 缓 存同步策略. (都可以比较常用的)
4.transactional: 事 务 型 缓 存,必 须运 行在JTA事 务环 境中.此 缓 存中, 缓 存的相 关 操作被添加到事 务 中(此 缓 存 类 似于一 个内 存 数 据 库 ), 如事 务 失 败 , 则缓 冲池的 数 据 会 一同回 滚 到事 务 的 开 始之前的 状态 . 事 务 型 缓 存 实现 了"Repeatable read"事 务 隔离等 级 , 有效保 证 了 数 据的合法性,适 应 于 对关键数 据的 缓 存,Hibernate 内 置 缓 存中,只有JBossCache支持事 务 型 缓 存.
缓 存就是 数 据 库数 据在 内 存中的 临时 容器,包括 数 据 库数 据在 内 存中的 临时 拷 贝 , 它 位于 数 据 库与数 据 库访问层 中 间 .ORM 在 查询数 据 时 首先 会 根据自身的 缓 存管理策略,在 缓 存中 查 找相 关数 据,如 发现 所需的 数 据, 则 直接 将 此 数 据作 为结 果加以利用, 从 而避免了 数 据 库调 用性能的 开销 . 而相 对内 存操作而言, 数 据 库调 用是一 个 代价高 昂 的 过 程.
一般 来讲 ORM 中的 缓 存分 为 以下几 类 :
1. 事 务级缓 存:即在 当 前事 务 范 围内 的 数 据 缓 存.就Hibernate 来讲 , 事 务级缓 存是基于Session的生命周期 实现 的,每 个 Session 内 部 会 存在一 个数 据 缓 存, 它随 着Session的 创 建而存在, 随 着Session的 销 毁而 灭 亡,因此也 称为 Session Level Cache.
2. 应 用 级缓 存:即在某 个应 用中或 应 用中某 个独 立 数 据 库访问 子集中的共享 缓 存,此 缓 存可由多 个 事 务 共享( 数 据 库 事 务 或 应 用事 务 ), 事 务 之 间 的 缓 存共享策略 与应 用的事 务 隔离机制密切相 关 . 在Hibernate中, 应 用 级缓 存由SessionFactory 实现 , 所有由一 个 SessionFactory 创 建的Session 实 例共享此 缓 存,因此也 称为 SessionFactory Level Cache.
3. 分布式 缓 存:即在多 个应 用 实 例,多 个 JVM 间 共享的 缓 存策略.分布式 缓 存由多 个应 用 级缓 存 实 例 组 成,通 过 某 种远 程机制(RMI,JMS) 实现 各 个缓 存 实 例 间 的 数 据同步,任何一 个实 例的 数 据修改, 将导 致整 个 集群 间 的 数 据 状态 同步.
Hibernate 数 据 缓 存:
1. 内 部 缓 存(Session Level Cache也 称 一 级缓 存):
举 例 说 明:
java 代 码
public class Test {
public void get(){
Session session = HibernateSessionFactory.getSession();
TUser t = (TUser)session.get("hibernate.TUser", 2);
System.out.println(t.getName());
session.close();
}
}
进 行 测试 : 在控制台打印出一 条 SQL 语 句:Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.*** as ***0_0_ from test.t_user tuser0_ where tuser0_.id=? 说 明 进 行了一次 数 据 库 的 调 用.
代 码 更改如下:
public class Test {
public void get(){
Session session = HibernateSessionFactory.getSession();
TUser t = (TUser)session.get("hibernate.TUser", 2);
System.out.println(t.getName());
TUser tt = (TUser)session.get("hibernate.TUser", 2);
System.out.println(tt.getName());
session.close();
}
}
再 进 行 测试 : 进 行了 两 次 查询 , 控制台仍然只打出一 条 SQL 语 句:Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.*** as ***0_0_ from test.t_user tuser0_ where tuser0_.id=? 说 明 还 是只 进 行了一次 数 据 库 的 调 用.
再 将 代 码 更改如下:
public class Test {
public void get(){
Session session = HibernateSessionFactory.getSession();
TUser t = (TUser)session.get("hibernate.TUser", 2);
System.out.println(t.getName());
session.close();
Session session1 = HibernateSessionFactory.getSession();
TUser tt = (TUser)session1.get("hibernate.TUser", 2);
System.out.println(tt.getName());
session1.close();
}
}
继续测试 : 进 行 两 次 查询 控制台打印 两条 SQL 语 句:Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.*** as ***0_0_ from test.t_user tuser0_ where tuser0_.id=?
Hibernate: select tuser0_.id as id0_0_, tuser0_.name as name0_0_, tuser0_.*** as ***0_0_ from test.t_user tuser0_ where tuser0_.id=?
结论 :Hibernate 进 行 查询时总 是先在 缓 存中 进 行 查询 , 如 缓 存中 没 有所需 数 据才 进 行 数 据 库 的 查询 .Hibernate 的 内 部 缓 存是基于Session的生命周期的,也就是 说 存在于每 个 Session 内 部, 它随 着Session的 创 建而存在, 随 着Session的 销 毁而 灭 亡, 内 部 缓 存一般由Hibernate自 动维护 , 不需要人 为 干 预 , 当 然我 们 也可以根据需要 进 行相 应 操作:Session.evict(Object)( 将 指定 对 象 从内 部 缓 存 清 除),Session.clear()( 清 空 内 部 缓 存).(如在 两 次 查询间 加入Session.clear() 将会清 空 内 部 缓 存,使得一 个 Sesion 内 部的 两 次相同的 查询 要 对数 据 库进 行 两 次操作).
2.二 级缓 存:(有 时称为 SessionFactory Level Cache)
Hibernate 本身 并 未提供二 级缓 存的 产 品化 实现 ( 只提供了一 个 基于HashTable的 简单缓 存以供 调试 ), 这 里我使用的是第三方 缓 存 组 件:EHcache.Hibernate的二 级缓 存 实现 需要 进 行以下配置(Hibernate3):
首先在hibernate.cfg.xml 内 添加:
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
<property name="hibernate.cache.use_query_cache">true</property>
然后在映射文件中添加:
<cache usage="read-only"/>
测试 上面代 码 : 控制台 输 出多了 这样 一句[ WARN] (CacheFactory.java:43) - read-only cache configured for mutable class: hibernate.TUser,二 级缓 存 启 用成功!!
java 代 码
public class Test {
public void executeQuery(){
List list = new ArrayList();
Session session = HibernateSessionFactory.getSession();
Query query = session.createQuery("from TUser t");
query.setCacheable(true);// 激活 查询缓 存
list = query.list();
session.close();
}
public void get(){
Session session = HibernateSessionFactory.getSession();
TUser t = (TUser)session.get("hibernate.TUser", 2);
System.out.println(t.getName());
session.close();
}
}
测试 : 控制台只 输 出一 条 SQL 语 句:Hibernate: select tuser0_.id as id0_, tuser0_.name as name0_, tuser0_.*** as ***0_ from test.t_user tuser0_(即Query query = session.createQuery("from TUser t") 这 句代 码 所 对应 的SQL). executeQuery()方法 与 get() 方法使用的是不同的Session!!可是executeQuery()方法 与 get() 方法只 对数 据 库进 行了一次操作, 这 就是二 级缓 存在起作用了.
结论 :Hibernate 二 级缓 存是SessionFactory 级 的 缓 存, 它 允 许 多 个 Session 间 共享,使用 时 需要使用第三方的 缓 存 组 件,新版Hibernate 将 EHcache 作 为 默 认 的二 级缓 存 实现 .
缓 存同步策略: 缓 存同步策略 决 定了 数 据 对 象在 缓 存中的存取 规则 , 我 们 必 须为 每 个实 体 类 指定相 应 的 缓 存同步策略.Hibernate中提供了4 种 不同的 缓 存同步策略:( 暂时 只 记个概 念 吧 )
1.read-only: 只 读 . 对 于不 会发 生改 变 的 数 据可使用 (对数据只能查询,其他的增删改都会报错不关是1或2 缓存中 ) .
2.nonstrict-read-write: 如果程序 对并发访问 下的 数 据同步要求不 严 格,且 数 据更新 频 率 较 低,采用本 缓 存同步策略可 获 得 较 好性能. ( 不能在二级缓存进行 增删改都会报错 )
3.read-write: 严 格的 读写缓 存.基于 时间 戳判定机制, 实现 了"read committed"事 务 隔离等 级 . 用于 对数 据同步要求的情 况 , 但不支持分布式 缓 存, 实际应 用中使用最多的 缓 存同步策略. (都可以比较常用的)
4.transactional: 事 务 型 缓 存,必 须运 行在JTA事 务环 境中.此 缓 存中, 缓 存的相 关 操作被添加到事 务 中(此 缓 存 类 似于一 个内 存 数 据 库 ), 如事 务 失 败 , 则缓 冲池的 数 据 会 一同回 滚 到事 务 的 开 始之前的 状态 . 事 务 型 缓 存 实现 了"Repeatable read"事 务 隔离等 级 , 有效保 证 了 数 据的合法性,适 应 于 对关键数 据的 缓 存,Hibernate 内 置 缓 存中,只有JBossCache支持事 务 型 缓 存.
Hibernate
获
取
数
据的方式有不同的几
种
,其
与缓
存
结
合使用的效果也不
尽
相同,而Hibernate中具体
怎么
使用
缓
存其
实
是我
们
很
关
心的一
个问题
,直接涉及到性能方面。
缓 存在Hibernate中主要有三 个 方面:一 级缓 存、二 级缓 存和 查询缓 存;一 级缓 存在Hibernate中 对应 的即 为 session 范 围 的 缓 存,也就是 当 session 关闭时缓 存即被 清 除,一 级缓 存在Hibernate中是不可配置的部分;二 级缓 存在Hibernate中 对应 的即 为 SessionFactory 范 围 的 缓 存,通常 来讲 SessionFactory 的生命周期和 应 用的生命周期相同,所以可以看成是 进 程 缓 存或集群 缓 存,二 级缓 存在Hibernate中是可以配置的,可以通 过 class-cache 配置 类 粒度 级别 的 缓 存(class-cache在class中 数 据 发 生任何 变 化的情 况 下自 动 更新),同 时 也可通 过 collection-cache 配置集合粒度 级别 的 缓 存(collection-cache 仅 在 collection中增加了元素或者 删 除了元素的情 况 下才自 动 更新,也就是 当 collection 中元素 发 生 值 的 变 化的情 况 下 它 是不 会 自 动 更新的), 缓 存自然 会带来并发 的 访问问题 , 这个时 候相 应 的就要根据 应 用 来设 置 缓 存所采用的事 务 隔离 级别 ,和 数 据 库 的事 务 隔离 级别概 念基本一 样 , 没 什 么 多介 绍 的, ^_^; 查询缓 存在Hibernate同 样 是可配置的,默 认 是 关闭 的,可以通 过设 置cache.use_ query_cache 为 true 来 打 开查询缓 存。根据 缓 存的通常 实现 策略,我 们 可以 来 理解Hibernate的 这 三 种缓 存, 缓 存的 实现 通 过 是通 过 key/value 的Map方式 来实现 ,在 Hibernate的一 级 、二 级 和 查询缓 存也同 样 如此,一 级 、二 级缓 存使用的key均 为 po 的主 键 ID ,value即 为 po 实 例 对 象, 查询缓 存使用的 则为查询 的 条 件、 查询 的 参数 、 查询 的 页数 ,value有 两种 情 况 ,如果采用的是select po.property 这样 的方式那 么 value 为 整 个结 果集,如采用的是from 这样 的方式那 么 value 为获 取的 结 果集中各po 对 象的主 键 ID , 这样 的作用很明 显 , 节 省 内 存,^_^
简单 介 绍 完Hibernate的 缓 存后,再 结 合Hibernate的 获 取 数 据方式 来说 明 缓 存的具体使用方式,在Hibernate中 获 取 数 据常用的方式主要有四 种 :Session.load、Session.get、Query.list、Query.iterator。
1、Session.load
在 执 行session.load 时 ,Hibernate首先 从当 前session的一 级缓 存中 获 取id 对应 的 值 ,在 获 取不到的情 况 下, 将 根据 该对 象是否配置了二 级缓 存 来 做相 应 的 处 理,如配置了二 级缓 存, 则从 二 级缓 存中 获 取id 对应 的 值 ,如仍然 获 取不到 则还 需要根据是否配置了延 迟 加 载来决 定如何 执 行,如未配置延 迟 加 载则从数 据 库 中直接 获 取,在 从数 据 库获 取到 数 据的情 况 下,Hibernate 会 相 应 的 填 充一 级缓 存和二 级缓 存,如配置了延 迟 加 载则 直接返回一 个 代理 类 ,只有在 触发 代理 类 的 调 用 时 才 进 行 数 据 库查询 的操作。
在 这样 的情 况 下我 们 就可以看到,在session一直打 开 的情 况 下,要注意在适 当 的 时 候 对 一 级缓 存 进 行刷新操作,通常是在 该对 象具有 单 向 关联维护 的 时 候,在Hibernate中可以使用象session.clear、session.evict的方式 来 强制刷新一 级缓 存。
二 级缓 存 则 在 数 据 发 生任何 变 化(新增、更新、 删 除)的情 况 下都 会 自 动 的被更新。
2、Session.get
在 执 行Session.get 时 ,和Session.load不同的就是在 当从缓 存中 获 取不到 时 ,直接 从数 据 库 中 获 取id 对应 的 值 。
3、Query.list
在 执 行Query.list 时 ,Hibernate的做法是首先 检查 是否配置了 查询缓 存,如配置了 则从查询缓 存中 查 找key 为查询语 句+ 查询参数 + 分 页条 件的 值 ,如 获 取不到 则从数 据 库 中 进 行 获 取, 从数 据 库获 取到后Hibernate 将会 相 应 的 填 充一 级 、二 级 和 查询缓 存,如 获 取到的 为 直接的 结 果集, 则 直接返回,如 获 取到的 为 一堆id的 值 , 则 再根据id 获 取相 应 的 值 (Session.load) ,最后形成 结 果集返回,可以看到,在 这样 的情 况 下,list也是有可能造成N次的 查询 的。
查询缓 存在 数 据 发 生任何 变 化的情 况 下都 会 被自 动 的 清 空。
4、Query.iterator
在 执 行Query.iterator 时 ,和Query.list的不同的在于 从数 据 库获 取的 处 理上,Query.iterator向 数 据 库发 起的是 select id from 这样 的 语 句,也就是 它 是先 获 取符合 查询条 件的id,之后在 进 行iterator.next 调 用 时 才再次 发 起session.load的 调 用 获 取 实际 的 数 据。
可 见 ,在 拥 有二 级缓 存 并 且 查询参数 多 变 的情 况 下,Query.iterator 会 比Query.list更 为 高效。
这 四 种获 取 数 据的方式都各有适用的 场 合,要根据 实际 情 况 做相 应 的 决 定,^_^,最好的方式无疑就是打 开 show_sql 选项 看看 执 行的情 况来 做分析,系 统结构 上只用保 证这种调 整是容易 实现 的就好了,在cache 这个 方面的 调 整自然是非常的容易,只需要 调 整配置文件里的 设 置,而 查询 的方式 则 可 对 外部 进 行 屏 蔽, 这样 要根据 实际 情 况调 整也非常容易。
缓 存在Hibernate中主要有三 个 方面:一 级缓 存、二 级缓 存和 查询缓 存;一 级缓 存在Hibernate中 对应 的即 为 session 范 围 的 缓 存,也就是 当 session 关闭时缓 存即被 清 除,一 级缓 存在Hibernate中是不可配置的部分;二 级缓 存在Hibernate中 对应 的即 为 SessionFactory 范 围 的 缓 存,通常 来讲 SessionFactory 的生命周期和 应 用的生命周期相同,所以可以看成是 进 程 缓 存或集群 缓 存,二 级缓 存在Hibernate中是可以配置的,可以通 过 class-cache 配置 类 粒度 级别 的 缓 存(class-cache在class中 数 据 发 生任何 变 化的情 况 下自 动 更新),同 时 也可通 过 collection-cache 配置集合粒度 级别 的 缓 存(collection-cache 仅 在 collection中增加了元素或者 删 除了元素的情 况 下才自 动 更新,也就是 当 collection 中元素 发 生 值 的 变 化的情 况 下 它 是不 会 自 动 更新的), 缓 存自然 会带来并发 的 访问问题 , 这个时 候相 应 的就要根据 应 用 来设 置 缓 存所采用的事 务 隔离 级别 ,和 数 据 库 的事 务 隔离 级别概 念基本一 样 , 没 什 么 多介 绍 的, ^_^; 查询缓 存在Hibernate同 样 是可配置的,默 认 是 关闭 的,可以通 过设 置cache.use_ query_cache 为 true 来 打 开查询缓 存。根据 缓 存的通常 实现 策略,我 们 可以 来 理解Hibernate的 这 三 种缓 存, 缓 存的 实现 通 过 是通 过 key/value 的Map方式 来实现 ,在 Hibernate的一 级 、二 级 和 查询缓 存也同 样 如此,一 级 、二 级缓 存使用的key均 为 po 的主 键 ID ,value即 为 po 实 例 对 象, 查询缓 存使用的 则为查询 的 条 件、 查询 的 参数 、 查询 的 页数 ,value有 两种 情 况 ,如果采用的是select po.property 这样 的方式那 么 value 为 整 个结 果集,如采用的是from 这样 的方式那 么 value 为获 取的 结 果集中各po 对 象的主 键 ID , 这样 的作用很明 显 , 节 省 内 存,^_^
简单 介 绍 完Hibernate的 缓 存后,再 结 合Hibernate的 获 取 数 据方式 来说 明 缓 存的具体使用方式,在Hibernate中 获 取 数 据常用的方式主要有四 种 :Session.load、Session.get、Query.list、Query.iterator。
1、Session.load
在 执 行session.load 时 ,Hibernate首先 从当 前session的一 级缓 存中 获 取id 对应 的 值 ,在 获 取不到的情 况 下, 将 根据 该对 象是否配置了二 级缓 存 来 做相 应 的 处 理,如配置了二 级缓 存, 则从 二 级缓 存中 获 取id 对应 的 值 ,如仍然 获 取不到 则还 需要根据是否配置了延 迟 加 载来决 定如何 执 行,如未配置延 迟 加 载则从数 据 库 中直接 获 取,在 从数 据 库获 取到 数 据的情 况 下,Hibernate 会 相 应 的 填 充一 级缓 存和二 级缓 存,如配置了延 迟 加 载则 直接返回一 个 代理 类 ,只有在 触发 代理 类 的 调 用 时 才 进 行 数 据 库查询 的操作。
在 这样 的情 况 下我 们 就可以看到,在session一直打 开 的情 况 下,要注意在适 当 的 时 候 对 一 级缓 存 进 行刷新操作,通常是在 该对 象具有 单 向 关联维护 的 时 候,在Hibernate中可以使用象session.clear、session.evict的方式 来 强制刷新一 级缓 存。
二 级缓 存 则 在 数 据 发 生任何 变 化(新增、更新、 删 除)的情 况 下都 会 自 动 的被更新。
2、Session.get
在 执 行Session.get 时 ,和Session.load不同的就是在 当从缓 存中 获 取不到 时 ,直接 从数 据 库 中 获 取id 对应 的 值 。
3、Query.list
在 执 行Query.list 时 ,Hibernate的做法是首先 检查 是否配置了 查询缓 存,如配置了 则从查询缓 存中 查 找key 为查询语 句+ 查询参数 + 分 页条 件的 值 ,如 获 取不到 则从数 据 库 中 进 行 获 取, 从数 据 库获 取到后Hibernate 将会 相 应 的 填 充一 级 、二 级 和 查询缓 存,如 获 取到的 为 直接的 结 果集, 则 直接返回,如 获 取到的 为 一堆id的 值 , 则 再根据id 获 取相 应 的 值 (Session.load) ,最后形成 结 果集返回,可以看到,在 这样 的情 况 下,list也是有可能造成N次的 查询 的。
查询缓 存在 数 据 发 生任何 变 化的情 况 下都 会 被自 动 的 清 空。
4、Query.iterator
在 执 行Query.iterator 时 ,和Query.list的不同的在于 从数 据 库获 取的 处 理上,Query.iterator向 数 据 库发 起的是 select id from 这样 的 语 句,也就是 它 是先 获 取符合 查询条 件的id,之后在 进 行iterator.next 调 用 时 才再次 发 起session.load的 调 用 获 取 实际 的 数 据。
可 见 ,在 拥 有二 级缓 存 并 且 查询参数 多 变 的情 况 下,Query.iterator 会 比Query.list更 为 高效。
这 四 种获 取 数 据的方式都各有适用的 场 合,要根据 实际 情 况 做相 应 的 决 定,^_^,最好的方式无疑就是打 开 show_sql 选项 看看 执 行的情 况来 做分析,系 统结构 上只用保 证这种调 整是容易 实现 的就好了,在cache 这个 方面的 调 整自然是非常的容易,只需要 调 整配置文件里的 设 置,而 查询 的方式 则 可 对 外部 进 行 屏 蔽, 这样 要根据 实际 情 况调 整也非常容易。
1
.
<
hibernate-configuration
>
<
session-factory
>
<
property
name
=
"connection.username"
>
sa
</
property
>
<
property
name
=
"connection.url"
>
jdbc:microsoft:sqlserver://localhost:1433;databasename=pubs
</
property
>
<
property
name
=
"dialect"
>
org.hibernate.dialect.SQLServerDialect
</
property
>
<
property
name
=
"myeclipse.connection.profile"
>
conn
</
property
>
<
property
name
=
"connection.driver_class"
>
com.microsoft.jdbc.sqlserver.SQLServerDriver
</
property
>
<
property
name
=
"show_sql"
>
true
</
property
>
<!--
添加
2
级缓存开始
-->
<
property
name
=
"cache.provider_class"
>
org.hibernate.cache.EhCacheProvider
</
property
>
<
property
name
=
"hibernate.cache.use_query_cache"
>
true
</
property
>
<!--
结束
-->
<
mapping
resource
=
"org/jw/po/Users.hbm.xml"
/>
</
session-factory
>
</
hibernate-configuration
>
2
.
<
hibernate-mapping
>
<
class
name
=
"org.jw.po.Users"
table
=
"users"
schema
=
"dbo"
catalog
=
"pubs"
>
<!--
配置
-->
<
cache
usage
=
"read-only"
></
cache
>
<
id
name
=
"sid"
type
=
"java.lang.Integer"
>
<
column
name
=
"sid"
/>
<
generator
class
=
"identity"
/>
</
id
>
<
property
name
=
"sname"
type
=
"java.lang.String"
>
<
column
name
=
"sname"
length
=
"20"
not-null
=
"true"
/>
</
property
>
</
class
>
</
hibernate-mapping
>