我想知道当你使用NHibernate从SQL中检索一个项目列表修改一个项目时你建议的做法是什么,但是通过最小化锁定来允许最大的并发性。
我正在关注这篇文章:http://www.hibernatingrhinos.com/products/nhprof/learn/alert/DoNotUseImplicitTransactions
这说我应该在操作中使用BeginTransaction
,但我想通过仅锁定更新来管理并发性 - 我不想在列表的收集周围使用BeginTransaction(IsolationLevel.ReadCommitted)
。
这个场景是选择我要更新哪个对象的条件无法在select中完成的(在我的设计示例中它是一个随机选择)但是我想要获取,修改并保存该对象的锁定事务确保如果另一个线程同时修改对象,则不会丢失任何人的更新。我假设选择要修改的对象的过程不会受到另一个线程修改的对象的影响(可能该条件基于不由任何其他线程管理的对象的元素)。
using (ISession session = _sessionFactory.OpenSession())
{
ParentEntity[] arr;
using (ITransaction transaction = session.BeginTransaction(IsolationLevel.ReadUncommitted))
{
arr = session.CreateQuery("from ParentEntity")
.List<ParentEntity>().ToArray();
transaction.Rollback();
}
// Process results to choose one for modification - for now we'll use a random one
int i = _rand.Next(arr.Count());
using (ITransaction transaction = session.BeginTransaction(IsolationLevel.ReadCommitted))
{
ParentEntity par = session.Get<ParentEntity>(arr[i].ID);
par.data += " modified at " + DateTime.Now.ToString();
session.SaveOrUpdate(par);
transaction.Commit();
}
}
所以这是使用ReadUncommitted
获取完整列表,选择要修改的列表,然后在ReadCommitted
事务中再次获取该项并提交它。
是否有更好的模式来管理这种操作?
答案 0 :(得分:0)
查询行不会锁定表。所以你可以只读取和更新内存实体。
using (ISession session = _sessionFactory.OpenSession())
{
ParentEntity[] arr;
int i = _rand.Next(arr.Count());
using (var transaction = session.BeginTransaction())
{
arr = session.CreateQuery("from ParentEntity")
.List<ParentEntity>().ToArray();
ParentEntity par = arr[i];
par.data += " modified at " + DateTime.Now.ToString();
session.SaveOrUpdate(par);
transaction.Commit();
}
}
答案 1 :(得分:0)
我发现在多个同步线程中强烈地运行这个,一些修改丢失了(一个线程会覆盖另一个线程的结果)。
我意识到我的缺陷是没有配置'乐观锁定',这是为解决我试图避免的问题而设计的技术。在声明之后,Rafael Mueller的代码原理即使在强烈的多线程处理时也能正常工作(如果抛出StaleObjectStateException
也会添加重试)。