Sie sind auf Seite 1von 5

Benefits of Using Oracle Forms' Stored Procedure Data Blocks

By Art Collins, OCP Introduction

Friday, December 23, 2011

With all the rich features Oracle Forms offer a developer, one of the least understood and least utilized functionality is the use of stored procedure data blocks. This functionality is really not new. It was first introduced in Forms version 5.0, but has received very little press coverage so far. This article will shed some light on stored procedure data blocks, describing why they are useful, and how to implement them.
View the source code for this article The Problem

The traditional method of creating a data block has always been a simple but constrained process. Prior to version 5.0, the only way to generate one was to base it from a single table. This technique does not meet the real-world needs of relational database systems, since most forms require data from multiple tables (often requiring complex joins). Experienced developers discovered two basic ways to circumvent this problem: 1. Create a database view from these complex joins, or 2. Write queries in post-query trigger to fetch the remaining data not already retrieved from the base table. These two solutions have some serious drawbacks. First, creating a view for each form is not always possible. Since a view is basically a SQL statement, there will be instances where it's not possible to retrieve all the data in one well-behaved optimized SQL (for example, when extensive data processing needs to be done to compute columns). Data modification is also an issue because updatable views have restrictions. As a result of these constraints, the second solution of writing post-query trigger is a more common one. The biggest drawback to this approach is performance. Within the trigger, each query fires separately for every row, causing excessive network traffic between the client and server computers. Illustration: Let's take as an example a table data block with 2 additional queries in the postquery trigger. In a query where 5 rows are being retrieved into the form, a post-query trigger will fire 5 times (one for each row), and each trigger performs the 2 queries. That is 3 fetches per row, which result in 15 fetches for 5 rows of data. No wonder most complex forms are slow to fetch data. There are a lot of queries going on there!
A Viable Solution

With the release of Forms version 5, there is another alternative that is much cleaner and more efficient. This is the use of stored procedure data block. Like its name implies, instead of basing a data block from a single table, the data block is now based on a stored procedure in the database. The basic concept involves writing a database procedure to do the query (or queries), populating the result in a PL/SQL table, and passing it to Forms so it can populate the data block. To provide update, delete, and insert capabilities, the opposite happens. Forms pass a PL/SQL table to an update, delete, or insert procedure in the database. The procedure loops through all the rows in the PL/SQL table and applies the appropriate changes to the tables. Thus, a stored procedure data block, which needs to have the ability to query, update, insert, and delete data, requires separate procedures (all implemented in a package) to perform each one of these functions. All this extra work may seem rather tedious, but the following benefits illustrate why stored procedure data blocks are a better choice.

y y y

Increased Security and Query Standards. At their most extreme, the developer can use stored procedure data blocks to create a 'middle tier' or 'data access layer' by writing sets of procedures to access and manipulate the data. This approach limits Forms data access to through these procedures and eliminates the need of writing SQL in Forms (with the exception of LOVs, which still require SQL definition). Reduced Network Traffic. Since Forms is just sending or receiving a PL/SQL table from the database, network traffic is reduced. There is no post-query trigger code to deal with. Sharing Across Forms. The same stored procedure may be shared across different Form modules. This increases reusability and improves application maintenance. Performance, Performance, Performance. Since the entire query is performed in the database, it may be easily optimized. Combine this with the efficiency gained by having only one 'round trip' data transfer between the database and Forms, and overall performance is greatly increased.

It is important to keep in mind that this is only a single approach, which may or may not be applicable for all programming needs. But in situations where a data block will fetch and modify information from multiple tables, using a stored procedure data block may be the easiest and most efficient solution.

A form needs to be created to query and manipulate (update, insert, and delete) employee data. This data comes from two tables with a 1 to 1 relationship.
Employee table Name Emp_id First_name Last_name Null? not null not null not null Type nnumber(10) varchar2(20) varchar2(20)

Employee_confidential table Name Null? Emp_id not null

Type number(10)

Ssn Salary

not null not null

varchar2(9) number(10)

To create a procedure to perform the query, a PL/SQL table needs to be defined. This is the parameter that will get passed from the database to Forms. Since it is easier to group all these procedures into a package, we will do so as follows.
Create or replace package employee_pkg is type emp_rec is record (emp_id employee.emp_id%type, first_name employee.first_name%type, last_name employee.last_name%type, ssn employee_confidential.ssn%type, salary employee_confidential.salary%type); type emp_tbl is table of emp_rec index by binary_integer;

Notice that emp_rec is nothing more than the columns which need to be queried. These columns are what will appear on the form. emp_tbl is the PL/SQL table made up of emp_recrecords. Now that the PL/SQL table type is defined, creating the query procedure is a simple task. Simply declare an emp_tbl argument as IN OUT. This PL/SQL table will be populated by the query and passed back to the form. Add additional parameters as needed to perform the query. In this particular case, p_emp_id is needed because the query looks for a specific employee id.
procedure do_query(p_emp_table IN OUT emp_tbl, p_emp_id IN employee.emp_id%type) is cursor emp_cur (p_id employee.emp_id%type) is select e.emp_id, e.first_name, e.last_name, c.ssn, c.salary from employee e, employee_confidential c where e.emp_id = c.emp_id and e.emp_id = p_id; idx number := 1; begin for rec in emp_cur(p_emp_id) loop -- populate a PL/SQL table with a record p_emp_table(idx) := rec; -- increment index idx := idx + 1; end loop; end;

That is it for the query! do_query procedure is called from Forms with the specified emp_id. The procedure then populates p_emp_table PL/SQL table and passes it back to the form for

display. There is another method of writing a query procedure using cursor variables instead of a PL/SQL table. Its implementation is relatively straightforward and an example code is provided in the complete listing below. If the data block needs data manipulation capability, then insert, update, and delete procedures need to be created. The insert procedure requires putting records into both employee andemployee_confidential tables (remember that it's a 1 to 1 relationship). When the forms 'Insert-Procedure' trigger is fired after the user inserts a record, a PL/SQL table is sent to the insert stored procedure where the actual insert queries are issued.
procedure do_insert(p_emp_table IN OUT emp_tbl) is numOfRows number; begin numOfRows := p_emp_table.count; for idx in 1 .. numOfRows loop insert into employee (emp_id, first_name, last_name) values (p_emp_table(idx).emp_id, p_emp_table(idx).first_name, p_emp_table(idx).last_name); insert into employee_confidential (emp_id, ssn, salary) values (p_emp_table(idx).emp_id, p_emp_table(idx).ssn, p_emp_table(idx).salary); end loop; end ;

The procedure simply loops through all the records in the PL/SQL and performs inserts into the two tables. Code for update and delete procedures are very similar to this insert except update and delete statements would be issued. One final note, if an update or delete procedure is created, a lock procedure needs to be created as well. This procedure is called to lock a record in the database when the user attempts to modify it. The logic is the same as the update or delete procedure, which requires looping through all the records in the PL/SQL table and issue a select ... for update nowait; statement for each affected table. Once the stored procedures are successfully compiled in Oracle, it's time to create a Form module to access them. Simply open up the Data Block Wizard and select Stored Procedure. The Wizard will prompt for the stored procedures one at a time (query, insert, update, delete, and lock). When Forms locates the procedure, it automatically retrieves the column definitions from the PL/SQL table as well as any procedure parameters. Here you may specify binding variables for these parameters. Once the form is compiled, it will automatically generate triggers

depending on what stored procedures were used. They are:

y y y y y

Query-procedure Insert-procedure Update-procedure Delete-procedure Lock-procedure

Do not attempt to modify code in these triggers because they are automatically generated every time Form compiles. Modifications may be done through the following block properties:
y y

Query Data Source Columns - lists the column definition of the PL/SQL table Query Data Source Arguments - lists the parameters to the query procedure

Once these properties are modified, recompile the form, and changes should be reflected in the data block triggers. The foregoing may seem quite complicated at first, but it is really straightforward after you try it. In summary, the first step is to write the stored procedures to access or manipulate your data. The second step is to create a data block that references these procedures. You will see that once you design one stored procedure block, all the rest are just slight variations. So go ahead, play with it, have fun, and most of all, good luck!