如果 2 个或更多人同时更新记录会发生什么?

2024-04-15

我使用 NHibernate 的版本属性,每次更新聚合根时该属性都会自动递增。如果 2 个或更多人同时更新同一条记录会发生什么?

另外,我将如何测试这个?

请注意,这不是我遇到过的情况,只是想知道。


什么是原子的,什么不是

正如其他人所说,SQL Server 中的更新是原子操作。然而,当使用 NHibernate(或任何 O/RM)更新数据时,您通常首先select数据,对对象进行更改,然后update数据库包含您的更改。该事件序列不是原子的。即使选择和更新是在几毫秒内执行的,另一个更新也有可能在中间发生。如果两个客户端获取相同数据的相同版本,并且假设他们是当时唯一编辑该数据的客户端,则他们可能会无意中覆盖彼此的更改。

问题说明

If we didn't guard against this concurrent-update scenario, weird things could happen - sneaky bugs that shouldn't seem possible. Suppose we had a class that modeled the state changes of water:
public class BodyOfWater
{
    public virtual int Id { get; set; }
    public virtual StateOfMatter State { get; set; }

    public virtual void Freeze()
    {
        if (State != StateOfMatter.Liquid)
            throw new InvalidOperationException("You cannot freeze a " + State + "!");
        State = StateOfMatter.Solid;
    }

    public virtual void Boil()
    {
        if (State != StateOfMatter.Liquid)
            throw new InvalidOperationException("You cannot boil a " + State + "!");
        State = StateOfMatter.Gas;
    }
}

假设数据库中记录了以下水体:

new BodyOfWater
{
    Id = 1,
    State = StateOfMatter.Liquid
};

两个用户大致同时从数据库中获取该记录,对其进行修改,然后将更改保存回数据库。用户A将水结冰:

using (var transaction = sessionA.BeginTransaction())
{
    var water = sessionA.Get<BodyOfWater>(1);
    water.Freeze();
    sessionA.Update(water);

    // Same point in time as the line indicated below...

    transaction.Commit();
}

用户 B 尝试将水煮沸(现在加冰!)...

using (var transaction = sessionB.BeginTransaction())
{
    var water = sessionB.Get<BodyOfWater>(1);

    // ... Same point in time as the line indicated above.

    water.Boil();
    sessionB.Update(water);
    transaction.Commit();
}

...并且成功了!什么?用户A把水结冰了。难道不应该抛出一个异常,说“你不能煮固体!”吗?用户B获取数据before用户 A 已保存他的更改,因此对于两个用户来说,水最初似乎都是液体,因此两个用户都可以保存其冲突的状态更改。

Solution

为了防止这种情况,我们可以添加一个Version属性到类并用 NHibernate 映射它<version />映射:

public virtual int Version { get; set; }

这只是 NH​​ibernate 每次更新记录时都会增加的一个数字,并且它会检查以确保在我们没有观看时没有其他人增加了版本。而不是像这样的并发原生 SQL 更新......

update BodyOfWater set State = 'Gas' where Id = 1;

... NHibernate 现在将使用更智能的查询,如下所示:

update BodyOfWater set State = 'Gas', Version = 2 where Id = 1 and Version = 1;

如果受查询影响的行数为 0,则 NHibernate 知道出了问题 - 要么是其他人更新了该行,导致版本号现在不正确,要么是有人删除了该行,导致该 Id 不再存在。 NHibernate 然后会抛出一个StaleObjectStateException.

关于网络应用程序的特别说明

初始之间的时间越长select的数据和随后的update,出现此类并发问题的机会就越大。考虑网络应用程序中典型的“编辑”表单。实体的现有数据从数据库中选择,放入 HTML 表单中,然后发送到浏览器。用户可能会花几分钟修改表单中的值,然后再将其发送回服务器。很有可能其他人正在同时编辑相同的信息,并且他们在我们之前保存了更改。

在这种情况下,确保版本在我们实际保存更改的几毫秒内不会更改可能还不够。要解决此问题,您可以将版本号作为隐藏字段与其余表单字段一起发送到浏览器,然后在保存之前从数据库中取回实体时检查以确保版本没有更改。此外,您可以限制初始之间的时间量select和决赛update通过提供单独的“查看”和“编辑”视图,而不是仅对所有内容使用“编辑”视图。用户花在“编辑”视图上的时间越少,他们看到无法保存更改的恼人错误消息的可能性就越小。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如果 2 个或更多人同时更新记录会发生什么? 的相关文章

  • 平滑滚动.net 表单

    您好 我正在 net 中使用表单 并且在运行时动态添加大量链接标签 我将这些链接标签添加到面板并将该面板添加到 winform 当链接标签的数量增加时 表单会显示一个自动滚动条 垂直 现在 当我使用自动滚动向下滚动时 表单在滚动时不会更新其
  • VS 程序在调试模式下崩溃,但在发布模式下不崩溃?

    我正在 VS 2012 中运行以下程序来尝试 Thrust 函数查找 include cuda runtime h include device launch parameters h include
  • 读取 C# 中的默认应用程序设置

    我的自定义网格控件有许多应用程序设置 在用户范围内 其中大部分是颜色设置 我有一个表单 用户可以在其中自定义这些颜色 并且我想添加一个用于恢复默认颜色设置的按钮 如何读取默认设置 例如 我有一个名为的用户设置CellBackgroundCo
  • GCC 和 ld 找不到导出的符号...但它们在那里

    我有一个 C 库和一个 C 应用程序 尝试使用从该库导出的函数和类 该库构建良好 应用程序可以编译 但无法链接 我得到的错误遵循以下形式 app source file cpp text 0x2fdb 对 lib namespace Get
  • 如何在 C# 控制台应用程序中将修饰符(ctrl、alt、shift)按键捕获为单个按键?

    Console ReadKey 仅在按下 正常 键时捕获输入 然后将修饰符 如果有 附加为键信息的一部分 如何将单个修饰键注册为输入 提供了一种解决方案这个链接 https blogs msdn microsoft com toub 200
  • SQL Server lat;lng varchar 分割过程用作 Lat 和 Lng 以提高搜索速度

    有人可以帮助我使用存储过程或函数来传递我的存储varchar表中的 lat lng 到各个字段作为浮点数作为 Lat 和 Lng 以在半径搜索中使用 lanlng in Table 33 0000 15 222222 Thanks 你只是想
  • 在 SQL Server 中通过标准差消除异常值

    我试图通过标准差消除 SQL Server 2008 中的异常值 我只想要特定列中包含该列平均值的 1 标准差范围内的值的记录 我怎样才能做到这一点 如果您假设事件呈钟形曲线分布 则只有 68 的值与平均值相差 1 个标准差以内 95 的值
  • fprintf() 线程安全吗?

    我正在为野人就餐问题的某些变量编写一个 C 解决方案 现在 我创建线程 每个线程都将 FILE 获取到同一个调试文件 在线程内我正在使用 fprintf 进行一些打印 打印的语句不受任何类型的互斥锁等保护 我没有在调试文件中观察到任何交错行
  • vs2008 c#:Facebook.rest.api如何使用它来获取好友列表?

    如何在此基础上取得进一步的进步 获取好友列表的下一步是什么 string APIKey ConfigurationManager AppSettings API Key string APISecret ConfigurationManag
  • 如何获取 QTableView 的标题列表?

    我有一个QTableView我的对话框中的对象 我需要访问该表的水平标题并将它们放入QStringList object 尽管进行了大量搜索 但我在 Qt 文档中找不到如何获取此标头列表 编辑 我发现的最接近的地方是this https w
  • 如何在 QTabWidget Qt 中展开选项卡

    我有一个QTabWidget像这个 但我想展开选项卡以 填充 整个小部件宽度 如下所示 我怎样才能做到这一点 我在用Qt 5 3 2 and Qt 创建者 3 2 1 Update 我尝试使用setExpanding功能 ui gt myT
  • std::forward_as_tuple 将参数传递给 2 个构造函数

    我想传递多个参数以便在函数内构造两个对象 以同样的方式std pair
  • 检查 RoutedEvent 是否有任何处理程序

    我有一个自定义 Button 类 当单击它时 打开特定窗口 它总是执行相同的操作 我添加了一个可以在按钮的 XAML 中分配的 Click 事件 就像常规按钮一样 当它被单击时 我想执行 Click 事件处理程序 如果已分配 否则我想执行默
  • ASP.NET MailMessage.BodyEncoding 和 MailMessage.SubjectEncoding 默认值

    很简单的问题 但我在 MSDN 上找不到答案 查找 ASP NET 将用于的默认值 MailMessage BodyEncoding and MailMessage SubjectEncoding 如果你不在代码中设置它们 Thanks F
  • 了解使用 Windows 本机 WPF 客户端进行 ADFS 登录

    我已经阅读了大量有关 ADFS 与 NodeJS Angular 或其他前端 Web 框架集成以及一般流程如何工作的文献 并通过 Auth0 Angular 起始代码构建了概念证明 但我不明白如何这可以与本机 WPF Windows 应用程
  • 跨多个域的 ASP.NET 会话

    是否有合适的 NET 解决方案来在多个域上提供持久服务器会话 即 如果该网站的用户在 www site1 com 下登录 他们也将在 www site2 com 下登录 安全是我们正在开发的程序的一个问题 Thanks 它是否需要在会话中
  • SQL Server Management Studio v18 的命令行参数登录

    使用 SQL Server 身份验证时是否仍然可以从命令行打开 SQL Server Management Studio v18 根据微软的发行说明 由于安全风险 他们删除了 P 命令行参数 因此 可以从命令行使用 SQL Server 身
  • 每个数据库多个/单个 *.edmx 文件

    我有一个通过 ADO net 数据服务与数据库交互的项目 数据库很大 近 150 个具有依赖关系的表 该项目几年前开始 当时使用的是数据集 现在我们正在转向实体模型关系 由于我们添加了更多需要使用的表 该模型正在不断增长 这是管理这一切的正
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte
  • 使用 QtWebEngine 将 C++ 对象暴露给 Qt 中的 Javascript

    使用 QtWebkit 可以通过以下方式将 C 对象公开给 JavascriptQWebFrame addToJavaScriptWindowObject如中所述https stackoverflow com a 20685002 5959

随机推荐