- SQL
- PL/SQL
- DBA
- Developer / Forms
- Developer / Reports
- Developer / Graphics
- Data-Warehouse

 
 
 

 
> Tutorials
 

LOADJAVA

di Mariateresa Piselli e Emanuela Marottoli

In questo articolo verrà analizzata una caratteristica di Oracle8i che permette di richiamare classi java da procedure PL/SQL.

Nella versione di Oracle 8i è incluso un prodotto denominato JServer, che contiene i seguenti elementi:

· Java Virtual Machine di Oracle, chiamata Aurora, ovvero l'ambiente di esecuzione e le librerie Java.
· Una stretta integrazione tra le funzionalità dell' RDBMS e PL/SQL.
· JServer Accelerator (compilatore) (disponibile solamente nella versione 8.1.6 della Enterprise Edition).

L'Aurora JVM permette di eseguire metodi e classi java come se fossero allocate direttamente nel database, essa fornisce un ambiente run-time per gli oggetti Java avendo a disposizione tutte le librerie di Java, comprese java.lang , java.io , java.net , java.math e java.util.
Per memorizzare le classi Java in un database Oracle, bisogna utilizzare l'utility LOADJAVA da riga di comando e non si può accedere mai direttamente a queste classi, ma soltanto Aurora JVM le utilizza.
Aurora JVM include un compilatore che traduce i sorgenti Java in un file con estensione .class chiamato anche bytecode (linguaggio esadecimale standard) .
Per eseguire i programmi Java, Aurora JVM include un interprete che permette di far girare i .class sul sistema .

Per poter eseguire una classe Java da Oracle è necessario seguire i seguenti punti:

· Installare la JDK
· Creare una classe Java e compilarla per ottenere il file .class
· Settare gli adeguati privilegi.
· Caricare la classe in Oracle tramite il comando 'LOADJAVA' da prompt oppure tramite il comando 'CREATE JAVA'
· Scrivere una procedura PL/SQL che esegua la chiamata alla classe Java.
· Richiamare la procedura PL/SQL che fa eseguire la classe Java.

Oracle8 i ha creato due nuovi ruoli per mantenere la sicurezza di Java. Per molte operazioni eseguite da Java non saranno necessari questi ruoli, ma se si desidera accedere o modificare file sarà necessario averne i permessi:

  • JAVASYSPRIV
  • JAVAUSERPRIV

Sarà sufficiente assegnare questi ruoli come qualsiasi altro ruolo della basedati. Per esempio, se volessi permettere all'utente SCOTT di effettuare qualsiasi operazione tramite le classi java basterà
che l'utente SYS o SYSTEM gli assegni questo ruolo:

    GRANT JAVASYSPRIV TO SCOTT;

Se invece volessi porre delle limitazioni a SCOTT gli assegnerei il seguente ruolo:

    GRANT JAVAUSERPRIV TO SCOTT;

Un esempio della differenza tra i due ruoli sta nel fatto che se volessi permettere a SCOTT di creare un file tramite Java avrebbe bisogno di JAVASYSPRIV, per limitarlo invece nella sola lettura o scrittura di file sarà sufficiente JAVAUSERPRIV.

Quando viene inizializzata la JVM Aurora, essa crea un istanza di java.lang.SecurityManager (Gestore della sicurezza di Java). Ad ogni utente verrà assegnato un ID dinamico che corrisponde alla sessione a cui può accedere l'utente tramite la classe Java lanciata da PL/SQL.
Se un utente non ha i privilegi assegnati da questi ruoli e prova ad eseguire un operazione che invece li richiederebbero, la JVM solleverebbe l'eccezione java.lang.SecurityException.
Il messaggio di errore che comparirebbe sulla shall di SQL/PLUS è:

ORA-29532: Java call terminated by uncaught Java exception:
java.lang.SecurityException


L'utility LOADJAVA eseguito da riga di comando permette di caricare nel DB i file Java.
La prima volta che si lancia LOADJAVA in uno schema vengono creati una serie di elementi:

· CREATE$JAVA$LOB$TABLE
Una tabella creata in ogni schema contenente gli elementi del codice java.

· JAVA$CLASS$MD5$TABLE
Un Hashtable utilizzata per puntare a tutti gli elementi java caricati in ogni schema

· LOADLOBS
Un package installato in ogni schema, utilizzato per caricare codice java sotto forma di oggetti LOBs nel DB.

Uilizzando LOADLOB, loadjava sposta il file java in un campo BLOB della tabella CREATE$JAVA$LOB$TABLE e nello stesso tempo controlla i valori della tabella JAVA$CLASS$MD5$TABLE.MD5 per verificare se la classe era stata già caricata in precedenza e se ha subito delle modifiche.

Questo caricamento avviene solamente se:

La classe si sta caricando per la prima volta o se ha subito delle modifiche.

La sintassi è la seguente:

loadjava {-user | -u} username/password[@database]
[-option_name [-option_name] ...] filename [filename ]...

Nel nostro caso:

loadjava -user scott/tiger[@host_name:port:sid]
-thin InserisciDip.class

Dopo aver caricato la nostra classe sul db sarà necessario creare una procedura PL/SQL tramite cui verrà chiamata la classe java:

create procedure Nome_procedura(par1, par2) is

LANGUAGE JAVA NAME ('nome_classe.nome_metodo(java.lang.String, java.lang.String)');

End;

Nel nostro caso:

create procedure Aggiorna(par1, par2) is

LANGUAGE JAVA NAME ('InserisciDip.addDept(java.lang.String, java.lang.String');

End;

A questo punto sarà sufficiente far eseguire da SQL/PLUS:

execute Aggiorna ('Marco', 'Rossi');

ed avremo aggiornato il db tramite il codice PL/SQL scritto in una classe Java.

Di seguito è riportato un esempio di classe java che effettua un inserimento nella tabella dept tramite due paramentri che gli verranno passati dalla procedura PL/SQL:

import java.sql.*;
import oracle.jdbc.driver.*;

public class InserisciDip
{

public static void addDept (String deptName, String deptLoc) throws SQLException 
             {
                     
              //Creo una connessione
              Connection conn = DriverManager.getConnection("jdbc:oracle:thin:scott/tiger@HOSTNAME:PORT:SID");
              String sql = "SELECT max(deptno)+1 FROM dept";
              String sql2 = "INSERT INTO dept VALUES (?, ?, ?)";
              int deptID = 0;

              try
              {
                    //faccio eseguire la prima select
                     PreparedStatement pstmt = conn.prepareStatement(sql);
                     ResultSet rset = pstmt.executeQuery();
                     while (rset.next()) {deptID = rset.getInt(1);}
                     //setto i valori di deptName e deptLoc valorizzati da input                      
                     pstmt = conn.prepareStatement(sql2);
                     pstmt.setInt(1, deptID);
                     pstmt.setString(2, deptName);
                     pstmt.setString(3, deptLoc);
                     //eseguo insert
                     pstmt.executeUpdate(); 
                     //chiudo il ResultSet, il PreparedStatement e la connessione
                     rset.close();
                     pstmt.close();
                     conn.close();
              }
                     catch (SQLException e) 
                     {System.err.println(e.getMessage());}
              }
                     
}