如何在使用NHibernate锁定模式更新之前锁定对象?

时间:2013-08-21 15:56:04

标签: .net nhibernate

首先让我说明我想要完成的事情。我有一个填充了工作的表。还有一个Web服务,其中包含一个允许更改名为SaveJob的作业数据的方法。此方法检索作业及其所有数据,在新数据上运行验证(需要对其他表进行一些数据库查询),然后将其保存回数据库。这有点慢。大约一秒钟。

有时会发生两次SaveJob次呼叫,这些呼叫将在同一个工作中紧密相连,并且它们会相互重叠。现在这是唯一的Web服务器,但我希望我的解决方案Web服务器兼容,所以即使我知道如何使用单例来解决这个问题,我更愿意让数据库处理锁。

问题是,我是否可以使用NHibernate和SQL Server数据库锁来尝试读取另一个SaveJob调用已被更改的作业时阻止第二次SaveJob调用?

我相信答案是肯定的,但我不确定如何解决这个问题。我已阅读有关ISession.Lock()的文档,我相信我需要的是使用NHibernate.LockMode.Upgrade

我的下一个问题是这个锁何时被释放?

我认为它是在提交事务时发布的,但我找不到任何明确说明这一点的文档。

然而,如果是这样的话那么我有没有办法打开另一个交易并运行一些查询而不关闭我在第一时间抓住这份工作时开始的交易?或者我在一次交易中完成这项工作吗?

2 个答案:

答案 0 :(得分:8)

首先,我建议不要跨多个线程使用单个事务,因为ISession本身不是线程安全的。

在会话中使用LockMode.Upgrade或在session.Get()上使用时,NHibernate会在检索实体时发出select with (rowlock)语句(详细信息可能取决于数据库类型和配置)。如果该对象先前已被另一个线程检索,则当前线程将等待锁定被释放或超时到期。在update上执行transaction.Commit()语句时,锁本身就会被释放。使用LockMode.UpgradeNoWait提供了类似的功能,除了它不等待并且在执行锁定时简单地失败。

这个Ayende's post也有一些关于NHibernate在session.Lock()上发出什么内容的信息。

您可以阅读NHibernate Documentation Chapter 11, Section 6上不同类型的锁定。

答案 1 :(得分:3)

  

偶尔会发生两次SaveJob通话......

这些作业指向同一个数据库行,还是完全不同的实体?

如果他们指向一个作业实例,您可以使用version使用乐观锁定来禁止数据重写。

如果它们不同,那么只需要数据库就可以执行并发查询。 somehow slow可以是表/索引/标识符锁定。

锁定不迟于提交后释放,取决于隔离级别。在查询执行后立即释放一些(读取)锁,在提交后可以释放特定行内的更新。

编辑:

显然乐观锁定对你不利,你需要使用

  

//使用SELECT ... FOR UPDATE进行版本检查,然后重新关联:   sess.Lock(pk,LockMode.Upgrade);

http://nhibernate.info/doc/nh/en/index.html#manipulatingdata-update-lock