Блокировка NHibernate и вновь созданных записей - PullRequest
0 голосов
/ 18 января 2019

У меня есть две таблицы. Обороты кошелька и баланс кошелька.

Оборот кошелька - это кредитные и дебетовые операции через кошелек в определенной валюте и для конкретного клиента.

Баланс кошелька содержит для каждого клиента и валюты не более одной записи, которая содержит баланс для клиента и валюты. Баланс является результатом суммы кредитов и дебетов от оборотов кошелька. Я хочу, чтобы приложение работало так, что, когда я добавляю оборот в таблицу оборотов, мне нужно пересчитать таблицу баланса путем добавления суммы текущего оборота. Если запись о валюте и клиенте текущего оборота еще не существует, я хотел бы создать новую.

Проблема заключается в том, как запретить приложению создавать несколько записей сальдо для одного и того же клиента и валюты в таблице сальдо.

Представьте себе, что я добавляю оборот для клиента с id = 1 и валютой = USD. Запись еще не существует, поэтому ее следует создать. Но что происходит, когда два одновременно работающих пользователя приложения A и B одновременно добавляют оборот для клиента 1 и валюты USD?

Что мне следует сделать, чтобы добиться поведения, чтобы при создании учетной записи баланса кошелька пользователем A в первый раз, когда она не была сохранена в базе данных, второму пользователю приложения B придется подождать, пока первый пользователь подтвердит свои записи Только для чтения текущего баланса (который содержит обновленный баланс от пользователя A) и продолжения с правильным значением баланса?

Может ли что-то подобное быть достигнуто? Как я должен сообщить nhibernate, что запись баланса с идентификатором клиента = 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 (объект, LockMode.Upgrade);

Я просто не совсем понимаю, если я заблокирую только что созданный объект, который еще не сохранен в БД - так что с ним не связан идентификатор, как nhibernate узнает, что комбинация клиента 1 и валюты USD уже была создана, это некоторые другие пользователи "память?"

Каков стандартный рецепт для подобных случаев, пожалуйста?

Большое спасибо,

Peter

РЕДАКТИРОВАТЬ 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;
        }

`

Но все еще существует проблема с возможностью того, что два разных веб-пользователя будут вызывать одну и ту же комбинацию идентификатора клиента и валюты в первый раз, в этом случае будут созданы две записи для одного клиента и валюты. И я не могу использовать синтаксис блокировки (объекта), потому что мы используем веб-ферму с несколькими серверами приложений, которые не используют статические переменные.

1 Ответ

0 голосов
/ 24 января 2019

Если вы хотите, чтобы объекты были уникальными по некоторым свойствам в базе данных, вы должны убедиться в этом, определив надлежащие УНИКАЛЬНЫЕ ограничения в базе данных. Затем он гарантирует, что дубликатов не будет.

На следующем шаге вы хотите подумать о том, что должно произойти, если два процесса попытаются добавить дубликаты записей, потому что они работают одновременно:

  • Вы можете перехватить УНИКАЛЬНОЕ нарушение и повторить этот процесс. На втором перейти он найдет запись, совершенную другой транзакцией.

  • Вы можете использовать SERIALIZABLE уровень изоляции транзакции. Это самая безопасная изоляция уровень. Если я правильно думаю, это должно гарантировать, что когда любой процесс пытается читать существующую запись, либо она не получит никакой записи, ни существующей записи, ИЛИ если любая другая открытая транзакция уже искала ту же запись, которую заблокирует и дождитесь завершения другой транзакции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...