Save method: Record class

Syntax

Save([CopyToOriginal])

Description

The Save method saves the record to the database in two steps.

  1. Check to see that it is safe to save this record to the database.

  2. Either insert this record or update an existing record.

To accomplish the first step and determine that it is safe to save the record, this method uses similar logic to that used in the component processor when it saves a record. To do that a “select for update” SQL statement is executed. If, as a result of this statement, the record does not exist in the database, this method simply inserts the record into the database. All the processing that occurs for the insert method then occurs.

If the result of the “select for update” statement returns a record, the values of all the fields in the database record are compared to the original values of the record executing this method. If they are equal, indicating that no other user has updated the record, the record in the database is updated. All the processing that occurs for the update method then occurs. If the result of the above comparison fails, indicating that there has been an update of the database record in the meantime, perhaps by some other user, the save method fails and returns a false result.

For the update process to succeed, the record object must contain both original and changed values of the data. It is crucial to get data loaded into the record object correctly. To do this, do one of the following:

  • Derive the record object from the component buffers.

  • Derive the record object from a rowset filled using the Fill method.

  • Load the data using the record class SelectByKey and SelectByKeyEffdt methods specifying the optional Boolean parameter to be False.

See the note and example below.

Of course when you use Save to simply insert data you do not need load data into the record object this way.

Use this method when you are writing applications that do not use the facilities that come predelivered with the component processor and you must deal with issues of data contention.

For example, suppose you are writing an application that deals with course registration. You have to handle changing the number of students who are registered carefully if it is possible for more than one user to be registering for the same course at the same time. The normal way is to use the component processor to handle data contention by placing the record that deals with the number of students registering on some page. Then if two users try to update the same record one of them receives a message indicating that the page has been updated by another user.

If you write an application, outside the component processor, that relies on the record class to manage the number of students enrolled, it is not sufficient to use the record class Insert or Update methods to maintain data integrity. Suppose one user registers for the course and sees there are 10 other registrants. At the same time another user registers for the course and sees that there are also 10 registrants. Suppose the maximum is 11 students. Both users at this point believe they can register since at the point they accessed the data from the database there were only 10 students. So far, so good. The trouble comes when they save their registrations. If the application uses the update method, after simply adding one to the number of registrants, both updates will succeed indicating there are now 11 students in the course when in fact there are now 12. It is for cases such as these that the Save method applies since it guarantees that one of the save calls will fail. It is up to the application then to let the user know that while they were thinking about it someone else registered for the course and took the last spot.

The Save method is important to maintain data integrity. However, it can execute more slowly than saving within the component processor.

Since this method results in a database change, and does either insert or update processing, all the considerations and implications of using the Insert or Update methods apply. See the description of those methods for more detail.

The parameter CopyToOriginal indicates what action should occur on a successful save. If this parameter is true, the system copies the current changed buffers for the record into the original buffers for the record. This means that subsequent Saves, without reloading data from the database will run correctly. However if there are database specific updates to this record occurring outside of this application's control (such as, using database specific triggers,) it is always advisable to reload the data from the database.

The default value for this optional parameter is false, indicating that no update of the original buffers for this record occurs. If you are certain that there are no database updates occurring outside of your application's on save processing, you can specify this optional parameter to be true and save the overhead of having to reload the data for this record from the database. If you are unsure you should always reload the record data after calling the Save method.

Note:

This method does not update fields marked as System maintained in the record definition in Application Designer.

Since this save is done outside the component processor save processing, no save processing PeopleCode that is associated with this record is executed.

Use of this method that results in an update, and requires that the data in the record object is such that both a before and after image of the data is kept. When the system loads data into the component buffers there is a concept of the “original data” (data loaded from the database) and “changed data” (data changed by the user or the application). This allows the system to determine when saving, whether the data has been changed by another user. If you use this method with record objects not derived from the component buffers you need to make sure that the data in the record object has both a before and after image otherwise the save method fails. Record objects derived from rowsets that have been populated using the Fill method will not produce an error. However if you load data into a record object using either the SelectByKey or SelectByKeyEffdt record class methods, you need to specify the CopyToOriginal parameter so the system will load the data into the original data buffers, not the changed data buffers. To do that specify the optional parameter to be False. See the example below.

Parameters

Parameter Description

CopyToOriginal

Specify what action should occur on a successful save. This parameter takes a Boolean value: true, then the system copies the current changed buffers for the record into the original buffers for the record, false, the buffers are not copied. The default value is false.

Returns

The result is True on successful completion, False if there were no changed fields in the record or it was not safe to save this record (see above for details). Any other conditions cause termination.

Note:

It is not possible to ignore the return code from the Save method in your PeopleCode.

Example

Suppose that KEYF1 and KEYF2 are the key fields of record definition MYRECORD, and that this record definition also contains the record field definitions MYRF3, MYRF4. The following code tries to save MYRECORD record in the database with KEYF1 set to "A", KEYF2 set to "B", setting MYRF3 to "X" and MYRF4 to "Y". This example works with a brand new record object and results in an insert if successful.

/* First -- create a brand new record */
Local record &REC;
&REC = CreateRecord(RECORD.MYRECORD);
&REC.KEYF1.Value = "A";
&REC.KEYF2.Value = "B";

/* Second -- update values as needed */
&REC.MYRF3.Value = "X";
&REC.MYRF4.Value = "Y";
/* Finally save it */
if &REC.Save() then
    /* handle the successful return */
else
    /* handle the unsuccessful return message to user? Try again? */
end-if;

This example works with an existing record and results in an update if successful.

/* First - create the record and get the data from the db */
Local record &REC;
&REC = CreateRecord(RECORD.MYRECORD);
&REC.KEYF1.Value = "A";
&REC.KEYF2.Value = "B";
&REC.SelectByKey(False); 
/* False retrieves data into the original, not changed buffers. */

/* Second - update values as needed */
&REC.MYRF3.Value = "X";
&REC.MYRF4.Value = "Y";
/* Finally save it */
if &REC.Save() then
    /* handle the successful return */
else
    /* handle the unsuccessful return - message to user? Try again? */
end-if;