前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >NHibernate 缓存

NHibernate 缓存

作者头像
beginor
发布于 2020-08-06 13:11:55
发布于 2020-08-06 13:11:55
61300
代码可运行
举报
运行总次数:0
代码可运行

NHibernate 缓存

NHibernate 支持两种级别的缓存, 即一级缓存以及二级缓存。

一级缓存

一级缓存就是 ISession 缓存, 在 ISession 的生命周期内可用, 多个 ISession 之间不能共享缓存的对象, 通过 ISessionFactory 创建的 ISession 默认支持一级缓存, 不需要特殊的配置。 在 NHibernate 的参考文档中, 对 ISession 的描述如下:

A single-threaded, short-lived object representing a conversation between the application and the persistent store. Wraps an ADO.NET connection. Factory for ITransaction. Holds a mandatory (first-level) cache of persistent objects, used when navigating the object graph or looking up objects by identifier. looking up objects by identifier.

注意最后一句, 明确说明了一级缓存的用途:

  • 在对象树种导航、浏览时, 使用一级缓存;
  • 根据对象的 id 加载对象;

由此可以看出, 一级缓存的作用是比较有限的, 但是也有用得着的地方。

一级缓存测试

一级缓存缓存无需配置, 默认支持, 因此, 在使用 session 查询对象, 如果仅仅是根据 id 加载指定的对象, 需要使用 session 的 Get 或 Load 方法, 这样可以充分利用 session 的一级缓存, 下面是一些测试用例以及输出:

1、测试一级缓存

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[Test]
public void TestSessionLoad() {
	using (var session = this._sessionFactory.OpenSession()) {
		Console.WriteLine("Before Load Category");
		var cat = session.Get<Category>(1);
		Console.WriteLine("{0}, {1}", cat.CategoryID, cat.CategoryName);
		cat = session.Get<Category>(1);
		Console.WriteLine("{0}, {1}", cat.CategoryID, cat.CategoryName);
	}
}

在上面的测试中, 两次加载同一个实体类, 该测试的输出为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
First get category 1
NHibernate: 
    SELECT
        category0_.[CategoryID] as column1_0_0_,
        category0_.[CategoryName] as column2_0_0_,
        category0_.[Description] as column3_0_0_,
        category0_.[Picture] as column4_0_0_ 
    FROM
        [dbo].[Categories] category0_ 
    WHERE
        category0_.[CategoryID]=@p0;
    @p0 = 1 [Type: Int32 (0)]
1, Beverages
second get category 1
1, Beverages

从输出可以看到, 只有第一次调用 Get 方法加载实体类时, 有 sql 输出, 从数据库取出了数据, 第二次加载则没有 sql 数据, 也就是利用了 session 的一级缓存。

2、测试 Get 与 Load 方法

session 提供了 Get 和 Load 两个方法, 这两个方法有什么区别呢? 我的测试代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[Test]
public void TestSessionGet() {
	using (var session = this._sessionFactory.OpenSession()) {
		Console.WriteLine("Before Get Category");
		var cat = session.Get<Category>(1);
		Console.WriteLine("After Get Category");
	}
}

对应的输出代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Before Get Category
NHibernate: 
    SELECT
        category0_.[CategoryID] as column1_0_0_,
        category0_.[CategoryName] as column2_0_0_,
        category0_.[Description] as column3_0_0_,
        category0_.[Picture] as column4_0_0_ 
    FROM
        [dbo].[Categories] category0_ 
    WHERE
        category0_.[CategoryID]=@p0;
    @p0 = 1 [Type: Int32 (0)]
After Get Category

从输出可以看到, 调用 Get 方法之后, Nh 立刻从数据库加载实例, 接下来看对 Load 方法的测试:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[Test]
public void TestSessionLoad() {
	using (var session = this._sessionFactory.OpenSession()) {
		Console.WriteLine("Before Load Category");
		var cat = session.Load<Category>(1);
		Console.WriteLine("After Load Category");
		Console.WriteLine("{0}, {1}", cat.CategoryID, cat.CategoryName);
	}
}

对应的输出代码为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
Before Load Category
After Load Category
NHibernate: 
    SELECT
        category0_.[CategoryID] as column1_0_0_,
        category0_.[CategoryName] as column2_0_0_,
        category0_.[Description] as column3_0_0_,
        category0_.[Picture] as column4_0_0_ 
    FROM
        [dbo].[Categories] category0_ 
    WHERE
        category0_.[CategoryID]=@p0;
    @p0 = 1 [Type: Int32 (0)]
1, Beverages

可以看出, 调用完 Load 方法之后, nh 并没有立即从数据库加载实体, 而是等到读取实体类属性时, 才从数据库加载, 也就是说, Load 方法是延迟加载的。

测试Query

根据文档的描述, 用 session 查询对象, 应该是不能利用一级缓存的, 我们来测试一下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[Test]
public void TestSessionLambdaQuery() {
	using (var session = this._sessionFactory.OpenSession()) {
		(from c in session.Query<Category>()
			where c.CategoryID == 1
			select c).First();

		(from c in session.Query<Category>()
			where c.CategoryID == 1
			select c).First();
	}
}

该测试用例的输出如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
NHibernate: 
    select
        TOP (1)  category0_.[CategoryID] as column1_0_,
        category0_.[CategoryName] as column2_0_,
        category0_.[Description] as column3_0_,
        category0_.[Picture] as column4_0_ 
    from
        [dbo].[Categories] category0_ 
    where
        category0_.[CategoryID]=@p0;
    @p0 = 1 [Type: Int32 (0)]
NHibernate: 
    select
        TOP (1)  category0_.[CategoryID] as column1_0_,
        category0_.[CategoryName] as column2_0_,
        category0_.[Description] as column3_0_,
        category0_.[Picture] as column4_0_ 
    from
        [dbo].[Categories] category0_ 
    where
        category0_.[CategoryID]=@p0;
    @p0 = 1 [Type: Int32 (0)]

从输出可以看出, 用 session 查询对象, 确实不能利用一级缓存。

注意: 如果查询时不想使用一级缓存, 可以 StatelessSession 对象, 和 Session 对象用法一样, 只是该对象没有一级缓存。

一级缓存管理

一级缓存的管理也是很简单的, 可以通过下面的三个方法管理:

  • session.Evict 从一级缓存中删除指定的实例;
  • session.Clear 清空一级缓存, 不会保存修改的内容;
  • session.Contains 检查实例是否存在于一级缓存中。

二级缓存

二级缓存是 SessionFactory 级别的缓存, 也就是数据库级别的缓存, 可以被同一个 SessionFactory 创建的所有 session 共享。

启用二级缓存

Nh 默认未启用二级缓存, 启用二级缓存需要如下步骤:

1、 在 hibernate.cfg.xml 文件中添加下面三个属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<property name="cache.provider_class">NHibernate.Cache.HashtableCacheProvider</property>`  
<property name="cache.use_second_level_cache">true</property>`  
<property name="cache.use_query_cache">true</property>`

这三个属性的作用是显而易见的, 第一个是指定使用什么二级缓存的实现, 第二个是启用二级缓存, 第三个是为查询启用二级缓存缓存。

NHibernate 的二级缓存是可以扩展的, NHibernate.ControlLib 提供了几个实现, 分别适用于不同的场景:

  • NHibernate.Caches.MemCache
  • NHibernate.Caches.Prevalence
  • NHibernate.Caches.SharedCache
  • NHibernate.Caches.SysCache
  • NHibernate.Caches.SysCache2
  • NHibernate.Caches.Velocity

这些实现都是

2、 配置指定的实体类、集合启用二级缓存缓存

在 hibernate.cfg.xml 文件中添加下面的设置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<class-cache class="HibernateTest.Models.Category" usage="read-only" include="all"/>

这一句表示对实体类 HibernateTest.Models.Category 启用二级缓存, 在实际项目中, 可以根据需要对多个实体类启用二级缓存。 也可以在实体类的 hbm 映射文件中配置使用二级缓存, 不过在 hibernate.cfg.xml 文件中统一配置是推荐的做法。

二级缓存测试

二级缓存的几个测试用例如下:

1、 测试 Get 方法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[Test]
public void TestGetEntity() {
	using (var session = this._sessionFactory.OpenSession()) {
		session.Get<Category>(1);
	}
	using (var session = this._sessionFactory.OpenSession()) {
		session.Get<Category>(1);
	}
}

对应的输出为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
NHibernate: 
    SELECT
        category0_.[CategoryID] as column1_0_0_,
        category0_.[CategoryName] as column2_0_0_,
        category0_.[Description] as column3_0_0_,
        category0_.[Picture] as column4_0_0_ 
    FROM
        [dbo].[Categories] category0_ 
    WHERE
        category0_.[CategoryID]=@p0;
    @p0 = 1 [Type: Int32 (0)]

从测试用例的输出可以看出, 二级缓存时可以在不同的 session 之间共享。

2、 测试 HQL 查询:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[Test]
public void TestHqlQuery() {
	using (var session = this._sessionFactory.OpenSession()) {
		var query = session.CreateQuery("from Category")
			.SetCacheMode(CacheMode.Normal)
			.SetCacheRegion("AllCategories")
			.SetCacheable(true);
		query.List<Category>();
	}
	using (var session = this._sessionFactory.OpenSession()) {
		var query = session.CreateQuery("from Category")
			.SetCacheMode(CacheMode.Normal)
			.SetCacheRegion("AllCategories")
			.SetCacheable(true);
		query.List<Category>();
	}
}

对应的输出为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
NHibernate: 
    select
        category0_.[CategoryID] as column1_0_,
        category0_.[CategoryName] as column2_0_,
        category0_.[Description] as column3_0_,
        category0_.[Picture] as column4_0_ 
    from
        [dbo].[Categories] category0_ 3、 测试 Linq 查询:

[Test]
public void TestLinqQuery() {
	using (var session = this._sessionFactory.OpenSession()) {
		var query = session.Query<Category>()
			.Cacheable()
			.CacheMode(CacheMode.Normal)
			.CacheRegion("AllCategories");
		var result = query.ToList();
	}
	using (var session = this._sessionFactory.OpenSession()) {
		var query = session.Query<Category>()
			.Cacheable()
			.CacheMode(CacheMode.Normal)
			.CacheRegion("AllCategories");
		var result = query.ToList();
	}
}

对应的输出为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
NHibernate: 
    select
        category0_.[CategoryID] as column1_0_,
        category0_.[CategoryName] as column2_0_,
        category0_.[Description] as column3_0_,
        category0_.[Picture] as column4_0_ 
    from
        [dbo].[Categories] category0_

4、 测试 QueryOver 查询:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[Test]
public void TestQueryOver() {
	using (var session = this._sessionFactory.OpenSession()) {
		var query = session.QueryOver<Category>()
			.Cacheable()
			.CacheMode(CacheMode.Normal)
			.CacheRegion("AllCategories");
		query.List();
	}
	using (var session = this._sessionFactory.OpenSession()) {
		var query = session.QueryOver<Category>()
			.Cacheable()
			.CacheMode(CacheMode.Normal)
			.CacheRegion("AllCategories");
		query.List();
	}
}

对应的输出为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
NHibernate: 
    SELECT
        this_.[CategoryID] as column1_0_0_,
        this_.[CategoryName] as column2_0_0_,
        this_.[Description] as column3_0_0_,
        this_.[Picture] as column4_0_0_ 
    FROM
        [dbo].[Categories] this_

二级缓存选项

NHibernate 二级有几个配置选项, 他们分别是:

实体类以及集合二级缓存配置选项

指定类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<class-cache class="类名称" region="默认类名称" include="all|non-lazy"
         usage="read-only|read-write|nonstrict-read-write|transactional" />

指定集合:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<collection-cache collection ="集合名称" region="默认集合名称"
              usage="read-only|read-write|nonstrict-read-write|transactional"/>
  • region:可选,默认值为类或集合的名称,用来指定二级缓存的区域名,对应于缓存实现的一个命名缓存区域。
  • include:可选,默认值为all,当取non-lazy时设置延迟加载的持久化实例的属性不被缓存。
  • usage:声明缓存同步策略,就是上面说明的四种缓存策略。

查询二级缓存配置

  • Cacheable 为一个查询显示启用二级缓存;
  • CacheMode 缓存模式, 有如下可选:
    1. Ignore:更新数据时将二级缓存失效,其它时间不和二级缓存交互
    2. Put:向二级缓存写数据,但不从二级缓存读数据
    3. Get:从二级缓存读数据,仅在数据更新时向二级缓存写数据
    4. Normal:默认方式。从二级缓存读/写数据
    5. Refresh:向二级缓存写数据,想不从二级缓存读数据,通过在配置文件设置 cache.use_minimal_puts从数据库中读取数据时,强制二级缓存刷新
  • CacheRegion 给查询缓存指定了特定的命名缓存区域, 如果两个查询相同, 但是指定的 CacheRegion 不同, 则也会从数据库查询数据。

以上是在项目中用到的二级缓存相关知识的整理, 肯定不完整, NHibernate 的缓存还有更多的地方需要挖掘。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
微信消息推送神器pushplus介绍,让消息推送如此简单
原生实现微信消息推送需要申请公众号、成为开发者,实现微信公众号接入和消息推送等接口。
杨永贞
2022/10/25
12.2K0
微信消息推送神器pushplus介绍,让消息推送如此简单
消息推送平台的设计
第一点:导入消息接收人的数据可以支持excel导入,然后去各个业务线去拉取完整的用户id即可,导入的数据需要分页提交给服务或者分页落库
花落花相惜
2021/11/23
2.5K0
小程序消息推送x微搭低代码,微信消息推送快速上手实操教程
近期有很多同学关注小程序消息推送的问题,今天就简单介绍下微搭低代码开发的小程序应用如何实现微信订阅消息的推送,目前方案有两种:
漫话开发者
2023/01/02
2K0
小程序消息推送x微搭低代码,微信消息推送快速上手实操教程
揭秘!消息管理平台的实现原理
这个系列就以「消息管理平台」来打个样吧,这是我维护近一年的系统了。这篇文章可以带你全面认识「消息管理平台」是怎么设计和实现的,有兴趣的同学欢迎在评论区下留言和交流。
Java3y
2020/09/24
1.6K0
揭秘!消息管理平台的实现原理
【随笔】自动化油价推送:GitHub Actions 实战
在网上找到一个油价网站,http://www.qiyoujiage.com , 定位到自己所在的具体地址,例如:http://www.qiyoujiage.com/hubei/xxx.shtml ,通过 jsoup 抓取关键数据,如 92#-0# 汽油价格等其他自己需要的数据。
框架师
2023/12/16
2070
【随笔】自动化油价推送:GitHub Actions 实战
Spug推送平台:开发者的消息推送神器
外滩首席运维
2025/02/25
1120
Austin消息中心
写这篇文章是首先我之前也是做过消息中心的需求,当时3y开源了它的项目Austin,当时觉得挺好的,之后我负责的消息中心重构了一版,现在想想也挺开心的。所以想把之前学习的整理了下来。
路行的亚洲
2023/08/31
9730
Austin消息中心
带你了解什么是Push消息推送
而Push消息是这么多种类型之中非常重要和常用的一种类型,最近也希望针对Push推送做些新的尝试,于是去学习学习些Push消息运营的技巧。
Java3y
2019/08/29
2.6K0
带你了解什么是Push消息推送
喜马拉雅亿级用户量的离线消息推送系统架构设计实践
对于IM的开发者来说,离线消息推送是再熟悉不过的需求了,比如下图就是典型的IM离线消息通知效果。
JackJiang
2021/07/14
1.4K0
喜马拉雅亿级用户量的离线消息推送系统架构设计实践
Android消息推送:手把手教你集成小米推送
继承自PushMessageReceiver(抽象类,继承自BroadcastReceiver),其作用主要是:
Carson.Ho
2019/02/22
4.7K0
消息推送技术,除了websocket还知道那些?
WebSocket是一种网络通信协议,它提供了在单个TCP连接上进行全双工通信的能力。这意味着数据可以在客户端和服务器之间双向流动,而无需客户端通过轮询或重复请求来获取更新。
老K博客
2024/06/01
7120
消息推送技术,除了websocket还知道那些?
移动端开发之APP消息推送[通俗易懂]
有这样一种场景,当你在手机APP上输入你的信息,会自动跳出一个弹窗,表示某任务已执行。最简单的一个例子就是当你输入手机号,点击获取验证码的时候,就会跳出一个对话框,说“验证码已发送到手机,请注意查收”,这些都是如何实现的。
全栈程序员站长
2022/09/05
3.6K0
移动端开发之APP消息推送[通俗易懂]
如何构建一套高可用的移动消息推送平台?
消息推送作为移动 APP 运营中的一项关键技术,已经被越来越广泛的运用。本文追溯了推送技术的发展历史,剖析了其核心原理,并对推送服务的关键技术进行深入剖析,围绕消息推送时产生的服务不稳定性,消息丢失、延迟,接入复杂性,统计缺失等问题,提供了一整套平台级的高可用消息推送解决方案。实践中,借助于该平台,不仅能提能显著提高消息到达率,还能提高研发效率,并道出了移动开发基础设施的平台化架构思路。
Java3y
2019/09/12
3.2K0
如何构建一套高可用的移动消息推送平台?
微信小程序之订阅消息推送java开发
2019年10月份微信发布了模板消息整改公告由模板消息更改为订阅消息: 具体公告地址:https://developers.weixin.qq.com/community/develop/doc/
猿码优创
2020/06/12
5.1K0
喜马拉雅亿级用户量的离线消息推送系统架构设计实践
对于IM的开发者来说,离线消息推送是再熟悉不过的需求了,比如下图就是典型的IM离线消息通知效果。
JackJiang
2021/07/12
1.1K0
SignalR 中丰富多彩的消息推送方式
在上一篇 SignalR 文章中,演示了如何通过 SignalR 实现了简单的聊天室功能;本着简洁就是美的原则,这一篇我们也来聊聊在 SignalR 中的用户和组的概念,理解这些基础知识有助于更好的开发基于 SignalR 的应用,通过对用户和分组的理解,进一步扩展出对用户和分组的管理,以及消息推送的各种方式,为全面接入 SignalR 做准备。
梁规晓
2019/04/11
1K0
SignalR 中丰富多彩的消息推送方式
小程序消息推送,订阅消息的实现,借助云开发云函数实现定时推送订阅消息功能
由于长期性订阅消息,目前仅向政务民生、医疗、交通、金融、教育等线下公共服务开放,后期将逐步支持到其他线下公共服务业务。仅就线下公共服务这一点,长期性订阅消息就和大部分开发者无缘了。 所以我们这里只能以使用一次性订阅消息为例。
编程小石头
2022/03/18
2.6K0
小程序消息推送,订阅消息的实现,借助云开发云函数实现定时推送订阅消息功能
小程序订阅消息推送(含源码)java实现小程序推送,springboot实现微信消息推送
至于如何创建模板消息,如果获取模板id我这节就不再讲解,不知道的同学可以查看我上篇文章《借助云开发实现小程序订阅消息和模板消息的推送功能》里面有详细的讲解。
编程小石头
2020/01/05
4.9K0
小程序订阅消息推送(含源码)java实现小程序推送,springboot实现微信消息推送
Java点餐系统和点餐小程序新加微信消息推送功能
我们今天就以排号入座为例,当排号等位的用户被叫号时,会给用户发送一条微信订阅消息。就是在管理点击下图的可入座时,发送订阅消息给用户。
编程小石头
2020/10/11
2.1K0
Hutool该怎么用?
在日常开发中,我们会使用很多工具类来提升项目开发的速度,而国内用的比较多的 Hutool 框架,就是其中之一。
程序猿川子
2024/05/21
2580
Hutool该怎么用?
推荐阅读
相关推荐
微信消息推送神器pushplus介绍,让消息推送如此简单
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文