Получение обновленного значения при использовании блокировки? - PullRequest
2 голосов
/ 12 марта 2012

это моя первая процедура.

define frame LockFrame Customer.Name Customer.CreditLimit Customer.Balance.
pause.

DO TRANSACTION:
    for each Customer exclusive-lock:
    assign Customer.CreditLimit = Customer.CreditLimit + 5.
    pause 1 no-message.
    display Customer.Name Customer.CreditLimit Customer.Balance.
    end.
end.

и это моя вторая процедура.

define frame LockFrame Customer.Name Customer.CreditLimit Customer.Balance.
pause.

DO TRANSACTION:
    for each Customer exclusive-lock:
    assign Customer.Balance= Customer.Balance + 2.
    pause 1 no-message.
    display Customer.Name Customer.CreditLimit Customer.Balance.
    end.
end.

Когда я запускаю первую процедуру и сразу после второй процедуры, я должен получить значение Обновлено первым (здесь CrditLimit). (И наоборот) Но я не могу запустить второй, так как запись заблокирована первым. Это показывает сообщение об ошибке. Я думаю, что проблема с моей блокировкой. Пожалуйста, помогите на этом.

Ответы [ 2 ]

3 голосов
/ 12 марта 2012

Это именно то, что вы должны ожидать.

Запись заблокирована, исключительно в интересах первой процедуры, до совершения транзакции. Фиксация произойдет во 2-м операторе END.

Я не уверен, почему у вас есть ПАУЗА - вы никогда не должны блокировать ввод-вывод внутри блока транзакции - это приведет только к проблемам (например, пользователи блокируют друг друга, когда они встают и идут за кофе). .)

Вы почти НИКОГДА не хотите вложить в транзакцию обновление всей таблицы (что и делает ваш блок DO TRANSACTION). Даже если вы думаете, что это то, что вы хотите сделать, или вам сказали, что это то, что вы должны сделать, это, вероятно, неправильно. Обычно это происходит в результате смешения «бизнес-транзакции» с «транзакцией базы данных» - это не одно и то же, особенно когда в игре используются большие объемы данных.

Лучший способ написать свой код (применить одну и ту же концепцию к обоим образцам):

define buffer updCustomer for customer.

for each customer no-lock /* where whatever */:

  /* maybe some no-lock logic... */

  do for updCustomer transaction:

    find updCustomer exclusive-lock where recid( updCustomer ) = recid( customer ).
    assign
      updCustomer.creditLimit = customer.creditLimit + 5.
    .

    display
      updCustomer.name
      updCustomer.creditLimit
      updCustomer.balance
    .

  end.

  pause 1 no-message.

end.

Использование NO-LOCK в FOR EACH позволяет логике выбора или другой логике работать без блокировки. Использование буфера обновления и блока DO FOR ... TRANSACTION тесно ограничивает блокировку записи и транзакцию для этого отдельного блока. Размещение ПАУЗЫ вне блока предотвращает проблему «пользователь идет за кофе».

0 голосов
/ 12 июня 2012

Стесняюсь комментировать любой ответ, который дал Том, так как считаю его авторитетным. Но я хочу отметить две маленькие вещи.

Прежде всего, использование RECID может быть лучше, чем ROWID. ROWID рекомендуется для использования Progress (см. http://documentation.progress.com/output/OpenEdge102a/oe102ahtml/wwhelp/wwhimpl/common/html/wwhelp.htm?context=dvref&file=dvref-15-48.html), поскольку RECID «поддерживается для обратной совместимости. Для большинства приложений вместо этого используйте функцию ROWID».

Но это незначительно, правда. На мой взгляд, также важно то, что Том сделал в своем примере для вас - он определил буфер («определите буфер updCustomer для клиента»), который он использовал в обновлении. Я хочу призвать вас использовать буферы КАЖДЫЙ раз, когда вы работаете с записью, особенно если вы используете постоянные или супер-процедуры, или если вы используете функции или внутренние процедуры.

Почему? Определение буфера гарантирует, что область действия обновляемого буфера ограничена тем местом, где вы его определили. В качестве примера, Progress «утечет» буфер по умолчанию в вашу супер процедуру, если вы не будете осторожны. Представьте себе этот сценарий ... программа, которая находит запись, вызывает функцию в супер-процедуре для выполнения "некоторых вещей", а затем удаляет запись.

FIND MyTable WHERE MyTable.fk = fkValue NO-LOCK NO-ERROR.
UpdateOtherStuff(MyTable.fkValue).
DeleteMyRecord(MyTable.fkValue).

Но в "UpdateOtherStuff" он выполняет некоторую работу, включая эту ...

FOR EACH MyTable:
    If MyTable.Thing = 'ThingOne' THEN LEAVE.
    /* other processing here... */
END.

Вы можете быть удивлены, когда обнаружите, что супер-процедура совместно использует по умолчанию буфер «MyTable» с вашей программой и заканчивает тем, что перемещает запись туда, куда вам не нужно ... так что вызов «DeleteMyRecord ()» имеет другую запись, чем вы ожидаете.

Проблема была бы решена, если бы «UpdateOtherStuff» имел «DEFINE BUFFER ... FOR MyTable» вверху, даже если бы это было «DEFINE BUFFER MyTAble для MyTable» (как ни странно это выглядит ...).

Вот почему пример Тома, включая DEFINE BUFFER ..., должен быть шаблоном для работы, которую вы выполняете в ABL.

Этот вопрос задавался ранее - см. https://stackoverflow.com/a/5490130/1433147.

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