The concept of basing a block on a stored procedure is introduced in Chapter 2, "Advanced GUI Development: Developing Beyond GUI." The same method is illustrated here with reference to object tables.
A block has to be based on a stored procedure when it is necessary to provide user -defined logic for replacing the default functionality of SELECT, LOCK, INSERT, UPDATE, and DELETE. This is required in the following circumstances:
This method of basing a block on a stored procedure involves getting and returning result sets of data rather than processing one record at a time, thus reducing network traffic. This is helpful especially when the network involved is a WAN. Similar to the method of basing a block on a stored procedure involving non-object relational tables, this method also involves getting and returning result sets of data in the form of Index-by table of records.
The basic building blocks and the steps involved are as follows :
CREATE OR REPLACE TYPE dept_emp AS OBJECT (empno NUMBER(4), ename VARCHAR2(10), job VARCHAR2(9), hiredate DATE, sal NUMBER(11,2), deptno NUMBER(2), dname VARCHAR2(14));
CREATE TABLE deptemp_tab OF dept_emp;
CREATE TABLE dept_emp_addr (deptemp REF dept_emp, addr REF add_type);
Tip
Data blocks can be based on a stored procedure returning a ref cursor, an index-by table, records, or a record type.
Similarly, under Advanced Database, set the corresponding properties for Insert, Update, Delete, and Lock accordingly .
DML is performed internally on the object/object-relational tables DEPTEMP_TAB and DEPT_EMP_ADDR.
The code for the package PKGDEPTEMPOBJ can be found in Listing 10.1.
Listing 10.1 Package with procedures for Query, Lock, Insert, Update and Delete operations involving Object Tables.
CREATE OR REPLACE PACKAGE PKGDEPTEMPOBJ AS /* Variables which are of type of an index-by table of records, are used as /IN OUT variables for the INSERT, UPDATE, DELETE and LOCK procedures. These /parameters are IN OUT as they transfer data to and from the block and the /database. */ TYPE dept_emp_rec IS RECORD (empno NUMBER(4), ename VARCHAR2(10), job VARCHAR2(9), hiredate DATE, sal NUMBER(11,2), deptno NUMBER(4), dname VARCHAR2(14)); TYPE dept_emp_tab IS TABLE OF dept_emp_rec INDEX BY BINARY_INTEGER; PROCEDURE query_procedure(resultset IN OUT dept_emp_tab, p_empno IN NUMBER); PROCEDURE lock_procedure(dmlset IN OUT dept_emp_tab); PROCEDURE insert_procedure (dmlset IN OUT dept_emp_tab); PROCEDURE update_procedure(dmlset IN OUT dept_emp_tab); PROCEDURE delete_procedure(dmlset IN OUT dept_emp_tab); END PKGDEPTEMPOBJ; / CREATE OR REPLACE PACKAGE BODY PKGDEPTEMPOBJ AS PROCEDURE query_procedure(resultset IN OUT dept_emp_tab, p_empno IN NUMBER) IS CURSOR c_dept_emp IS SELECT DEREF(a.deptemp) FROM dept_emp_addr a WHERE a.deptemp.empno = NVL(p_empno, a.deptemp.empno); i BINARY_INTEGER := 1; v_deptemp dept_emp; BEGIN /* The code below selects from the DEPT_EMP_ADDR tables into a cursor based / on dept_emp object type and outputs the result to the block. Note how the /DEREF operator is used to get the actual record rather than the OID. The /co-ordination and synchronization between the input resultset and the /population of the block with these records is done by Forms automatically */ OPEN c_dept_emp; LOOP FETCH c_dept_emp INTO v_deptemp; EXIT WHEN c_dept_emp%NOTFOUND; resultset(i).empno := v_deptemp.empno; resultset(i).ename := v_deptemp.ename; resultset(i).job := v_deptemp.job; resultset(i).hiredate := v_deptemp.hiredate; resultset(i).sal := v_deptemp.sal; resultset(i).deptno := v_deptemp.deptno; resultset(i).dname := v_deptemp.dname; i := i+1; END LOOP; EXCEPTION WHEN OTHERS THENRAISE_APPLICATION_ERROR(-20101, SQLERRM); END query_procedure; PROCEDURE lock_procedure(dmlset IN OUT dept_emp_tab) IS v_deptemp dept_emp; BEGIN /* The following locks each record in the input dmlset. The co-ordination and / synchronization between the input dmlset and the block's records that have /been marked for LOCKING is done by the LOCK-PROCEDURE trigger written by /Forms. Again note the use of the DEREF operator. */ FOR i IN 1..dmlset.COUNT LOOP SELECT DEREF(a.deptemp) INTO v_deptemp FROM dept_emp_addr a WHERE a.deptemp.empno = dmlset(i).empno FOR UPDATE; END LOOP; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20102, SQLERRM); END lock_procedure; PROCEDURE insert_procedure (dmlset IN OUT dept_emp_tab) IS CURSOR c_dept_emp(i BINARY_INTEGER) IS SELECT DEREF(a.deptemp) FROM dept_emp_addr a WHERE a.deptemp.empno=dmlset(i).empno; v_deptemp dept_emp; BEGIN /* The following inserts into the DEPTEMP_TAB table if already not found. It /also inserts into the DEPT_EMP_ADDR table. Note that the actual OID is /inserted into the DEPT_EMP_ADDR table for the deptemp REF column. Each record / in the input dmlset is inserted. The co-ordination and synchronization /between the input dmlset and the block's records that have been marked for /INSERT is done by the INSERT-PROCEDURE trigger written by Forms */ FOR i IN 1 .. dmlset.COUNT LOOP OPEN c_dept_emp(i); FETCH c_dept_emp INTO v_deptemp; IF c_dept_emp%NOTFOUND THEN INSERT INTO deptemp_tab VALUES (dmlset(i).empno,dmlset(i).ename, dmlset(i).job, dmlset(i).hiredate, dmlset(i).sal, dmlset(i).deptno,dmlset(i).dname); INSERT INTO dept_emp_addr(deptemp) SELECT REF(a) FROM deptemp_tab a WHERE a.empno = dmlset(i).empno; END IF; CLOSE c_dept_emp; END LOOP; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20103, SQLERRM); END insert_procedure; PROCEDURE update_procedure(dmlset IN OUT dept_emp_tab) IS CURSOR c_dept_emp(i BINARY_INTEGER) IS SELECT DEREF(a.deptemp) FROM dept_emp_addr a WHERE a.deptemp.empno = dmlset(i).empno; v_deptemp dept_emp; BEGIN /* The following inserts into the DEPTEMP_TAB table if already not found, else / it updates the same table. Each record in the input dmlset is updated. The /co-ordination and synchronization between the input dmlset and the block's /records that have been marked for UPDATE is done by the UPDATE-PROCEDURE /trigger written by Forms */ FOR i IN 1..dmlset.COUNT LOOP OPEN c_dept_emp(i); FETCH c_dept_emp INTO v_deptemp; IF c_dept_emp%NOTFOUND THEN INSERT INTO deptemp_tab VALUES (dmlset(i).empno,dmlset(i).ename, dmlset(i).job, dmlset(i).hiredate, dmlset(i).sal, dmlset(i).deptno,dmlset(i).dname); ELSE UPDATE deptemp_tab a SET ename = dmlset(i).ename, job=dmlset(i).job, hiredate=dmlset(i).hiredate, sal =dmlset(i).sal WHERE a.empno = dmlset(i).empno; END IF; CLOSE c_dept_emp; END LOOP; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20104, SQLERRM); END update_procedure; PROCEDURE delete_procedure(dmlset IN OUT dept_emp_tab) IS v_deptemp dept_emp; BEGIN /* The following deletes from the DEPT_EMP_ADDR table. Note how the DEREF /operator is used to find the matching record. Each record in the input dmlset / is deleted. The co-ordination and synchronization between the input dmlset /and the block's records that have been marked for DELETE is done by the /DELETE-PROCEDURE trigger written by Forms */ FOR i IN 1..dmlset.COUNT LOOP SELECT DEREF(a.deptemp) INTO v_deptemp FROM dept_emp_addr a WHERE a.deptemp.empno = dmlset(i).empno; DELETE FROM dept_emp_addr a WHERE DEREF(a.deptemp) = v_deptemp; END LOOP; EXCEPTION WHEN OTHERS THEN RAISE_APPLICATION_ERROR(-20105, SQLERRM); END delete_procedure; END PKGDEPTEMPOBJ;
Tip
OIDS are generated automatically when the INSERT_PROCEDURE is invoked at the server side.
Tip
It is not necessary to set the DML Returning Value because the procedures use DEREF to get the actual row values, and hence are not based on the OID.
GUI Development
Advanced GUI Development: Developing Beyond GUI
Multi-form Applications
Advanced Forms Programming
Error-Message Handling
Object-oriented Methods in Forms
Intelligence in Forms
Additional Interesting Techniques
Working with Trees
Oracle 8 and 8i Features in Forms Developer