NHibernate和新创建的记录锁定

时间:2019-01-18 12:05:58

标签: nhibernate locking

我有两个桌子。 钱包营业额和钱包余额。

钱包营业额是指使用特定货币并针对特定客户在钱包上进行的贷记和借记操作。

每个客户和货币的钱包余额最多包含一个记录,其中包含客户和货币的余额。余额是来自钱包营业额的贷方和借方之和的结果。 我想应用程序的行为方式是,当我将营业额添加到营业额表中时,我需要通过增加当前营业额来重新计算余额表。如果尚无货币记录和当前营业额的客户,我想创建一个新记录。

问题是,如何防止应用程序在余额表中为同一客户和货币创建多个余额记录。

想象一下,我要为id = 1和currency = USD的客户增加营业额。 该记录尚不存在,因此应创建它。 但是,当两个同时使用应用程序的用户A和B同时增加客户1的营业额和货币USD时会发生什么?

我应该做些什么来实现这种行为,当我由用户A首次创建钱包余额记录并且现在还没有将其保存到数据库时,应用程序的第二个用户B必须等到第一个用户提交其记录时,仅读取当前余额(包含用户A更新的余额)并继续正确的余额值即可。

这样的事情还能实现吗?我应该如何让nhibernate知道,已经有一些用户创建了客户ID = 1且币种= USD的余额记录,因此,当其他用户在表中查找此组合时,该记录已创建,用户应等到用户A为止。提交吗?

GetOrCreateBalance的代码:

    public ClientWalletBalance GetOrCreateWalletBalance(int clientId, int currency)
    {
        var clientObj = ClientService.GetClient(clientId);

        var res = clientObj.WalletBalances.FirstOrDefault(x => x.Currency.Id == currency);

        if (res == null)
        {
            res = new ClientWalletBalance()
            {
                Client = clientObj,
                Currency = ClientService.GetCurrency(currency),
            };
        };
        clientObj.WalletBalances.Add(res);

        return res;
    }

我已经知道,可以通过

来实现

session.Lock(object,LockMode.Upgrade);

我只是不清楚,如果我锁定了新创建的对象,该对象尚未保存到数据库中-因此它没有关联的ID,nhibernate怎么会知道客户端1和货币USD的组合已经创建? “记忆?”

请问这种情况下的标准配方是什么?

非常感谢您

彼得

编辑1: 直到现在,我最终还是这样: 呼叫代码:

using (ISession session = NHibernateConfiguration.ReturnOpenedSession())
            {
                using (ITransaction transaction = session.BeginTransaction())
                {
                    var cwb = Service.OrderAndPaymentService.GetOrCreateWalletBalance(clientId, currency, delay);

                    response.Error = cwb.Balance.ToString();

                    transaction.Commit();
                }
            }

`

调用代码:`

public ClientWalletBalance GetOrCreateWalletBalance(int clientId, int currency, int delay = 0)
        {
            var clientObj = ClientService.GetClient(clientId);

            var res = clientObj.WalletBalances.FirstOrDefault(x => x.Currency.Id == currency);

            if (res != null)
            {
                NHibernateConfiguration.GetCurrentSession().Lock(res, LockMode.Upgrade);
            }
            else
            {
                res = new ClientWalletBalance()
                {
                    Client = clientObj,
                    Currency = ClientService.GetCurrency(currency),
                    Balance = delay,
                };

                clientObj.WalletBalances.Add(SaveClientWalletBalance(res));

                NHibernateConfiguration.GetCurrentSession().Lock(res, LockMode.Upgrade);
            }

            System.Threading.Thread.Sleep(delay * 1000);

            return res;
        }

`

但是仍然存在以下问题:两个不同的Web用户将首次调用客户端ID和货币的相同组合,在这种情况下,将创建相同客户端和货币的两个记录。而且我不能使用lock(object)语法,因为我们使用的Web场具有多个不共享静态变量的应用服务器。

1 个答案:

答案 0 :(得分:1)

如果您希望对象在数据库的某些属性上是唯一的,则应通过在数据库中定义适当的UNIQUE约束来确保这一点。然后,它将确保不会重复。

在下一步中,您要考虑如果两个进程因为并发运行而试图添加重复的记录,该怎么办:

  • 您可以捕获UNIQUE违例,然后重试该过程。在第二 它将找到其他事务提交的记录。

  • 您可以使用SERIALIZABLE事务隔离级别。这是最安全的隔离 水平。如果我的想法正确,则应确保任何流程尝试 读取现有记录,它将不获取任何记录,现有记录,或者 任何其他未结交易已经在寻找将阻止的相同记录 然后等到另一笔交易完成。