Try OpenEdge Now
skip to main content
ABL Essentials
Handling Data and Locking Records : Record locking in ABL : Optimistic and pessimistic locking strategies : Using FIND CURRENT and CURRENT-CHANGED
When you have a record in a record buffer, you can re-read it from the database to see if it has changed since it was read, using the FIND CURRENT statement or, for a query, the GET CURRENT statement. You can then use the CURRENT-CHANGED function to compare the record currently in the buffer with what is in the database. This is a part of your optimistic locking strategy. The simple example that follows, saved as h-findCurrent.p, shows how the syntax works:
/* h-findCurrent.p */
  Customer.CustNum Customer.Name FORMAT "x(12)"
  Customer.CreditLimit Customer.Balance.

/* When the user closes the frame by pressing F2, start a transaction: */
ON "GO" OF FRAME CustFrame DO:
      MESSAGE "This record has been changed by another user." SKIP
              "Please re-enter your changes." VIEW-AS ALERT-BOX.
      DISPLAY Customer.CreditLimit Customer.Balance WITH FRAME CustFrame.
      RETURN NO-APPLY. /* Cancel the attempted update/GO */
    /* Otherwise assign the changes to the database record. */
    ASSIGN Customer.CreditLimit Customer.Balance.
  RELEASE Customer.

/* To start out with, find, display, and enable the record with no lock. */
DISPLAY Customer.CustNum Customer.Name Customer.CreditLimit Customer.Balance
  WITH FRAME CustFrame.
ENABLE Customer.CreditLimit Customer.Balance WITH FRAME CustFrame.
/* Wait for the trigger condition to do the update and close the frame. */
The code executed first is at the bottom of the procedure, after the trigger block. It finds a desired CustomerNO-LOCK, so as to avoid lock contention, then displays it and any enabled fields for input. If the user changes the CreditLimit or Balance in the frame and presses F2, which fires the GO event for the frame, the code re-reads the same record with an EXCLUSIVE-LOCK and uses CURRENT-CHANGED to compare it with the record in the buffer. Note that because the changes haven't been assigned to the record buffer yet, the record in the buffer and the one in the database should be the same if no one else has changed it. If someone has, then the procedure displays a message, displays the changed values, and executes a RETURN NO-APPLY to cancel the GO event. Otherwise, it assigns the changes.
The DO TRANSACTION block defines the scope of the update. The RELEASE statement after that block releases the locked record from the buffer to free it up for another user. You'll learn more about these statements in Managing Transactions.
To test this procedure:
1. Run this procedure in both sessions. Either session can update the CreditLimit or Balance, because neither session has the record locked. One session displays it:
2. In the other session, update it and save the change by pressing F2:
3. If you try to update the same record in the first session, you see a message:
The change made by the other session is displayed and, because you now see the record as it's saved in the database, you can now re-enter your change and save it.
In a distributed application, it's much more difficult to manage this type of situation. In the event that a server procedure gets a modified record back from another session that has already been changed, it must somehow send a response back to that session to inform it that there is a conflict. The procedure that detects the conflict does not have access to the user interface and so cannot display a message or change values directly. This is why ABL provides SmartObjects, which handle passing data from server to client, manage record locks and record contention, and communicate messages and updates back to the client from the server transparently. But it does all this using the same ABL statements that you can use in procedures you write from scratch.