This guide walks you through building a simple data-entry form in Oracle Forms from scratch. We will connect to a database, create a data block, design the layout, add validations and buttons, compile, and run the form. The instructions use plain language and focus on the essentials so you can get productive quickly.
Who is this for?
New Oracle Forms developers, and developers coming from tools like Visual FoxPro or MS Access who want a clear, practical path to their first working form.
Prerequisites
- Oracle Database access (any recent version with a schema you can use).
- Oracle Forms Builder installed and configured (commonly Oracle Forms 12c).
- A database user with privileges to create/modify tables or at least to insert/update into an existing table.
- A working tnsnames.ora (or an EZCONNECT string) so Forms Builder can log in.
Tip: If you know Visual FoxPro: think of a Data Block as a data source/buffer like a VFP table/cursor bound to controls; a Canvas is like a form surface; Triggers are event handlers.
Create a Simple Table for the Demo (Optional but Recommended)
If you do not already have a table to work with, run this SQL in your schema:
-- Table CREATE TABLE emp ( emp_id NUMBER PRIMARY KEY, full_name VARCHAR2(100) NOT NULL, email VARCHAR2(100), hire_date DATE DEFAULT SYSDATE NOT NULL, salary NUMBER(10,2), dept_id NUMBER ); -- Sequence for primary key CREATE SEQUENCE emp_seq START WITH 1 INCREMENT BY 1;
You can add foreign keys (e.g., to a
departmentstable) later. For the first form, the above is enough.
Step 1: Start a New Form Module
- Open Oracle Forms Builder.
- In the Object Navigator, right-click Forms → New → choose Form.
- Save the module immediately as
emp_form.fmbin your working folder.
Step 2: Create the Data Block (Data Block Wizard)
- In Object Navigator, right-click Data Blocks → Create.
- Choose Use the Data Block Wizard → Table/View → Next.
- Connect to the database when prompted.
- Select table EMP → move these columns to Items:
EMP_ID(we will set it non-enterable and auto-fill using a sequence)FULL_NAME(required)EMAILHIRE_DATE(default to SYSDATE)SALARYDEPT_ID
- Finish the wizard.
Step 3: Create the Layout (Layout Wizard)
Right after the Data Block Wizard, the Layout Wizard opens:
- Select your data block EMP.
- Choose Content Canvas (default) and Form layout style for single-record entry.
- Select all items.
- Arrange items in two columns if you like; accept defaults for prompt alignment and spacing.
- Finish. Forms Builder creates a Canvas, a Window, and items with labels.
Step 4: Clean Up Item Properties (Property Palette)
For each item (in the Object Navigator or by clicking the item and pressing F4):
- EMP_ID
- Set Item Type: Text Item
- Set Database Item: Yes
- Set Enabled: No (user should not type into the PK)
- Set Insert Allowed / Update Allowed: No
- FULL_NAME
- Set Required: Yes
- Set Maximum Length: 100
- EMAIL
- Set Maximum Length: 100
- HIRE_DATE
- Set Format Mask:
DD-MON-YYYY(or your preferred standard)
- Set Format Mask:
- SALARY
- Set Hint: “Enter positive amount”
- DEPT_ID (we will attach a List of Values in a later step)
Opinion: Keep prompts short and consistent (e.g., Full Name, Hire Date) and use Hint for tooltips. It results in a cleaner UI and fewer user errors.
Step 5: Add an Auto-Number for EMP_ID (PRE-INSERT Trigger)
Instead of a database trigger, we will show the Forms way for learning purposes.
- In Object Navigator, expand Triggers under the EMP block.
- Create a PRE-INSERT trigger:
:emp.emp_id := emp_seq.NEXTVAL; - Compile the trigger (menu Program → Compile). Fix typos if any.
Note: In production, I prefer database-side identity/trigger logic for integrity. For your first form, PRE-INSERT is easier to understand.
Step 6: Add Basic Validations (WHEN-VALIDATE-ITEM)
We will ensure SALARY is not negative:
- Under item EMP.SALARY, create trigger WHEN-VALIDATE-ITEM:
IF :emp.salary IS NOT NULL AND :emp.salary < 0 THEN
MESSAGE('Salary cannot be negative.');
RAISE FORM_TRIGGER_FAILURE;
END IF;
Optional: Validate EMAIL with a simple pattern (basic check):
IF :emp.email IS NOT NULL THEN
IF INSTR(:emp.email, '@') = 0 OR INSTR(:emp.email, '.') = 0 THEN
MESSAGE('Please enter a valid email address.');
RAISE FORM_TRIGGER_FAILURE;
END IF;
END IF;
Step 7: Add a Default on Startup (WHEN-NEW-FORM-INSTANCE)
Set defaults (for example, default HIRE_DATE to today if empty):
Create form-level trigger WHEN-NEW-FORM-INSTANCE:
IF :system.form_status = 'NEW' THEN
IF :emp.hire_date IS NULL THEN
:emp.hire_date := TRUNC(SYSDATE);
END IF;
END IF;
Step 8: Add Buttons (Save, Delete, Clear, Exit)
Create four command buttons on the canvas (use the Button tool). For each:
- BTN_SAVE → WHEN-BUTTON-PRESSED
DO_KEY('COMMIT_FORM'); - BTN_DELETE → WHEN-BUTTON-PRESSED
DO_KEY('DELETE_RECORD'); - BTN_CLEAR → WHEN-BUTTON-PRESSED
CLEAR_FORM; - BTN_EXIT → WHEN-BUTTON-PRESSED
EXIT_FORM(ASK_COMMIT);
Opinion: Use
EXIT_FORM(ASK_COMMIT)to reduce accidental data loss. It is friendlier for new users.
Step 9: Add a List of Values (LOV) for DEPT_ID (Optional but Useful)
If you have a DEPARTMENTS table (e.g., DEPT_ID, DEPT_NAME), provide a picklist.
- Record Group: In Object Navigator → Record Groups → Create → name it
RG_DEPT.
Query:SELECT dept_id, dept_name FROM departments ORDER BY dept_name - LOV: In LOVs → Create → base it on
RG_DEPT.- Display two columns (ID and Name).
- Return value: map
dept_idto EMP.DEPT_ID.
- On item EMP.DEPT_ID, set List of Values to the LOV you created.
- Set item property Validate from List = Yes to restrict to valid values.
Step 10: Compile and Run
- Compile the module: Program → Compile Module (or Ctrl+T).
- Run: Click Run Form (or press Ctrl+R).
Your runtime should open, and the form should be ready for insert/update.
Try it now:
- Press Insert (or navigate to a new record).
- Enter Full Name, Email, Hire Date, Salary, Dept (via LOV).
- Click Save.
- Ensure no errors appear, and confirm that
EMP_IDwas filled.
(Optional) Create a Master-Detail Form
Once you are comfortable, try adding a detail block (e.g., EMP_SKILLS) linked to EMP.EMP_ID.
- Use Data Block Wizard for the child table.
- In the wizard, specify the relation columns (master
EMP.EMP_ID→ detailEMP_SKILLS.EMP_ID). - Choose a Tabular layout for the detail block to show multiple rows.
(Optional) Deploy the Form for Others
For a basic first deployment:
- Generate FMX: Program → Compile Module → choose All to create
emp_form.fmx. - Place FMX on your server in a directory included in FORMS_PATH.
- Ensure your Forms runtime (e.g., WebLogic Managed Server for Forms) is running.
- Add/update a runtime configuration (e.g., in
formswebconfiguration) to point to your module if needed. - Users can launch the form using your standard runtime method (browser-launched runtime or FSAL).
Advice: Keep a consistent build/deploy folder structure. It simplifies maintenance and troubleshooting.
Troubleshooting Checklist
- Cannot connect to DB
Verify your tnsnames entry, network reachability, and credentials. Test the connection in SQL*Plus or SQL Developer. - FRM/ORA errors during Save
Read the full error message. Common causes: missing privileges, invalid date/number formats, unhandled nulls on required columns. - PK not generated
Check the PRE-INSERT trigger compiles and thatemp_seqexists. Make sureEMP_IDis non-enterable. - LOV does not open or is empty
Ensure the Record Group query works in SQL. Rebuild the LOV if the record group name changed. - Item not updating
Check item properties: Database Item should be Yes and Update Allowed should be Yes where appropriate.
Good Practices from Day One
- Consistent formats for dates and numbers (set Format Mask).
- Required property for non-nullable fields to catch issues early.
- Meaningful prompts and hints (clean labels improve user adoption).
- Minimal triggers at first; prefer built-ins like
COMMIT_FORM,CLEAR_FORM. - Keep logic close to data: prefer DB constraints and sequences for integrity; use Forms triggers for UI logic and convenience.
Conclusion
You have built a working Oracle Form that connects to a table, manages primary keys, validates user input, and provides basic navigation and actions. From here, explore master-detail relationships, menus, alerts, parameter forms, and libraries (PLL) to reuse code across modules. Keep your first forms simple and reliable; polish the UI and validations; and move shared rules to the database for robustness.

