2012-03-12 2 views
2

это моя первая процедура.Получение обновления Значение при использовании блокировки?

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. 

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

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). (И наоборот) Но я не могу запустить второй, так как запись заблокирован первым. Появляется сообщение об ошибке. Я думаю, проблема связана с моей блокировкой. Пожалуйста, помогите в этом.

ответ

3

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

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

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

Вы почти НИКОГДА не хотите прикладывать обновление всей таблицы в транзакции (что и делает блок 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 на ПО КАЖДОМУ позволяет логику выбора или другой логики для запуска без необходимости блокировки. Использование буфера обновления и блок DO FOR ... TRANSACTION сильно привязывают блокировку записи и транзакцию к этому единственному блоку. Размещение ПАУЗА вне блока предотвращает проблему «пользователь выходит за кофе».

+0

Идеально подходит для меня сэр .. просто начал изучать прогресс. Я не знал, как заблокировать одну запись, а не блокировать всю таблицу. теперь я получил его. Мне также не было известно о функции recid. Это пауза размещения на самом деле мне нужно, как вы предложили. Спасибо за ценный четкий ответ. –

0

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

Прежде всего, использование 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».

Но это на самом деле мало. Что также важно, на мой взгляд, то, что Том сделал в своем примере для вас - он определил буфер («define buffer 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.