The following demonstrates a simple batch transaction, transfering money from one account to another. The account table is indexed on the account number.

function transferButton_onClick

   try 

      form.rowset.parent.database.beginTrans( ) 

      if form.transferAmt.value <= 0 

         throw new AppException( "Transfer amount must be greater than zero" ) 

      endif 

      do ; until form.rowset.lockSet( ) // Wait for set lock to prevent deadlock 

      if form.rowset.find( form.transferFrom.value ) 

         form.rowset.fields[ "Balance" ].value -= form.transferAmt.value 

      else 

         throw new AppException( "Transfer From account not found" ) 

      endif 

      if form.rowset.find( form.transferTo.value ) 

         form.rowset.fields[ "Balance" ].value += form.transferAmt.value 

      else 

         throw new AppException( "Transfer To account not found" ) 

      endif 

      form.rowset.save( ) 

      form.rowset.parent.database.commit( ) 

   catch ( AppException e ) 

      form.rowset.parent.database.rollback( ) 

      msgbox( e.message, "Tranfer failed!", 48 ) 

   catch ( Exception e ) 

      form.rowset.parent.database.rollback( ) 

   logException( e ) 

      #ifdef DEBUG 

         throw e 

      #else 

         fatalError( e ) 

      #endif 

   endtry 

As the example demonstrates, there are quite a few things that can go wrong with the transaction. These are separated into application logic errors, and all other unexpected errors. A custom class, AppException, is used for the application errors. It provides an easy way to THROW an exception with an error message.

The debit to the first row is saved when calling findKey( ) to find the second row. The credit to the second row must be saved before committing the transaction. If an application logic exception occurs, whatever is done so far (if anything) is rolled back. Then a dialog box is displayed, detailing the error. The user can try again. For all other exceptions, a function is called to log the exception for debugging purposes. Then the exception is either THROWn so that the standard error dialog appears, if the application is compiled with the debug flag on; or another function is called that details the error for the user before telling them they have to terminate the application.

logException( ) and fatalError( ) are custom functions that are left as an exercise for the developer. Here is the code for the AppException class:

class AppException( cMsg, nCode ) of Exception

   if argcount( ) < 2 

      nCode := -1 

      if argcount( ) < 1 

         cMsg := "Application logic exception" 

      endif 

   endif 

   this.code := nCode 

   this.message := cMsg 

endclass