【发布时间】:2017-04-10 22:45:11
【问题描述】:
我有一个表格,其中包含一列预先填充的数字。我使用 Nhibernate 的 API 抓取了前 10 行,其中“已使用”标志设置为 false。 当多个会话尝试从表中抓取行时,避免并发问题的最佳方法是什么? 选择行后,我可以将标志列更新为 True,以便后续调用不会使用相同的数字。
【问题讨论】:
标签: sql-server nhibernate concurrency locking fluent-nhibernate
我有一个表格,其中包含一列预先填充的数字。我使用 Nhibernate 的 API 抓取了前 10 行,其中“已使用”标志设置为 false。 当多个会话尝试从表中抓取行时,避免并发问题的最佳方法是什么? 选择行后,我可以将标志列更新为 True,以便后续调用不会使用相同的数字。
【问题讨论】:
标签: sql-server nhibernate concurrency locking fluent-nhibernate
在这样一个大背景下,可以这样做:
// RepeatableRead ensures the read rows does not get concurrently updated by another
// session.
using (var tran = session.BeginTransaction(IsolationLevel.RepeatableRead))
{
var entities = session.Query<Entity>()
.Where(e => !e.Used)
.OrderBy(e => e.Id)
.Take(10)
.ToList();
foreach(var entity in entities)
{
e.Used = true;
}
// If your session flush mode is not the default one and does not cause
// commits to flush the session, add a session.Flush(); call before committing.
tran.Commit();
return entities;
}
这很简单。它可能会因死锁而失败,在这种情况下,您必须丢弃会话,获取一个新会话,然后重试。
使用乐观更新模式可能是另一种解决方案,但这也需要一些代码来从失败的尝试中恢复。
使用不会导致死锁风险的无显式锁定解决方案可以做到,但需要更多查询:
const int entitiesToObtain = 10;
// Could initialize here with null instead, but then, will have to check
// for null after the while too.
var obtainedEntities = new List<Entity>();
while (obtainedEntities.Count == 0)
{
List<Entity> candidates;
using (var tran = session.BeginTransaction())
{
candidatesIds = session.Query<Entity>()
.Where(e => !e.Used)
.Select(e => e.Id)
.OrderBy(id => id)
.Take(entitiesToObtain)
.ToArray();
}
if (candidatesIds.Count == 0)
// No available entities.
break;
using (var tran = session.BeginTransaction())
{
var updatedCount = session.CreateQuery(
@"update Entity e set e.Used = true
where e.Used = false
and e.Id in (:ids)")
.SetParameterList("ids", candidatesIds)
.ExecuteUpdate();
if (updatedCount == candidatesIds.Length)
{
// All good, get them.
obtainedEntities = session.Query<Entity>()
.Where(e => candidatesIds.Contains(e.Id))
.ToList();
tran.Commit();
}
else
{
// Some or all of them were no more available, and there
// are no reliable way to know which ones, so just try again.
tran.Rollback();
}
}
}
这使用NHibernate DML-style operations 建议here。 strongly typed alternative 在 NHibernate v5.0 中可用。
【讨论】: