This is not a good use of Oracle stored procedures, simply because selecting data from a table from your application is faster than calling a stored procedure that selects data from a ta
Trang 1getAsciiStream( ), getBinaryStream( ), and getCharacter-Stream( ); the
nonstreaming get methods are getBytes( ) and getString( )
12.5.1 Inserting or Updating a LONG
Other than the use of the methods just mentioned, when inserting or updating a LONG, there are limitations on the size of a String or byte array that you can update using the setString( )
or setBytes( ) methods These limitations are listed in Table 11-3 When you exceed these limits, you will get the following error message: "Data size bigger than max size for this type:
####." To work around these limitations, you need to use the streaming methods
12.5.2 Selecting a LONG
To retrieve data in ASCII, use the ResultSet object's getAsciiStream( ) method You can use the getAsciiStream( ) method only if the underlying database uses the US7ASCII or WE8ISO8859P1 character set Otherwise, you can use the getCharacterStream( ) method, which returns UCS-2-encoded characters (Unicode) regardless of the underlying database's character set Even if you get the data as a String, the JDBC driver will stream the data for you
If you use the getBinaryStream( ) method, one of two possibilities exists If you are using the OCI driver, and its client character set is not set to US7ASCII or WE8ISO8859P1, then a call to the getBinaryStream( ) method returns data in the UTF-8 character set If you are using the Thin driver, and the database's character set is not set to US7ASCII or WE8ISO8859P1, then a call to the getBinaryStream( ) method also returns data in the UTF-8 character set
Otherwise, you'll get the data in the US7ASCII character set
As with the LONG RAW data type, you must call the getXXX( ) methods for each column returned by the SELECT statement in the same order in which those columns are listed in the SELECT statement Also, when processing a streamed column, you must process the streamed data before calling another getXXX( ) method Once you move on to another getXXX( ) method, the streamed data is no longer accessible In addition, you can prevent the JDBC driver from streaming LONG data by using the OracleResultSet object's defineColumnType( ) method, setting the data type of a LONG column to Types.VARCHAR This may be desirable if there are a small number of characters in the data to be stored
Now that you are an expert on using streaming data types, let's take a look at how to call stored procedures in Chapter 13
Chapter 13 Callable Statements
CallableStatement objects are used to call stored procedures The stored procedures
themselves can be written using PL/SQL or Java If they are written in Java, they must be
published to the RDBMS by creating a SQL call specification You can see examples of this in
Chapter 5 Stored procedures exist to perform data-intensive operations that cannot be
accomplished using just SQL, and they perform these operations inside the database where network performance is a moot issue
For example, let's say you need to access five different tables in order to perform a complex calculation and need to store the result in a sixth table Let's further assume that the calculation is complex enough that it cannot be performed using just SQL If you perform the work on a client, then the client will have to retrieve all the data necessary to perform the calculation and send the result back to the database If the number of rows that need to be retrieved by the client is large, this network transfer of data could consume an inordinate amount of elapsed time However, if you perform the calculation as a stored procedure, the elapsed time of transmitting data across
Trang 2the network will be eliminated, and the resulting calculation will be much quicker This example represents the type of situation in which stored procedures excel
As with all good things, stored procedures are sometimes taken to an extreme and are
sometimes used as a panacea For example, some developers eliminate SQL from their
application altogether and use only stored procedures This is not a good use of Oracle stored procedures, simply because selecting data from a table from your application is faster than calling
a stored procedure that selects data from a table and returns it to your application When you go through a stored procedure, you have two network round trips instead of one
Enough with my soapbox speeches! Let's get on to some real meat In this chapter, you'll learn how to identify the parameters for, and formulate, stored procedure calls using both SQL92 and Oracle syntax You'll learn how to create a CallableStatement object to execute stored procedures, how to set and retrieve parameter values, and how to actually execute a stored procedure Let's get started by looking at how to identify stored procedure names and parameter types
13.1 Understanding Stored Procedures
With Oracle, stored procedure actually refers collectively to standalone stored functions,
standalone procedures, packaged functions, and procedures So when I use the term stored procedure in this chapter, please understand that I am referring generally to any of these
procedure or function types
To call a stored procedure, you need to know the procedure name and know about any
parameters that will be passed to the procedure In the case of a function, you also need to know the return type One way to get this information is to query Oracle's data dictionary views for stored procedure source code and look at the signature for the procedures you wish to use The next section shows you how to interpret Oracle's stored procedure signatures Following that is a section that shows you, among other things, how to query the data dictionary for procedure signatures
13.1.1 Stored Procedure Signatures
It's important to know the differences in the syntax used by stored procedures so you can code your callable statements appropriately Oracle stored procedures are created using three different syntaxes, one for standalone functions, another for standalone procedures, and a third for
functions and procedures that are part of a package
13.1.1.1 Standalone functions
The difference between a procedure and function is that a function returns a value, so it can be used as an evaluated item in an expression The syntax to create a stored function is:
CREATE [OR REPLACE] [user.]FUNCTION function_name
[(parameters)]
RETURN data_type {AS | IS}
function_body
parameters ::= parameter_declaration [,parameter_declaration ]
parameter_declaration ::= parameter_name [IN | OUT | IN OUT] data_type
which breaks down as:
user
The schema owner of the function
Trang 3function_name
The name of the function
RETURN data_type
Specifies the SQL data type returned by the function
parameter_name
The name of a parameter passed to the function Zero or more parameters may be passed to a function
IN | OUT | IN OUT
Specifies the use of a parameter An IN parameter can be read, but you cannot write to it
An OUT parameter can be written to but not read You can both read from and write to an
IN OUT parameter
data_type
The SQL data type of the parameter
For example, to create an errorless TO_NUMBER function for user SCOTT, log into Oracle with SQL*Plus as SCOTT/TIGER and execute the following PL/SQL code:
create or replace function ToNumberFun (
aiv_varchar2 in varchar2 )
return number is
begin
return to_number( aiv_varchar2 );
exception
when OTHERS then
return null;
end ToNumberFun;
/
13.1.1.2 Standalone procedures
Use the following syntax to create a standalone stored procedure A procedure does not return a value However, both functions and procedures can have OUT or IN OUT variables that return values
CREATE [OR REPLACE] [user.]PROCEDURE procedure_name
[(parameters)] {AS | IS}
procedure_body
parameters ::= parameter_declaration [,parameter_declaration ]
parameter_declaration ::= parameter name [IN | OUT | IN OUT] data_type
See Section 13.1.1.1 for an explanation of the syntax
13.1.1.3 Packages
A package is a collection of related functions and procedures It has a specification that defines which functions, procedures, and variables are publicly accessible It also has a body that
contains the functions and procedures defined in the specification and possibly also contains private functions, private procedures, and private variable declarations Packages are an
improvement over standalone functions and procedures, because the separation between the specification and body reduces stored procedure dependency problems The syntax for creating
a package specification is:
CREATE [OR REPLACE] PACKAGE package_name AS
package_specification
Trang 4in which package_name is the name of the package The following syntax is used to create a package body:
CREATE [OR REPLACE] PACKAGE BODY package_name AS
package_body
Why is this explanation of stored procedure syntax important? Because you need to understand the stored procedure syntax in order to know the name of a stored procedure (or function), which data types you can pass to it, and which data type it returns
13.1.2 Describing Signatures
If you want to invoke a stored procedure and double-check its name and the parameters you must pass to it, you can take one of several approaches If you have access to SQL*Plus, you can use one of the following variations on the DESCRIBE command:
desc[ribe] [schema.]function_name
desc[ribe] [schema.]procedure_name
desc[ribe] [schema.]package_name
The following example shows the DESCRIBE command being used to display information about the ToNumberFun function owned by the user Scott:
SQL> desc tonumberfun
FUNCTION tonumberfun RETURNS NUMBER
Argument Name Type In/Out Default? - - - - AIV_VARCHAR2 VARCHAR2 IN
The JDBC API defines a method, named DatabaseMetaData.getProcedureColumns( ), you can invoke to get stored procedure signature data In practice, however, the
getProcedureColumns( ) method is not all that useful Oracle stored procedures can be overloaded, and, using getProcedureColumns( ), you can't distinguish between the different overloaded versions of the same stored procedure So, for Oracle at least, the
getProcedureColumns( ) method is useless
A third way to determine the signature for a standalone function, standalone procedure, or
package is to take a peek at the source code The program named
DescribeStoredProcedures, shown in Example 13-1, does this You pass in the name of a stored procedure on the command line, then the program queries the SYS.DBA_SOURCE view for the source code and displays that source code If you don't have access to the DBA views, you can change the program to use SYS.ALL_SOURCE
Example 13-1 Describing a stored procedure
import java.io.*;
import java.sql.*;
import java.text.*;
public class DescribeStoredProcedures {
Connection conn;
public DescribeStoredProcedures( ) {
try {
DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver( ));
conn = DriverManager.getConnection(
"jdbc:oracle:thin:@dssw2k01:1521:orcl", "scott", "tiger");
}
catch (SQLException e) {
Trang 5System.err.println(e.getMessage( ));
e.printStackTrace( );
}
}
public static void main(String[] args)
throws Exception, IOException {
String storedProcedureName = null;
String schemaName = null;
if (args.length > 0)
schemaName = args[0];
if (args.length > 1)
storedProcedureName = args[1];
new DescribeStoredProcedures( ).process(
schemaName, storedProcedureName);
}
public void process(
String schemaName,
String storedProcedureName)
throws SQLException {
int rows = 0;
ResultSet rslt = null;
Statement stmt = null;
String previous = "~";
String schemaPattern = "%";
String procedureNamePattern = "%";
try {
if (schemaName != null && !schemaName.equals("")) schemaPattern = schemaName ;
if (storedProcedureName != null &&
!storedProcedureName.equals(""))
procedureNamePattern = storedProcedureName;
stmt = conn.createStatement( );
rslt = stmt.executeQuery(
"select type||' '||owner||'.'||name, line, text " + "from sys.dba_source " +
"where type = 'FUNCTION' " +
"and owner like '" + schemaPattern + "' " +
"and name like '" + procedureNamePattern + "' " + "union all " +
"select type||' '||owner||'.'||nam e, line, text " + "from sys.dba_source " +
"where type = 'PROCEDURE' " +
"and owner like '" + schemaPattern + "' " +
"and name like '" + procedureNamePattern + "' " + "union all " +
"select type||' '||owner||'.'||name, line, text " + "from sys.dba_source " +
"where type = 'PACKAGE' " +
"and owner like '" + schemaPattern + "' " +
"and name like '" + procedureNamePattern + "' " + "union all " +
"select type||' '||owner||'.'||name, line, text " + "from sys.dba_source " +
Trang 6"where type = 'TYPE' " +
"and owner like '" + schemaPattern + "' " +
"and name like '" + procedureNamePattern + "' " +
"order by 1, 2");
while (rslt.next( )) {
rows++;
if (!rslt.getString(1).equals(previous)) {
if (!previous.equals("~"))
System.out.println("");
previous = rslt.getString(1);
}
System.out.print(rslt.getSt ring(3));
}
if (!previous.equals("~"))
System.out.println("");
}
catch (SQLException e) {
System.err.println("SQL Error: " + e.getMessage( ));
}
finally {
if (rslt != null)
try { rslt.close( ); } catch (SQLException ignore) { }
if (stmt != null)
try { stmt.close( ); } catch (SQLException ignore) { }
}
}
protected void finalize( )
throws Throwable {
if (conn != null)
try { conn.close( ); } catch (SQLExcep tion ignore) { }
super.finalize( );
}
}
Here are the results of using DescribeStoredProcedures to describe the
scott.ToNumberFun function:
C:\>java DescribeStoredProcedures SCOTT TONUMBERFUN
function ToNumberFun (
aiv_varchar2 in varchar2 )
return number is
begin
return to_number( aiv_varchar2 );
exception
when OTHERS then
return null;
Of course, the best place to get information on stored procedure names and parameters is from written documentation, but we all know that from time to time, written documentation is not available Consequently, the SQL*Plus DESCRIBE command and the
DescribeStoredProcedures program can be quite handy
Once you have the signature for the stored procedure you wish to execute, there is a problem that can arise: the username that will execute the stored procedure may not have the rights to do
so
Trang 7The SYS.DBA and SYS.V_$ Views
Every developer who plans to work with an Oracle database should have
access to the SYS.DBA and SYS.V_$ views These views show all the
objects for all the schemas in a database, even if the current user does
not have access to them At the same time, they do not show any of the
actual data in schemas that a developer does not have access to, so
they can't be used for malicious activities
If a developer does have access to these views, she can determine
whether a problem with not finding a database object is due to grants (or
lack thereof) or due to the fact that an object does not actually exist in
the database She can do this by compar ing the output of the SYS.ALL
views, which everyone typically has access to, against their SYS.DBA
counterparts
So if you don't have access to the DBA views, request it It will save you
a great deal of time to be able to query the data dictionary views
yourself
13.1.3 Granting Execute Rights
If you've written a stored procedure, how do you determine who has access to it? Initially, only you, the owner of the stored procedure, and anyone who has been granted the EXECUTE ANY PROCEDURE system privilege have access to your new procedure But how can you tell if another user has EXECUTIVE rights? Examine the SYS.ALL_TAB_PRIVS view and look at the rows from the view that contain the name of the stored procedure in the table_name column The view and column names make doing this a bit confusing, but that's the way things work For example, to see who has access to user SCOTT's ToNumberFun function, execute the following query:
select grantee,
privilege
from sys.all_tab_privs
where table_name = 'TONUMBERFUN'
and table_schema = 'SCOTT'
This gets results such as the following:
GRANTEE PRIVILEGE
- - SYSTEM EXECUTE
In this case, because no users are listed in the output, you know that user SCOTT has not yet granted anyone else access to ToNumberFun If you find that a particular username or role needs to be granted access to a stored procedure, you or your DBA can grant those rights using the following form of the GRANT statement:
GRANT EXECUTE ON [schema.]stored_procedure TO {user_name | role_name}
which breaks down as:
schema
Optionally used by a DBA to grant someone access to another user's object
Trang 8stored_procedure
The name of a standalone function, standalone procedure, or package
user_name | role_name
The name of the user or role to which you are granting EXECUTE rights
Given the information in this section, you can check whether a stored procedure that does not seem to exist really does not exist or if the EXECUTE privilege has just not been granted to the desired user
Now that you have some background in Oracle stored procedure signatures and in the granting of EXECUTE privileges, let's take a look at how to formulate a stored procedure call for a callable statement
13.2 Calling Stored Procedures
Calling a stored procedure from your Java program requires a five-step process:
1 Formulate a callable statement
2 Create a CallableStatement object
3 Register any OUT parameters
4 Set any IN parameters
5 Execute the callable statement
In the sections that follow, I'll describe each of the steps listed here The function ToNumberFun, owned by the user SCOTT, will form the basis for most of the examples
13.2.1 Formulating a Callable Statement
The first step in the process of calling a stored procedure is to formulate a stored procedure call, that is, properly format a string value that will be passed to a Connection object's
prepareCall( ) method in order to create a CallableStatement object When it comes to formulating a stored procedure call, you have two syntaxes at your disposal: the SQL92 escape syntax and the Oracle syntax In theory, because it's not vendor-specific, the SQL92 escape syntax gives you better portability But let's be realistic Stored procedure capabilities and syntax vary wildly from one database vendor to another If you choose to invest in stored procedures, I'd say you're not too interested in portability Nonetheless, let's first take a look at the SQL92
escape syntax
13.2.1.1 SQL92 escape syntax
When using the SQL92 escape syntax, the String object or string literal you pass to the
Connection object's prepareCall( ) method to create a CallableStatement object takes
on one of the following forms:
{? = call [schema.][package.]function_name[(?,?, )]}
{call [schema.][package.]procedure_name[(?,?, )]}
which breaks down as:
schema
Refers to the stored procedure's owner's username or schema name
Trang 9package
Refers to a package name and applies only when you are not calling a standalone function or procedure
function_name
Refers to the name of a standalone function or to the name of a function in a package
procedure_name
Refers to the name of a standalone procedure or to the name of a procedure in a package
?
A placeholder for an IN, OUT, or IN OUT parameter, or for the return value of a function
In this syntax diagram, the curly braces ({}) are part of the syntax; they do not, in this case, denote optional choices
The question mark characters (?) in the procedure or function call mark the locations of
parameters that you supply later, after creating the CallableStatement object and before executing the statement For example, consider the following signature for the ToNumberFun standalone function:
create or replace function ToNumberFun (
aiv_varchar2 in varchar2 )
return number;
The properly formatted callable statement string for this function would look like:
{ ? = call tonumberfun( ? ) }
If you are calling a stored procedure rather than a stored function, omit the leading ? =
characters That's because procedures do not return a value as functions do The following is an example of a standalone procedure:
create or replace procedure ToNumberPrc (
aiv_varchar2 in varchar2,
aon_number out number )
begin
aon_number := to_number( aiv_varchar2 );
exception
when OTHERS then
aon_number := null;
end ToNumberPrc;
/
A properly formatted callable statement string for the ToNumberPrc procedure would look like: { call tonumberprc( ?, ? ) }
If the procedure or function that you are calling is part of a stored package, then you need to reference the package name when creating your callable statement The following is a package named chapter_13 that implements a procedure and a function, both of which are named ToNumber:
REM The specification
create or replace package chapter_13 as
function ToNumber (
Trang 10aiv_varchar2 in varchar2 )
return number;
procedure ToNumber (
aiv_varchar2 in varchar2,
aon_number out number );
end;
/
REM The body
create or replace package body chapter_13 as
function ToNumber (
aiv_varchar2 in varchar2 )
return number is
begin
return to_number( aiv_varchar2 );
exception
when OTHERS then
return null;
end ToNumber;
procedure ToNumber (
aiv_varchar2 in varchar2,
aon_number out number ) is
begin
aon_number := to_number( aiv_varchar2 );
exception
when OTHERS then
aon_number := null;
end ToNumber;
end;
/
The callable statement strings for the ToNumber function and procedure defined in the chapter_13 package would look like:
{ ? = call chapter_13.tonumber( ? ) }
{ call chapter_13.tonumber( ?, ? ) }
Now that you understand the SQL92 escape syntax, let's take a look at Oracle's syntax for callable statements
13.2.1.2 Oracle syntax
Oracle's syntax, which is PL/SQL syntax, is very similar to SQL92 syntax:
begin ?:=[schema.][package.]function_name[(?,?, )]; end;
begin [schema.][package.]procedure_name[(?,?, )]; end;
This breaks down as:
schema
Refers to the stored procedure owner's username or schema name