这是我的第一个程序。
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.
当我运行第一个过程并且刚好在第二个过程之后,我必须首先获得值Updated(这里是CrditLimit)。(反之亦然) 但是由于记录被第一个锁定,我无法运行第二个。它显示错误消息。 我认为问题在于我的锁定。请帮忙。
答案 0 :(得分:3)
这正是您应该期待的。
在事务提交之前,记录被锁定,专门用于第一个过程的好处。提交将在第二个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放在块外可以防止“用户去喝咖啡”的问题。
答案 1 :(得分: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函数。”
但这很小,真的。在我看来,重要的是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”,即使它是“MyTable的DEFINE BUFFER MyTAble”(这看起来很奇怪......),问题就解决了。
这就是为什么汤姆的例子,包括一个DEFINE BUFFER ......,应该成为你在ABL中所做工作的模板。
之前曾问过这个问题 - 请参阅https://stackoverflow.com/a/5490130/1433147。