原子“如果不存在则插入”与 NHibernate

发布于 2024-12-04 21:41:07 字数 732 浏览 2 评论 0原文

考虑一个执行以下操作的应用程序:

var user = session.QueryOver<User>().Where(x => x.Name==name).SingleOrDefault();

if (user == null)
{
    user = new User(name);
    session.Save(user);
}

业务规则规定用户名必须是唯一的,并且这是由数据库中的 UNIQUE INDEX 支持的。上面的代码工作得很好,直到两个用户尝试使用相同的名称同时注册,都得到 user == null 并尝试创建一个新的 User。第一个提交将成功,第二个将失败并从数据库引发异常。

避免此类竞争条件的一种方法是将关键代码包装在 lock { } 块中,但是当应用程序的多个实例针对同一数据库运行时,这将无济于事。现在,如果我可以使用 RDBS 的锁定机制(在本例中为 MS SQL)就好了……

这就是我陷入困境的地方。根据我在 NHibernate 文档中读到的内容,我也许可以通过向请求显式锁定的 QueryOver() 链添加一些提示来解决这个问题。这必须是表锁,因为我还没有实际的行。这可能吗?或者

,我可以提高事务的隔离级别并自动获得所需的锁吗?考虑到其他(无问题的)查询是在同一工作单元内进行的,我怀疑这种更改可能会引入大量的阻塞/等待。是这样吗?

Consider an application that does something along the lines of:

var user = session.QueryOver<User>().Where(x => x.Name==name).SingleOrDefault();

if (user == null)
{
    user = new User(name);
    session.Save(user);
}

Business rules states that a user's name must be unique, and this is backed by a UNIQUE INDEX in the database. The code above works just fine, until two users try to register simultaneously with the same name, both get user == null and try to create a new User. The first one to commit will suceed, the second one will fail with an exception raised from the database.

One way to avoid such race conditions involves wrapping the critical code in a lock { } block, but this will not help when multiple instances of the applications works against the same database. Now, if only I could use the locking mechanisms of the RDBS (MS SQL in this case)…

This is where I got stuck. From what I can read in the NHibernate docs, I might be able to solve it by adding some a hint to my QueryOver() chain requesting an explicit lock. This would have to be a table lock, as I have no actual rows yet. Is this possible? How

Alternatively, could I increase the isolation level of the transaction and gain the required locks automatically? Considering that other (non-problematic) queries are made within the same unit-of-work, I suspect this kind of change could introduce a lot of blocking/waiting. Is that so?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

烟若柳尘 2024-12-11 21:41:07

将隔离级别增加到可串行化。这将在整个查询中获取读锁和写锁,包括“where”子句。

Increase the isolation level to Serializable. That would acquire both read and write lock across the whole query, including your 'where' clause.

万水千山粽是情ミ 2024-12-11 21:41:07

确保您在保存时通过同一个 NHibernate 会话运行查询 - 这样做将确保您使用相同的事务,自动利用数据库的锁定机制。

也就是说,您总是需要为事务失败做好准备,并且您的代码需要优雅地处理这种情况。喜欢序列化的块和锁会增加代码的复杂性,但无法消除数据库级别失败的可能性,因此您增加了复杂性,但收效甚微。

Make sure you run the queries through the same NHibernate session as you save - doing this will ensure that you use the same transaction, leveraging the locking mechanisms of the database automatically.

That said, you always need to be prepared for the transaction to fail, and your code needs to handle that case gracefully. Getting fancy with serialised blocks and locks adds complexity to your code but can't eliminate the possibility of failing at the database level, so you're adding that complexity to little gain.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文