How to Save Changes Using Commit Processing in Oracle Forms

What happens when an Oracle Forms 14.1.2 form saves: the commit steps and triggers, audit rows, generated keys, POST, and rollback.

A form collects changes, new, changed, and deleted records in any of its blocks, and saves them together as one database transaction when the user presses Save. Either everything is saved or nothing is.

This guide follows that save step by step in Oracle Forms 14.1.2. It covers the commit triggers and what each is for, writing an audit trail in the same transaction, saving rows whose key the database generates, and the POST, savepoint, and CLEAR_FORM tools for controlling a transaction from code.

Sample Form for This Guide

The examples and screenshots use the sample form CH21_FEES from the Oracle Forms code repository on GitHub. Download it, open it in Forms Builder, and connect as CAREWELL to follow along.

FormFileWhat it shows
CH21_FEESforms/ch21/ch21_fees.fmbCardiology fees with an audit trail written in the same transaction

The forms run against the CareWell Clinic sample schema, which you install first.

The Steps of Commit Processing

COMMIT_FORM, called by the Save key's default action or by your code, saves the form's changes in six steps:

  1. Validate the form: every changed item and record, at every level, as if the cursor were leaving them (WHEN-VALIDATE-ITEM, WHEN-VALIDATE-RECORD).
  2. Fire PRE-COMMIT, once, before any row is written.
  3. Post the changes, block by block: for each deleted record, PRE-DELETE, the DELETE, and POST-DELETE; for each new record, PRE-INSERT, the INSERT, and POST-INSERT; for each changed record, PRE-UPDATE, the UPDATE, and POST-UPDATE.
  4. Fire POST-FORMS-COMMIT, when every row is written but not yet committed.
  5. Commit the database transaction.
  6. Fire POST-DATABASE-COMMIT.

Before step 3, Forms sets a savepoint. If any trigger of steps 2 to 4 fails, or the database rejects a row, Forms rolls back to that savepoint. Nothing of this save is kept, the records stay changed in the form, and the user can correct them and save again. You can watch these steps in a real session in how to trace the firing order of triggers.

What Each Commit Trigger Is For

TriggerUse it for
PRE-COMMITChecks that span blocks, or work that must happen once before the save.
PRE-INSERT, PRE-UPDATE, PRE-DELETEPer-row work before the statement: keys from sequences, audit columns, and checks against the database that must run inside the transaction.
POST-INSERT, POST-UPDATE, POST-DELETEPer-row work after the statement, such as writing related rows.
POST-FORMS-COMMITWork after all rows are written and before the commit, such as a check on the result of the whole save.
POST-DATABASE-COMMITWork after the commit, which can no longer be undone with the save, such as a message or a notification.

The ON- versions (ON-INSERT, ON-UPDATE, ON-DELETE, ON-LOCK, and ON-COMMIT) replace Forms' own statements. They are meant for blocks based on procedures or other data sources. PRE-INSERT for keys is shown in how to populate primary keys from a sequence.

Write an Audit Trail in the Same Transaction

The statements of triggers run in the same transaction as the form's own. The sample fees form changes the consultation fees of the Cardiology doctors, and its PRE-UPDATE on DOCTORS writes a row to AUDIT_LOG for every fee that changes, with the old value from DATABASE_VALUE.

Example (PRE-UPDATE trigger on DOCTORS):

declare
  v_old varchar2(20) := get_item_property('DOCTORS.CONSULT_FEE', database_value);
begin
  if v_old != :doctors.consult_fee then
    insert into audit_log (table_name, row_key, action, details)
    values ('DOCTORS', :doctors.doctor_id, 'UPDATE',
            'consult_fee ' || v_old || ' -> ' || :doctors.consult_fee);
  end if;
end;

If the save fails, the audit rows are rolled back with it; if it succeeds, they are committed with it. The form's KEY-COMMIT saves and, when everything was saved, queries the audit block again so it shows the new rows.

Example (KEY-COMMIT trigger on the form):

begin
  commit_form;
  if :system.form_status = 'QUERY' then        -- everything was saved
    go_block('AUDIT_LOG');
    execute_query;                             -- show the new audit rows
    go_block('DOCTORS');
  end if;
end;

:SYSTEM.FORM_STATUS is QUERY after a successful save, because no changes remain. Here, two fees changed from 80 to 85 and were saved, with their audit rows. CHANGED_BY and CHANGED_ON were filled by the columns' defaults, USER and SYSDATE.

Oracle Forms fees form with two changed fees and the audit rows written in the same transaction
Two fees changed and saved, with the audit rows written in the same transaction.

Save Rows Whose Key the Database Generates

AUDIT_LOG.AUDIT_ID is an identity column, GENERATED ALWAYS. The database assigns it and refuses any value, even null, in an INSERT. A block on the table must therefore leave the column out of its inserts, but an ordinary database item does not: Forms lists every database item in its INSERT, and the save fails.

Output:

ORA-32795: cannot insert into a generated always identity column
Oracle Forms error ORA-32795 when inserting into an identity column
Forms inserting into an identity column.

Query Only and DML Returning Value

Two properties solve it:

  • The item's Query Only is Yes: Forms queries the column but never inserts or updates it.
  • The block's DML Returning Value is Yes: Forms adds a RETURNING clause to its statements and puts back into the items the values the database set, such as the new identity and the columns' defaults.

With both, a note typed in the audit block is saved, and its new AUDIT_ID appears in the item.

Oracle Forms showing the identity value the database assigned to a new audit row
A new audit row, with the identity value the database assigned.

Check the audit rows:

select audit_id, table_name, row_key, action, details from audit_log order by audit_id;

Output:

  AUDIT_ID TABLE_NAME ROW_KEY ACTION DETAILS
---------- ---------- ------- ------ ------------------------------------------
         1 DOCTORS    1004    UPDATE consult_fee 80 -> 85
         2 DOCTORS    1005    UPDATE consult_fee 80 -> 85
         3 DOCTORS    1005    NOTE   Fee raised after the review of 1 October

The same approach works for keys set by database triggers, and for columns with defaults the form should show after the save.

POST, Savepoints, and Rollback

POST

Syntax:

post

POST writes the form's changes, steps 1 to 4 of commit processing, without the commit. The rows are in the database, visible to the form's own session and locked, but not committed. Use it before calling another form that must see the changes, or before code that queries the rows just written.

Savepoints and CLEAR_FORM

The form's Savepoint Mode property (SAVEPOINT_MODE for SET_FORM_PROPERTY) lets Forms set savepoints at the start of each save and when a form calls another, so a failure rolls back only the work since then.

In form code, a ROLLBACK statement is treated as CLEAR_FORM with no arguments, and a COMMIT statement as COMMIT_FORM. To undo the user's unsaved changes, call CLEAR_FORM directly.

Syntax:

clear_form [(commit_mode number [, rollback_mode number])]
  • CLEAR_FORM(NO_VALIDATE, FULL_ROLLBACK) discards every change and rolls back the database transaction, without asking.
  • ASK_COMMIT, the default, asks whether to save first.
  • CLEAR_BLOCK and CLEAR_RECORD do the same for one block or record.
  • ISSUE_ROLLBACK and ISSUE_SAVEPOINT exist for ON-ROLLBACK and ON-SAVEPOINT triggers.

For CLEAR_BLOCK in practice, see CLEAR_BLOCK in Oracle Forms. What happens when two users change the same rows is covered in how to handle record locking in Oracle Forms.

Conclusion

COMMIT_FORM validates the form, fires PRE-COMMIT, posts deletes, inserts, and updates with their PRE- and POST- triggers, fires POST-FORMS-COMMIT, commits, and fires POST-DATABASE-COMMIT, rolling back to a savepoint if anything fails. Because trigger statements run in the form's transaction, audit rows and other related rows are saved, or not, together with the form's changes. Give identity and trigger-set columns Query Only items and turn on DML Returning Value, use POST to write without committing, and CLEAR_FORM to discard changes.

Vinish Kapoor
Vinish Kapoor

An Oracle ACE and software veteran with 25+ years of experience, passionate about AI and IT innovation.

guest

0 Comments
Oldest
Newest Most Voted
00