By including appropriate calls to internal procedures of the transaction initiating procedure in your remote procedures, you can encapsulate the management of a multi-request automatic transaction in a manner completely hidden from the client. However, the following example is relatively simple in that the client calls all of the internal procedures of the transaction initiating procedure directly, thus more openly exposing the transaction mechanism to the client.
The following two procedures include a transaction initiating procedure and an ABL client procedure that accesses it.
The transaction initiating procedure (a-txtest.p) implements an unchained automatic transaction. This procedure provides three internal procedures that allow the client to explicitly start (startme), commit (stopme), and roll back (abortme) transactions.
a-txtest.p
/* This statement makes this procedure a transaction-initiating procedure */
TRANSACTION-MODE AUTOMATIC.
PROCEDURE startme: /* This empty internal procedure starts a transaction */
END.
PROCEDURE stopme: /* This internal proc arranges for a commit */
DEFINE VARIABLE hTrans AS HANDLE.
hTrans = THIS-PROCEDURE:TRANSACTION. htrans:SET-COMMIT(). END.
PROCEDURE abortme: /* This internal proc arranges for a rollback */
DEFINE VARIABLE hTrans AS HANDLE.
hTrans = THIS-PROCEDURE:TRANSACTION. hTrans:SET-ROLLBACK(). END.
The client procedure (a-txclnt.p) establishes the initial transaction object by instantiating a-txtest.p on the server. After calling two database update procedures, the client commits the transaction with a call to stopme. It then begins a new transaction by calling startme, and after calling the same two database update procedures, rolls back the transaction by calling abortme.
As noted in the comments a-txtest.p establishes an unchained transaction object. If the procedure instead establishes a chained transaction object, each time the current transaction terminates (stopme, abortme), the server session automatically starts a new transaction with no need for the client to call a procedure (startme) to initiate it.
a-txclnt.p
DEFINE VARIABLE ph AS HANDLE.
/* Create a transaction initiating procedure and start the transaction */
RUN a-txtest.p ON SERVER h TRANSACTION DISTINCT PERSISTENT SET ph.
RUN custupdate.p ON SERVER h TRANSACTION DISTINCT.
RUN orderupdate.p ON SERVER h TRANSACTION DISTINCT.
/* Causes transaction to commit including both custupdate and orderupdate */
RUN stopme IN ph.
/* Start another transaction by running an internal proc of a-txtest.p. This
starts a new transaction, which is unnecessary if the TRANSACTION-MODE
statement in a-txtest.p specifies the CHAINED option. */
RUN startme IN ph.
RUN custupdate.p ON SERVER h TRANSACTION DISTINCT.
RUN orderupdate.p ON SERVER h TRANSACTION DISTINCT.
/* Causes transaction to rollback including both custupdate and orderupdate */
RUN abortme IN ph. DELETE PROCEDURE ph.
The RUN statements in this example use ABL syntax for executing remote procedures of various types. For more information, see Programming ABL Client Applications.
Note that while these examples show a close correlation between transaction directives and remote procedure requests, there is no requirement that this be so. You can completely hide any sense that the client is ultimately initiating and managing a transaction by hiding all calls to the internal procedures of the transaction initiating procedure within the logic of your application API. For example, you can encapsulate the remote procedure calls in a-txclnt.p in higher-level procedures, such as start-updates.p, rerun-updates.p, and end-updates.p.