Friday, April 10, 2009

Collaborate '09

Collaborate '09 is less than a month away. I will presenting session 61450 - PeopleTools Tips and Techniques on Tuesday from 4:30 pm to 5:30 pm. See you there!

Wednesday, April 08, 2009

Using JDBC to Execute Stored Procedures with Output Parameters

PeopleSoft allows developers to execute database stored procedures using PeopleCode that resembles SQLExec("EXEC PACKAGE.PROC_NAME(:1, :2)", &bind1, &bind2);. Even though stored procedures can have input and output parameters, SQLExec discards output parameters. Several years ago I found an interesting post that described how to use DBMS_PIPE with Oracle database to return output parameters, but, it appears the author removed that post. What made the DBMS_PIPE solution so compelling was that it shared the PeopleSoft database connection. The alternative presented below, unfortunately, requires you to maintain a user name and password, preferably encrypted and stored in a secure location. Because of the class loading issues mentioned in my post Using Oracle JDBC from PeopleCode, this post uses the Oracle specific data access classes. If you use a different database, the classes will differ, but the concept is the same. Furthermore, if you use a different database and JDBC driver, you may be able to use the standard, generic JDBC classes as described in the PSST0101 post Writing to Access Databases

Before demonstrating how to call a stored procedure from PeopleCode, we need a stored procedure to call. The following PL/SQL describes a stored procedure that has two parameters: one in and one in/out. The implementation of the procedure hard codes the output value for simplicity.

CREATE OR REPLACE PACKAGE JJM_IN_OUT AS

PROCEDURE P(
IN1 IN VARCHAR2,
INOUT1 IN OUT VARCHAR2);
END JJM_IN_OUT;
/

CREATE OR REPLACE PACKAGE BODY JJM_IN_OUT AS
PROCEDURE P(
IN1 IN VARCHAR2,
INOUT1 IN OUT VARCHAR2) IS
BEGIN
INOUT1 := 'Hello World';
END P;
END JJM_IN_OUT;
/
show errors

To build this package, copy the PL/SQL above into a text editor and then run it from SQLPlus. The procedure test follows:

SQL> var out1 varchar2(100)
SQL> exec JJM_IN_OUT.P('x', :out1);

PL/SQL procedure successfully completed.

SQL> print out1

OUT1
-------------------------------------------------------
Hello World

SQL>

The following IScript demonstrates calling a procedure with in/out parameters from PeopleCode:

Function IScript_OutParms()
Local JavaObject &driver = CreateJavaObject("oracle.jdbc.OracleDriver");;
Local JavaObject &info = CreateJavaObject("java.util.Properties");

&info.put("user", "dbuser");
&info.put("password", "secret");

Local JavaObject &conn = &driver.connect("jdbc:oracle:thin:@server:1521:SID", &info);
Local JavaObject &stmt = &conn.prepareCall("{call JJM_IN_OUT.P (?,?)}");
Local JavaObject &types = GetJavaClass("java.sql.Types");

REM ** set input parameter values;
&stmt.setString(1, "aa");
&stmt.setString(2, "bb");

REM ** register the second parameter as a output parameter;
&stmt.registerOutParameter(2, &types.VARCHAR);

REM ** execute the SQL statement;
&stmt.execute();

%Response.SetContentType("text/plain");

REM ** Display the value of the second parameter;
%Response.WriteLine("JJM_IN_OUT.P output parameter value: " | &stmt.getString(2));

&conn.close();
End-Function;

After making the connection, the code prepares an SQL statement: {call JJM_IN_OUT.P (?,?)}. JDBC procedure calls use the syntax {call proc_name (?,?)} (the question marks represent the procedure's parameters).

The code above is for demonstration purposes only. Be sure to store the database user name and password in a secure manner. When executing SQL using a second connection, be sure to consider deadlock, race conditions, etc.