Microsoft Dynamic AX 2009 : The Database Layer – Database-Triggering Methods (part 2) – Changing the Default Behavior

4. Changing the Default Behavior

The record buffer contains a dozen methods used to change the default behavior of DML statements issued in X++ code. You can call all the methods except one, concurrencyModel, with a Boolean parameter to change the default behavior, and you can call all of them without a parameter to query the current status. None of the methods can be overridden.

The following methods on the record buffer influence how the application runtime interprets select statements that use the record buffer.

SelectForUpdate

Calling selectForUpdate(true) on a record buffer replaces the use of the forupdate keyword in a select statement. The X++ code

custTable.selectForUpdate(true);
select custTable where custTable.AccountNum == '4000';

 

is equal in behavior to this code

select forupdate custTable where custTable.AccountNum == '4000';

 

Depending on the concurrency model settings on the table, no hint or UPDLOCK hint is added to the SELECT statement passed to SQL Server 2005.

Tip

If you use the Query framework instead of select statements to retrieve records, you can also retrieve these records as if a forupdate keyword had been used, by calling update(true) on the QueryBuildDataSource object.

concurrencyModel

Calling concurrencyModel(ConcurrencyModel::OptimisticLock) on a record buffer replaces the use of the optimisticlock keyword, and calling concurrencyModel(ConcurrencyModel:: PessimisticLock) replaces the use of the pessimisticlock keyword. The X++ code

custTable.concurrencyModel(ConcurrencyModel::OptimisticLock);
select custTable where custTable.AccountNum == '4000';

 

is equal in behavior to this code

select optimisticlock custTable where custTable.AccountNum == '4000';

 

This method overrules any concurrency model setting on the table and causes either no hint or a UPDLOCK hint to the SELECT statement passed to SQL Server 2005. The type of hint depends on whether the OptimisticLock or PessimisticLock enumeration value was passed as a parameter when the application logic called the concurrencyModel method.

Tip

If you use the Query framework instead of select statements to retrieve records, you can retrieve these records with a specific concurrency model by calling concurrencyModel (ConcurrencyModel::OptimisticLock) or concurrencyModel(ConcurrencyModel::PessimisticLock) on the QueryBuildDataSource object.

selectWithRepeatableRead

Calling selectWithRepeatableRead(true) on a record buffer replaces the use of the repeatableread keyword in a select statement. The X++ code

custTable.selectWithRepeatableRead(true);
select custTable where custTable.AccountNum == '4000';

 

is equal in behavior to this code

select repeatableread custTable where custTable.AccountNum == '4000';

 

Using this keyword results in the addition of a REPEATABLEREAD hint to the SELECT statement passed to SQL Server 2005.

Tip

If you use the Query framework instead of select statements to retrieve records, you can retrieve these records with a REPEATABLEREAD hint as well, by calling selectWithRepeatableRead (true) on the QueryBuildDataSource object.

readPast

When readPast(true) is called on a record buffer, a READPAST ROWLOCK hint is added to the SELECT statement passed to SQL Server 2005. This hint instructs the database to skip rows on which an exclusive lock is held. But because RCSI is enabled, SQL Server 2005 doesn’t skip records on which an exclusive lock is held; it only returns the previous committed version.

skipTTSCheck

The record buffer also contains a method that affects the behavior of updates and deletes. Calling skipTTSCheck(true) on a record buffer makes it possible to later call update or delete on the record buffer without first selecting the record for update. The following code, in which a custTable record is selected without a forupdateskipTTSCheck set to true, doesn’t fail. keyword and is later updated with

static void skipTTSCheck(Args _args)
{
    CustTable custTable;

    ttsbegin;

    select custTable where custTable.AccountNum == '1101';
    custTable.CreditMax = 1000;

    custTable.skipTTSCheck(true);
    custTable.update();

    ttscommit;

}

 

The execution of update method doesn’t throw an error in this example because the Dynamics AX application runtime doesn’t verify that the buffer was selected with forupdate or an equivalent keyword. In a pessimistic concurrency scenario, no update lock would be acquired before the update, and in an optimistic concurrency scenario, the RecVersion check wouldn’t be made. 

If skipTTSCheck hadn’t been called in the preceding scenario, the application runtime would have thrown an error and presented the following in the Infolog: “The operation cannot be completed, since the record was not selected for update. Remember TTSBEGIN/TTSCOMMIT as well as the FORUPDATE clause.”

selectLocked

The selectLocked record buffer method is essentially obsolete because executing selectLocked(false) on a record buffer before selecting any rows with it has no effect. Records will not be read uncommitted.

5. Set-Based DML Statements

As explained in the preceding sections, insertupdate, and delete methods are available on the buffer to manipulate data in the database. The buffer also offers the less frequently used write, doInsert, doUpdate, and doDelete methods for use when writing application logic in X++ code. All these methods are record-based methods; when they are executed, at least one statement is sent to the database, representing the INSERTUPDATE, or DELETE statement being executed in the database. Each execution of these statements therefore results in a call from the AOS to the database server in addition to previous calls to select and retrieve the records.

X++ contains set-based insertupdate, and delete operators as well as set-based classes that can reduce the number of round-trips made from the AOS to the database tier. The Dynamics AX application runtime can downgrade these set-based operations to row-based statements because of metadata setup, overriding of methods, or configuration of the Dynamics AX application. The record buffer, however, offers methods to change this behavior and prevent downgrading.