3.1 Invoking Java Methods

The type of the Java application determines how the client calls a Java method. The following sections discuss each of the Java application programming interfaces (APIs) available for calling a Java method:

3.1.1 Using PL/SQL Wrappers

You can run Java stored procedures in the same way as PL/SQL stored procedures. In Oracle Database, Java is usually invoked through a PL/SQL interface.

To call a Java stored procedure, you must publish it through a call specification. The following example shows how to create, resolve, load, and publish a simple Java stored procedure that returns a String:

  1. Define a class, Hello, as follows:
    public class Hello
    {
      public static String world()
      {
        return "Hello world";
      }
    }
    

    Save the file as a Hello.java file.

  2. Compile the class on your client system using the standard Java compiler, as follows:
    javac Hello.java
    

    It is a good idea to specify the CLASSPATH on the command line with the javac command, especially when writing shell scripts or make files. The Java compiler produces a Java binary file, in this case, Hello.class.

    You must determine the location at which this Java code will run. If you run Hello.class on your client system, then it searches the CLASSPATH for all the supporting core classes that Hello.class needs for running. This search should result in locating the dependent classes in one of the following:

    • As individual files in one or more directories, where the directories are specified in the CLASSPATH

    • Within .jar or .zip files, where the directories containing these files are specified in the CLASSPATH

  3. Decide on the resolver for the Hello class.

    In this case, load Hello.class on the server, where it is stored in the database as a Java schema object. When you call the world() method, Oracle JVM locates the necessary supporting classes, such as String, using a resolver. In this case, Oracle JVM uses the default resolver. The default resolver looks for these classes, first in the current schema, and then in PUBLIC. All core class libraries, including the java.lang package, are found in PUBLIC. You may need to specify different resolvers. You can trace problems earlier, rather than at run time, by forcing resolution to occur when you use the loadjava tool.

  4. Load the class on the server using the loadjava tool. You must specify the user name and password. Run the loadjava tool as follows:
    loadjava -user HR Hello.class
    Password: password
    
  5. Publish the stored procedure through a call specification.

    To call a Java static method with a SQL call, you must publish the method with a call specification. A call specification defines the arguments that the method takes and the SQL types that it returns.

    In SQL*Plus, connect to the database and define a top-level call specification for Hello.world() as follows:

    sqlplus HR
    Enter password: password
    connected
    SQL> CREATE OR REPLACE FUNCTION helloworld RETURN VARCHAR2 AS
      2  LANGUAGE JAVA NAME 'Hello.world () return java.lang.String';
      3  /
    Function created.
    
  6. Call the stored procedure, as follows:
    SQL> VARIABLE myString VARCHAR2(20);
    SQL> CALL helloworld() INTO :myString;
    Call completed.
    SQL> PRINT myString;
    
    MYSTRING
    ---------------------------------------
    Hello world
    
    SQL>
    

    The call helloworld() into :myString statement performs a top-level call in Oracle Database. SQL and PL/SQL see no difference between a stored procedure that is written in Java, PL/SQL, or any other language. The call specification provides a means to tie inter-language calls together in a consistent manner. Call specifications are necessary only for entry points that are called with triggers or SQL and PL/SQL calls. Furthermore, JDeveloper can automate the task of writing call specifications.

3.1.1.1 Using PL/SQL Wrappers with JDK 11

Beginning with Oracle Database Release 23ai, The Java database object names can also contain a module component.

Java objects that are the members of a module, are stored in database objects with names in the following format:

<module_name>///<class_source_or_resource_name>

If a Java database object is not part of a module, that is, if it is part of the unnamed module, then the format is <class_source_or_resource_name>, as it was in the earlier database releases. In the class_source_or_resource portion of the name, the package delimiter period (.) is replaced by a forward slash (/) in the database object name, as in the earlier database releases.

No character replacement occurs in the module portion of the name. The fully modularized form of the database object class name is specified as the name of the top-level class, whose method is being called. For example, a call specification to call the method world in the class named hello.Hello in the module named hello.in.there can be the following:

CREATE OR REPLACE FUNCTION helloworld RETURN VARCHAR2 AS LANGUAGE JAVA NAME 
'hello.in.there///hello.Hello.world () return java.lang.String';

As in the earlier database releases, class names with either period (.) or forward slash (/) delimiters are both accepted in the class_name portion of the database object name of the stored procedure being called. In the argument and return value portion of call specifications, module names are not specified, even if the argument or return value classes are members of a module.

All JDK and Oracle JVM system classes are themselves contained in modules in JDK11. Exceptionally, if the class name of a Java stored procedure to be invoked is one of the built-in system classes, then in the stored procedure definition, you can specify either the fully modularized database object name or just the class name portion as the class of the method to be called.

3.1.2 About JNI Support

The Java Native Interface (JNI) is a standard programming interface for writing Java native methods and embedding the JVM into native applications. The primary goal of JNI is to provide binary compatibility of Java applications that use platform-specific native libraries.

Native methods can cause server failure, violate security, and corrupt data. Oracle Database does not support the use of JNI in Java applications. If you use JNI, then your application is not 100 percent pure Java and the native methods require porting between platforms.

3.1.3 About Utilizing JDBC with Java in the Database

You can use Java Database Connectivity (JDBC) APIs from a Java client. These APIs establish a session with a given user name and password on the database, and run SQL queries against the database. All Oracle JDBC drivers communicate seamlessly with Oracle SQL and PL/SQL.

3.1.3.1 Using JDBC

JDBC is an industry-standard API that lets you embed SQL statements as Java method arguments. JDBC is based on the X/Open SQL Call Level Interface (CLI) and complies with the Entry Level of SQL-92 standard. Each vendor, such as Oracle, creates its JDBC implementation by implementing the interfaces of the standard java.sql package. Oracle provides the following JDBC drivers that implement these standard interfaces:

  • The JDBC Thin driver, a 100 percent pure Java solution that you can use for either client-side applications or applets and requires no Oracle client installation.

  • The JDBC OCI driver, which you use for client-side applications and requires an Oracle client installation.

  • The server-side JDBC driver embedded in Oracle Database.

Using JDBC is a step-by-step process of performing the following tasks:

  1. Obtaining a connection handle
  2. Creating a statement object of some type for your desired SQL operation
  3. Assigning any local variables that you want to bind to the SQL operation
  4. Carrying out the operation
  5. Optionally retrieving the result sets

This process is sufficient for many applications, but becomes cumbersome for any complicated statements. Dynamic SQL operations, where the operations are not known until run time, require JDBC. However, in typical applications, this represents a minority of the SQL operations.

3.1.4 About Using the Command-Line Interface

The command-line interface to Oracle JVM is analogous to using the JDK or JRE shell commands. You can:

  • Use the standard -classpath syntax to indicate where to find the classes to load

  • Set the system properties by using the standard -D syntax

The interface is a PL/SQL function that takes a string (VARCHAR2) argument, parses it as a command-line input and if it is properly formed, runs the indicated Java method in Oracle JVM. To do this, PL/SQL package DBMS_JAVA provides the following functions:

runjava

This function takes the Java command line as its only argument and runs it in Oracle JVM. The return value is null on successful completion, otherwise an error message. The format of the command line is the same as that taken by the JDK shell command, that is:

[option switches] name_of_class_to_execute [arg1 arg2 ... argn]

You can use the option switches -classpath, -D, -Xbootclasspath, and -jar. This function differs from the runjava_in_current_session function in that it clears any Java state remaining from previous use of Java in the session, prior to running the current command. This is necessary, in particular, to guarantee that static variable values derived at class initialization time from -classpath and -D arguments reflect the values of those switches in the current command line.

FUNCTION runjava(cmdline VARCHAR2) RETURN VARCHAR2;

runjava_in_current_session

This function is the same as the runjava function, except that it does not clear Java state remaining from previous use of Java in the session, prior to executing the current command line.

FUNCTION runjava_in_current_session(cmdline VARCHAR2) RETURN VARCHAR2;

Syntax

The syntax of the command line is of the following form:

[-options] classname [arguments...]
[-options] -jar jarfile [arguments...]

Options

-classpath
-D
-Xbootclasspath
-Xbootclasspath/a
-Xbootclasspath/p
-cp

Note:

The effect of the first form is to run the main method of the class identified by classname with the arguments. The effect of the second form is to run the main method of the class identified by the Main-Class attribute in the manifest of the JAR file identified by JAR. This is analogous to how the JDK/JRE interprets this syntax.

Argument Summary

The following table summarizes the command-line arguments.

Table 3-1 Command Line Argument Summary

Argument Description

classpath

Accepts a colon (:) separated (semicolon-separated on Windows systems) list of directories, JAR archives, and ZIP archives to search for class files. In general, the value of -classpath or similar arguments refer to file system locations as they would in a standard Java runtime. You also have an extension to this syntax to allow for terms that refer to database resident Java objects and sets of bytes.

D

Establishes values for system properties when there is no existing Java session state. The default behavior of the command-line interface, that is, the runjava function, is to terminate any existing Java session prior to running the new command. On the other hand, the alternative function, runjava_in_current_session leaves any existing session in place. So, values established with the -D option always take effect when runjava function is used, but the values may not take effect when runjava_in_current_session function is used.

Xbootclasspath

Accepts a colon (:) separated (semicolon-separated on Windows systems) list of directories, JAR archives, and ZIP archives. This option is used to set search path for bootstrap classes and resources.

Xbootclasspath/a

Accepts a colon (:) separated (semicolon-separated on Windows systems) list of directories, JAR archives, and ZIP archives. This is appended to the end of bootstrap class path.

Xbootclasspath/p

Accepts a colon (:) separated (semicolon-separated on Windows systems) list of directories, JAR archives, and ZIP archives. This is added in front of bootstrap class path.

cp

Acts as a synonym of -classpath.

Note:

System classes created by create java system are always used before using any file or folder that are found using the -Xbootclasspath option.

3.1.5 Overview of Using the Client-Side Stub

Oracle Database 10g introduced the client-side stub, formerly known as native Java interface, for calls to server-side Java code. It is a simplified application integration. Client-side and middle-tier Java applications can directly call Java in the database without defining a PL/SQL wrapper. The client-side stub uses the server-side Java class reflection capability.

In previous releases, calling Java stored procedures and functions from a database client required Java Database Connectivity (JDBC) calls to the associated PL/SQL wrappers. Each wrapper had to be manually published with a SQL signature and a Java implementation. This had the following disadvantages:

  • The signatures permitted only Java types that had direct SQL equivalents

  • Exceptions issued in Java were not properly returned

Starting from Oracle Database 12c Release 2 (12.2.0.1), you can use the Oracle JVM Web Services Call-Out Utility for generating the client-side stub.

3.1.5.1 Using the Default Service Feature

If you install Oracle Database client, then you need not specify all the details of the database server in the connection URL. Under certain conditions, Oracle Database connection adapter requires only the host name of the computer where the database is installed.

For example, in the JDBC connection URL syntax, that is:

jdbc:oracle:driver_type:[username/password]@[//]host_name[:port][:ORCL]

,the following have become optional:

  • // is optional.

  • :port is optional.

    You must specify a port only if the default Oracle Net listener port (1521) is not used.

  • :ORCL or the service name is optional.

    The connection adapter for Oracle Database Client connects to the default service on the host. On the host, this is set to ORCL in the listener.ora file.

3.1.5.2 Testing the Default Service with a Basic Configuration

The following code snippet shows a basic configuration of the listener.ora file, where the default service is defined:

MYLISTENER = (ADDRESS_LIST=(ADDRESS=(PROTOCOL=tcp)(HOST=testserver1)(PORT=1521)))
DEFAULT_SERVICE_MYLISTENER=dbjf.app.myserver.com
SID_LIST_MYLISTENER = (SID_LIST=(SID_DESC=(SID_NAME=dbjf)
(GLOBAL_DBNAME=dbjf.app.myserver.com)(ORACLE_HOME=/test/oracle))
)

After defining the listener.ora file, restart the listener with the following command:

lsnrctl start mylistener

Now, any of the following URLs should work with this configuration of the listener.ora file:

  • jdbc:oracle:thin:@//testserver1.myserver.com.com

  • jdbc:oracle:thin:@//testserver1.myserver.com:1521

  • jdbc:oracle:thin:@testserver1.myserver.com

  • jdbc:oracle:thin:@testserver1.myserver.com:1521

  • jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=testserver1.myserver.com)(PORT=1521)))

  • jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=testserver1.myserver.com)))

  • jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=testserver1.myserver.com)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=)))