You are on page 1of 6

Using Java Stored Procedures

File I/O has greater capabilities with Java than with UTL_FILE such as:
• More fine-grained permission policy while reading and writing files to
the OS.
in Oracle 9i • Recursive directories can be specified
• Ability to handle both text and binary files unlike UTL_FILE that can handle
only text files.
By Bulusu Lakshman • Ability to create directory or list contents of directory

Defining the Database Schema Along with Sample Data

T
his article highlights the method of using Java Stored This article will use the following tables in a schema named plsql9i/plsql9i:
Procedures in Oracle9i, including new techniques
introduced in Oracle9i. It also briefly discusses the create table site_tab
( site_no number(4) not null,
capabilities of Java Stored Procedures in Oracle Forms 6i. site_descr varchar2(20) not null);

Code samples are provided to help developers implement alter table site_tab add primary key (site_no);

the concepts. create table hrc_tab


(hrc_code number(4) not null,
hrc_descr varchar2(20) not null);

alter table hrc_tab add primary key (hrc_code);


Java Stored Procedures were introduced along with Java in the Oracle 8i create table org_tab
database. Oracle 8i extended PL/SQL capabilities with the ability to store Java (hrc_code number(4) not null,
in the database in two ways: Java Stored Procedures and JDBC. Oracle 9i org_id NUMBER(8) not null,
enhanced the capability of Java Stored Procedures to return a ResultSet. org_short_name varchar2(30) not null,
org_long_name varchar2(60) );
Forms 6i’s enhanced features include the ability to call Java Stored alter table org_tab add primary key (hrc_code, org_id);
Procedures from Forms. Forms can also use JDBC calls from Java Stored alter table org_tab add constraint org_tab_uk unique (org_id);
procedures. This article discusses how to create and execute Java Stored alter table org_tab add constraint org_tab_fk foreign key (hrc_code)
Procedures in Oracle 9i and well as return a ResultSet from a Java Stored references hrc_tab(hrc_code);
Procedure in Oracle 9i. It also highlights the use of Java Stored Procedures create table org_site_tab
and JDBC from Forms. Code examples are provided to help readers imple- ( org_id number(8) not null,
ment the technique. site_no number(4) not null );

alter table org_site_tab add primary key (org_id, site_no);


Why Java in the Database? alter table org_site_tab add constraint org_site_tab_fk1 foreign key (org_id)
references org_tab(org_id);
Oracle 9i provides a seamless environment where Java and PL/SQL can alter table org_site_tab add constraint org_site_tab_fk2 foreign key (site_no)
interact as two major database languages. There are many advantages to references site_tab(site_no);
using both languages are many.
PL/SQL advantages include:
• Intensive Database Access – It is faster than Java.
• Oracle Specific Functionality that has no equivalent in Java such as using
DBMS_LOCK and DBMS_ALERT.
• Using the same data types and language constructs as SQL providing seam-
less access to the database.
Advantages of using Java in the database include:
• Automatic garbage collection, polymorphism, inheritance, multi-threading
• Access to system resources outside of the database such as OS commands,
files, sockets
• Functionality not available in PL/SQL such as OS commands, fine-grained
security policies, image generation, easy sending of e-mails with attach-
ments using JavaMail.
As an example, sending e-mail using UTL_SMTP has several limitations. First,
E-mail messages should conform to RFC 822 Internet specification which
requires prerequisite knowledge. Second, attachments should be identified
with a specific MIME header followed by file contents. This requires more
changes and is error prone. JavaMail hides the details of MIME message
structure and can be used to send e-mail for any type of protocol, not just
over SMTP protocol as with UTL_SMTP.

Page 30 ◆ Select
The data for the tables is created by means of the following INSERT Procedures can be used for void Java methods and functions can be used for
statements: methods returning values. Also packaged procedures and functions can be
used as call specs. Only top-level and packaged procedures and functions
can be used as call specs.
insert into site_tab values (1, ‘New York’);
insert into site_tab values (2, ‘Washington’); Java methods published as procedures and functions must be invoked explic-
insert into site_tab values (3, ‘Chicago’);
insert into site_tab values (4, ‘Dallas’);
itly. They can accept arguments and are callable from SQL CALL statements,
insert into site_tab values (5, ‘San Francisco’); anonymous PL/SQL blocks, stored procedures, functions and packages. Java
methods published as functions are also callable from SQL DML statements
insert into hrc_tab values (1, ‘CEO/COO’); (i.e., INSERT, UPDATE, DELETE and SELECT statements).
insert into hrc_tab values (2, ‘VP’);
insert into hrc_tab values (3, ‘Director’);
insert into hrc_tab values (4, ‘Manager’);
Database triggers can use the CALL statement to invoke a Java method to
insert into hrc_tab values (5, ‘Analyst’); perform a particular action based on the triggering event.
insert into org_tab values (1, 1001, ‘Office of CEO ABC Inc.’,’Office of CEO Object Types can be defined with attributes and methods that operate on
ABC Inc.’); these attributes. The methods can be written in Java.
insert into org_tab values (1, 1002, ‘Office of CEO XYZ Inc.’,’Office of CEO
XYZ Inc.’); Java stored procedures can also be called from a Java client via JDBC or
insert into org_tab values (1, 1003, ‘Office of CEO DataPro Inc.’,’Office of
CEO DataPro Inc.’); SQLJ, a Pro*, OCI or ODBC client or an Oracle Developer Forms client.
insert into org_tab values (2, 1004, ‘Office of VP Sales ABC Inc.’,’Office of
VP Sales ABC Inc.’); Developing a Java stored procedure involves the following steps:
insert into org_tab values (2, 1005, ‘Office of VP Mktg ABC Inc.’,’Office of 1. Writing the Java method by creating a custom Java class to achieve the
VP Mktg ABC Inc.’);
insert into org_tab values (2, 1006, ‘Office of VP Tech ABC Inc.’,’Office of desired functionality.
VP Tech ABC Inc.’); 2. Loading the Java method into the RDBMS and resolving external refer-
ences.
insert into org_site_tab values (1001, 1);
insert into org_site_tab values (1002, 2); 3. Publishing the Java method into the RDBMS by writing a PL/SQL call spec.
insert into org_site_tab values (1003, 3);
insert into org_site_tab values (1004, 1);
4. If needed, granting the appropriate privileges to invoke the Java stored
insert into org_site_tab values (1004, 2); procedure.
insert into org_site_tab values (1004, 3); 5. Calling the Java stored procedure from SQL and/or PLSQL.
insert into org_site_tab values (1005, 1);
insert into org_site_tab values (1005, 4); Each of these steps is explained in detail below.
insert into org_site_tab values (1005, 5);
insert into org_site_tab values (1006, 1);
1. Writing the Java method to achieve the desired
functionality
In addition, a minimal setup of the environment for Java and JDBC to work
should be done as follows: The following code shows a method that lists the contents of a directory
• Set the CLASSPATH to include the current working directory and passed to it as an argument:
[OracleHome]\jdbc\lib\classes12.zip.
• Set the PATH to include [JavaHome]\bin and [OracleHome]\bin directories.

Defining and Executing Java Stored Procedures


A Java stored procedure is a Java method published to SQL and stored in an
Oracle 9i database. Once a Java method is written and compiled as a Java
class, it can be published by writing call specifications abbreviated as call
specs. The call specs map Java method names, parameter types, and return
types to their SQL counterparts. A call spec simply publishes the existence of
a Java method. The Java method is called through its call spec and the
runtime system dispatches the call with minimum overhead. A call spec is not
a wrapper program since it does not add a second layer of code to be
executed. It just publishes the existence of the Java method.
Once published, the Java stored procedure is callable by client applications.
When called, the stored procedure can accept arguments, reference Java
classes, and return Java return values. The type of the application that can
invoke a Java stored procedure is determined by the runtime context. Any
Java method that does not include the GUI methods can be stored and run in
the RDBMS as a stored procedure. The run-time contexts are as follows:
• Functions and procedures
• Database triggers
• Object-relational methods

continued on page 32

4th Qtr 2002 ◆ Page 31


Using Java Stored Procedures in Oracle 9i continued from page 31

To verify that the class has been loaded, query the data dictionary view
import java.io.*; import java.sql.*; import oracle.sql.*; import
oracle.jdbc.driver.*; public class Directory
USER_OBJECTS as shown below:
{
public static oracle.sql.ARRAY list(String dirName) throws
SQLException
SQL> column object_name format a30;
{ Connection conn = null; int ret_code ;
SQL> select object_name, object_type, status, timestamp
String[] files = null ; File file = new File(dirName);
2 from user_objects
if (file.exists())
3 where (object_name not like ‘SYS_%’
{
4 and object_name not like ‘CREATE$%’
if (file.isDirectory())
5 and object_name not like ‘JAVA%’
{
6 and object_name not like ‘LOADLOB%’)
if (file.canRead())
7 and object_type like ‘JAVA %’
{
8 order by object_type, object_name
files = file.list(); }
9 /
}
}
OBJECT_NAME OBJECT_TYPE STATUS TIMESTAMP
try {
——————————————— ————————— ———- —————————-
Directory JAVA CLASS VALID 2002-03-16:00:01:26
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
RefCursor JAVA CLASS VALID 2001-11-01:01:52:09
conn = DriverManager.getConnection(
“jdbc:oracle:thin:@ASSOCIAT-SZECHG:1521:Oracle9”, “plsql9i”,
“plsql9i”);
3. Publishing the Java method into the RDBMS
ArrayDescriptor x_ad
= ArrayDescriptor.createDescriptor(“X_NESTED_TABLE”, conn); After loading into the RDBMS, the Java method must be published in the
ARRAY x_array = new ARRAY(x_ad, conn, files);
conn.close(); Oracle Data Dictionary by writing a PL/SQL call specification. The call spec
return x_array; maps the Java method names, parameter types and return types to their SQL
}catch (SQLException e) {ret_code = e.getErrorCode(); counterparts. The call spec is written using the CREATE PROCEDURE or
System.err.println(ret_code + e.getMessage()); conn.close(); return
null;} CREATE FUNCTION, CREATE PACKAGE or CREATE TYPE statements.
}
Methods with return values are published as functions and void Java methods
} are published as procedures. Inside the function or procedure body, the
} LANGUAGE JAVA clause is specified. This clause maintains information about
the name, parameter types and return type of the Java method. The following
Once the custom class is written, it is saved as a .java source file. The next code illustrates the publishing of the Directory.list Java method described above:
step is to compile and test it before loading it into the RDBMS. To compile
this class use the javac command from the command line with the source file
create or replace type x_nested_table is table of varchar2(100);
name as the argument. Note that the current directory should be the directory /
where the source file resides.
create or replace package pkg_dir
is
C:\> javac Directory.java function list_dir(p_dir_name VARCHAR2)
return x_nested_table;
end pkg_dir;
This results in a Directory.class file. /

2. Loading the Java method into the RDBMS and resolving external create or replace package body pkg_dir
references. is
function list_dir(p_dir_name VARCHAR2)
Once the above Java method has been tested, it has to be loaded into the return x_nested_table
is language java
RDBMS using loadjava. Loadjava is a command line utility provided by Oracle name ‘Directory.list(java.lang.String) return oracle.sql.ARRAY’;
to load Java code files (.java source files, .class files, .jar files) into the data- end pkg_dir;
base. The command when invoked looks like the following: /

C:\> loadjava –user PL/SQL9i/PLSQL9i –oci8 –resolve Directory.class 4. Granting the appropriate privileges to invoke the Java stored
procedure
Two roles are defined by Oracle 9i to be granted permissions to schemas
accessing resources outside of the database. These are JAVAUSERPRIV and
JAVASYSPRIV. These provide general access control. To provide fine-grained
access control, the procedure dbms_java.grant_permission can be used.

Page 32 ◆ Select
For the Directory.list routine to be callable from PL/SQL, the following fine- Writing the Java method that returns a ResultSet and
grain permissions should be granted: compiling it to obtain a .class file.
The steps involved to define a class RefCursor containing a Java function
SQL> connect system/manager RefCursor_func() are as follows:
Connected.
SQL> begin 1. Import the JDBC specific packages such as java.sql.*, oracle.jdbc.driver.*
2 dbms_java.grant_permission( ‘PLSQL9I’, ‘SYS:java.io.FilePermission’, and oracle.sql.*.
3 ‘<<ALL FILES>>’, ‘read,write,execute,delete’ );
4 end;
5 / import java.sql.*;
import oracle.jdbc.driver.*;
PL/SQL procedure successfully completed. import oracle.sql.*;
SQL> begin
2 dbms_java.grant_permission( ‘PLSQL9I’, ‘SYS:java.net.SocketPermission’,
3 ‘ASSOCIAT-SZECHG’, ‘resolve’ );
4 end; 2. Get the default server-side Oracle connection.
5 /

PL/SQL procedure successfully completed. Connection conn = new OracleDriver().defaultConnection();

SQL> begin
2 dbms_java.grant_permission( ‘PLSQL9I’, 3. Create the Statement or PreparedStatement as a REF CURSOR. This is done
3 ‘SYS:java.net.SocketPermission’, ‘127.0.0.1:1521’, ‘connect,resolve’ ); by calling the method setCreateStatementAsRefCursor(true) on the
4 end;
5 / Connection object cast to an OracleConnection object.

PL/SQL procedure successfully completed.


((OracleConnection)conn).setCreateStatementAsRefCursor(true);

5. Calling the Java stored procedure from SQL and/or PLSQL. 4. Define the Statement object.
Once published, the Java method is called from SQL and/or PLSQL using the
standard procedure for calling PL/SQL procedures or functions. The code for Statement sql_stmt = conn.createStatement();
calling the Directory.list java method is as follows:
5. Define a ResultSet object to execute the appropriate query. This query
SQL> connect plsql9i/plsql9i
returns the resultset so desired.
Connected.
ResultSet rset = sql_stmt.executeQuery(
SQL> declare “SELECT hrc_descr, org_long_name FROM org_tab o, hrc_tab h where o.hrc_code =
2 v_tab x_nested_table; h.hrc_code”);
3 begin
4 v_tab := pkg_dir.list_dir(‘c:\lax\plsql9i’);
5 for i in 1..v_tab.COUNT loop
6 dbms_output.put_line(v_tab(i)); 6. Return the ResultSet object (as a REF CURSOR).
7 end loop;
8 end;
9 / return rset;

Defining the Java Stored Procedure that Returns a ResultSet


The functionality of Java Stored Procedures in Oracle 8i was limited by the
fact that it was not possible to return a ResultSet directly from them. There
was no mapping defined of the type ResultSet->REF CURSOR. Oracle9i has
incorporated this mapping thus allowing returning a ResultSet from a func-
tion or as an OUT parameter to a procedure. This conversion is made
possible by creating the Statement or PreparedStatement as a REF CURSOR.
This is done by calling the setCreateStatementAsRefCursor() method on the
Connection object with an argument as true, before creating that statement.

continued on page 34

4th Qtr 2002 ◆ Page 33


Using Java Stored Procedures in Oracle 9i continued from page 33

The complete program (saved as RefCursor.java) is shown below: Publishing the Java Method in Oracle 9i Using a PL/SQL Call
Specification (call spec)
//Import JDBC packages A packaged function to correspond to the Java function is shown below:
import java.sql.*;
import oracle.jdbc.driver.*;
import oracle.sql.*;
create or replace package pkg_rc as
public class RefCursor { TYPE rc IS REF CURSOR;
public static ResultSet RefCursor_func() throws SQLException { function f_rc return rc;
try { end pkg_rc;
Connection conn = new OracleDriver().defaultConnection(); /
((OracleConnection)conn).setCreateStatementAsRefCursor(true); create or replace package body pkg_rc as
function f_rc return rc
//Create a Statement object is language java
Statement sql_stmt = conn.createStatement(); name ‘RefCursor.RefCursor_func() return java.sql.Resultset’;
//Create a ResultSet object, execute the query and return a end pkg_rc;
// resultset /
ResultSet rset = sql_stmt.executeQuery(
“SELECT hrc_descr, org_long_name FROM org_tab o, hrc_tab h where o.hrc_code
= h.hrc_code”);
return rset; The return type of the PL/SQL packaged function is of type REF CURSOR. The
} catch (SQLException e) {System.out.println(e.getMessage()); Java method should specify the fully qualified type name for its return type.
return null;}
} So, java.sql.ResultSet and not just ResultSet.should be specified.
}
Calling the Java Stored Procedure using the PL/SQL call spec
This is a simple PL/SQL program to call the packaged function
Tips: pkg_rc.f_rc as shown below:

1. If it is a function, the Java method should return a ResultSet of type declare


java.sql.ResultSet. If it is a procedure, it should be declared as a para- TYPE rc IS REF CURSOR;
meter of type java.sql.ResultSet[], i.e. an array of type java.sql.ResultSet. r1 rc;
v_hrc_descr varchar2(20);
2. Only OUT parameters in the call spec are allowed. IN or IN OUT parame- v_org_long_name varchar2(60);
begin
ters cannot be used as there is no mapping from REF CURSOR to r1:= pkg_rc.f_rc;
ResultSet. dbms_output.put_line(‘Hierarchy Org Long Name’);
dbms_output.put_line(‘————- ——————-’);
3. The method setCreateStatementAsRefCursor(true) should be invoked fetch r1 into v_hrc_descr, v_org_long_name;
prior to creating the Statement or PreparedStatement object. Otherwise, while r1%FOUND loop
dbms_output.put_line(rpad(v_hrc_descr, 9)||’ ‘||v_org_long_name);
the following error message is generated when the Java Stored Procedure fetch r1 into v_hrc_descr, v_org_long_name;
is executed: ORA-00932: inconsistent datatypes. Once defined, this Java end loop;
program can be compiled into a .class file as follows: close r1;
end;
/
javac RefCursor.java

The Java Stored Procedure is executed producing the following output, based
Loading the .class file into the Oracle 9i database. on the data in the org_tab table:

This is done using the loadjava utility as follows:


Hierarchy Org Long Name
————————- ——————-
loadjava -user plsql9i/plsql9i -r -oci8 RefCursor.class CEO/COO Office of CEO ABC Inc.
CEO/COO Office of CEO XYZ Inc.
CEO/COO Office of CEO DataPro Inc.
VP Office of VP Sales ABC Inc.
VP Office of VP Mktg ABC Inc.
VP Office of VP Tech ABC Inc.

PL/SQL procedure successfully completed.

Page 34 ◆ Select
Forms 6i and Java – Calling Java Stored Procedures from Forms
One of the ways to incorporate Java in Forms 6i is by means of using Java
Stored Procedures in Forms code. Using a Java Stored Procedure in Forms 6i
is as simple as calling a stored subprogram from Forms. However, the data
type and parameter-mode matching between Forms data types and the Java About the Author
stored procedure data types should be in sync. Bulusu Lakshman is the author of Oracle and Java Development and
Forms 6i even allows using JDBC calls. This allows for complex logic to be Oracle Developer Forms Techniques from SAMS publishing. He holds
embedded in a Java Stored Procedure involving JDBC calls and then used an Oracle Masters credential from Oracle Corporation, is an OCP-
from within Forms. Certified Application Developer, and is a double Honors graduate in
Computer Science & Engineering and Mathematics. He has more than
A Java Stored Procedure using JDBC calls can be invoked from Forms in the ten years experience in application development using Oracle and its
same way a stored sub-program is invoked from Forms. various tools including Oracle and Java, both in the client/server and
Web environments. Bulusu has presented papers at numerous national
and international conferences and also contributed to lead technical
journals in the U.S and U.K. He is employed by Compunnel Software
Group Inc., a leading technical consulting firm in New Jersey and can
be reached at balakshman@hotmail.com.

4th Qtr 2002 ◆ Page 35

You might also like