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.

这是我的第二个程序。

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)。(反之亦然)但我无法运行第二个,因为记录被第一个锁定。它显示一条错误消息。我认为问题出在我的锁定上。请对此提供帮助。

4

2 回答 2

4

这正是您应该期待的。

记录被锁定,专门为了第一个过程的利益,直到事务提交。提交将发生在第二个 END 语句中。

我不确定你为什么在那里有 PAUSE——你不应该在事务块内阻塞 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.

在 FOR EACH 上使用 NO-LOCK 允许选择逻辑或其他逻辑在不需要锁的情况下运行。使用更新缓冲区和 DO FOR ... TRANSACTION 块将记录锁和事务严格限定到该单个块。将 PAUSE 放在块外可以防止“用户去喝咖啡”的问题。

于 2012-03-12T13:07:38.437 回答
0

我不愿评论汤姆给出的任何答案,因为我认为他是权威的。但我想指出两件小事。

首先,使用 RECID 作为 ROWID 可能会更好。建议 Progress 使用 ROWID(请参阅http://documentation.progress.com/output/OpenEdge102a/oe102ahtml/wwhelp/wwhimpl/common/html/wwhelp.htm?context=dvref&file=dvref-15-48.html),因为RECID “支持向后兼容。对于大多数应用程序,请改用 ROWID 函数。”

但那是次要的,真的。在我看来,同样重要的是 Tom 在他的示例中为您所做的 - 他定义了一个缓冲区(“为客户定义缓冲区 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 for MyTable”,问题也会得到解决(看起来很奇怪......)。

这就是为什么 Tom 的示例(包括 DEFINE BUFFER...)应该是您在 ABL 中所做工作的模板。

之前有人问过这个问题 - 请参阅https://stackoverflow.com/a/5490130/1433147

于 2012-06-12T04:33:29.227 回答