Home ORDIX AG             Dienstleistung             Trainingsshop    Kunden / Referenzen Aktuelles    Kontakt
Home  Pfeil  ORDIX News  Pfeil  2/2005
suche: 
Dieser Artikel richtet sich an Datenbank- Anwendungsentwickler, die sich zusätzlich zum PL/SQL auch für Java als serverseitige Datenbank- Programmiersprache entscheiden können.

Glossar

RMI

RMI bedeutet Remote Method Invocation. Mit RMI besteht die Möglichkeit, innerhalb einer JVM, Methoden von Objekten aufzurufen, die sich in einer anderen JVM befinden.

RPC

Remote Procedure Call (RPC) ist ein Protokoll für die Implementierung verteilter Anwendungen.

Multithreading

Multithreading bedeutet, dass Programme parallel verarbeitet werden können.

Weiterführende Links

Literaturhinweise

  • Oracle und Java, Datenbankentwicklung für E-Commerce-Anwen­dungen, Elion Bonazzi, Glenn Stokol, Markt+Technik Verlag, 2002
  • Unleash the Power of Java Stored Procedures, Oracle Corporation,
    Oracle Technology Network, 2002
  • Simplify with Java Stored Procedures, Oracle Corporation,
    Oracle Technology Network, 2004
  • Oracle10g, Java
    Developer′s Guide Release 1 (10.1), Oracle Corporation, 2004

Verlagerung von
Java-Anwendungen in
die Datenbank (Teil I)

Oracle stellt seit der Oracle-Datenbankversion 8.1.5 eine Java Virtual Machine (OracleJVM) zur Verfügung. Mit Hilfe von OracleJVM besteht die Möglichkeit, Java-Anwendungen innerhalb der Datenbank ausführen zu lassen.

Dieser Beitrag zeigt anhand von Beispielen, wie Java-Anwendungen in eine Oracle 10g Datenbank geladen und ausgeführt werden können. Dabei werden ein Sicherheitskonzept in Bezug auf die Java-Klassen und die Anwendungsmöglichkeiten von Java Stored Procedures erläutert. Im ersten Teil dieser Reihe werden die Java Stored Procedures vorgestellt und die Besonderheiten bei der Entwicklung von Java-Anwendungen für die OracleJVM skizziert. Im zweiten Teil gehen wir dann auf das Resolving-Konzept zum Auffinden aller von einer Klasse benötigten Klassen innerhalb der Datenbank sowie einige Anwendungsmöglichkeiten von Java Stored Procedures ein.

Einsatz von Java Stored Procedures

In den meisten Fällen wird die Geschäftslogik sowohl von einer Datenbankanwendung als auch von Batchprogrammen aufgerufen. Mit Hilfe von Stored Procedures kann die Funktionalität zentral in der Datenbank abgelegt werden und sowohl von einer Dia­log-Anwendung als auch von einem Batchprogramm verwendet werden.

Durch die Nutzung von Stored Procedures lässt sich somit die Wiederverwendbarkeit der einzelnen Anwendungskomponenten steigern und die Komplexität der Anwendungen deutlich reduzieren. Da die Stored Procedures vorkompiliert in der Datenbank aufbewahrt werden, ist außerdem eine schnelle Ausführung und ein direkter Datenzugriff möglich. Dies stellt eine gute Performance sicher.

Java

Ein großer Nachteil von Stored Procedures ist jedoch die Datenbankabhängigkeit. Die meisten Datenbankhersteller stellen eine eigene Datenbankprogrammiersprache zur Verfügung, mit der Stored Procedures erstellt werden können.

Stored Procedures lassen sich nur mit hohem Aufwand von einem Datenbanksystem auf ein anderes Datenbanksystem migrieren. Um das Problem der Datenbankabhängigkeit zu lösen, kann man auf die sogenannten Java Stored Procedures zurückgreifen.

Die Java Stored Procedures werden in der Programmiersprache Java erstellt. Sie können auf jedem Rechnersystem mit Hilfe einer Java Virtual Machine (JVM) interpretiert und ausgeführt werden. Außerdem wird der Datenbankzugriff einer Java Stored Procedure mit einem JDBC-Datenbanktreiber sichergestellt, der für alle herkömmlichen Datenbanksysteme verfügbar ist.

OracleJVM

OracleJVM ist eine in den Kernel von Oracle implementierte Java Virtual Machine. Ab der Oracle Version 10g stehen J2SE 1.4 (Java 2 Standard Edition) und der Datenbanktreiber JDBC 3.0 in der Datenbank zur Verfügung. Somit können Java-Programme, die in einer OracleJVM Version 10g ablaufen, den vollen Umfang der Java-Funktionsbibliotheken von J2SE 1.4 nutzen.

Die Java-Funktionsbibliotheken von J2SE be­finden sich auf dem Datenbankrechner in ei­nem $ORACLE_HOME/javavm/admin Verzeichnis. Da der vom Java-Compiler erstellte Java-Bytecode zur Laufzeit vom Interpreter (OracleJVM) interpretiert und ausgeführt wird, entstehen Ineffizienzen bei der Programm­ausführung.

Um diesen Engpass zu beseitigen, sind alle Java-Funktionsbibliotheken mit einem betriebssystemabhängigen Native Compiler übersetzt. Ein Native Compiler übersetzt den Java-Bytecode einer Java-Klasse in einen betriebssys­tem­abhängigen Programmcode und senkt damit deutlich die Ausführungsgeschwindigkeit von Java-Programmen, die innerhalb von OracleJVM ablaufen. Oracle bietet auch die Möglichkeit, selbst entwickelte Java-Klassen mit einem Native Compiler zu übersetzen.

Besonderheiten bei der Entwicklung von Java-Anwendungen für die OracleJVM

Bei der Entwicklung von Java-Anwendungen für die OracleJVM muss man auf folgende Besonderheiten achten:

  • Jede statische Methode kann als Programm­einstiegspunkt verwendet werden.
  • Es gibt keine Unterstützung für GUI-Kom­po­nenten wie AWT und Swing.
  • Multithreading wird nicht unterstützt.
  • Es muss der serverseitige JDBC-Treiber verwendet werden.

Programmeinstiegspunkt

In einer herkömmlichen JVM-Umgebung stellt die main-Methode einer Java-Klasse den Einstiegspunkt eines Programms dar. Beim Starten einer Klasse mit dem Kommando java wird die main-Methode dieser Klasse aufgerufen. Der Einstiegspunkt einer in der Datenbank gespeicherten Java-Klasse kann eine beliebige, statische Methode dieser Klasse sein.

Keine Unterstützung für GUI-Komponenten wie AWT und Swing

Da es sich bei den Java-Anwendungen, die in­nerhalb einer OracleJVM ablaufen, um server­seitige Anwendungen handelt, ist die Verwendung von grafischen Komponenten (GUI) nicht sinnvoll. Daher wird die Erzeugung von Instanzen der GUI-Objekte wie z. B. AWT und Swing nicht unterstützt. Allerdings kann eine GUI-Funktionalität, wie z. B. die Erstellung und Manipulation von Bildern, verwendet werden, solange die erstellten bzw. veränderten Bilder nicht angezeigt werden.

Fehlende Unterstützung für Multithreading-Verfahren

Eine weitere Besonderheit bei der Entwicklung von Java-Anwendungen für die OracleJVM ist die fehlende Unterstützung für das Multithreading-Verfahren. In einer OracleJVM werden alle Threads nacheinander anstatt gleichzeitig ausgeführt. Java-Anwendungen müssen allerdings wegen dieser Besonderheit nicht extra angepasst werden. Sie können unverändert in der serverseitigen OracleJVM ausgeführt werden.

Serverseitige JDBC-Treiber

Beim Zugriff von Java Stored Procedures auf die Datenbankinhalte muss ein serverseitiger JDBC-Treiber verwendet werden. Bei der Verwendung dieses datenbankinternen JDBC-Treibers sind folgende Punkte zu berücksichtigen:

  • Der serverseitige JDBC-Treiber muss im Programm nicht registriert werden, da dieser bereits im Oracle Server integriert ist.
  • Die Datenbankverbindung kann nicht physisch geschlossen werden.
  • Die Verwendung von Auto-Commits wird nicht unterstützt.
  • Eine Verbindung zu einer entfernten Datenbank ist mit dem serverseitigen JDBC-Treiber nicht möglich.
  • Die JDBC-Datenbankverbindung wird mit der Zeichenkette jdbc:default:connection: ermittelt, z. B. DriverManager.getConnection("jdbc:default:connection:").

Java-Klassen ohne Datenbankzugriff

Die o. g. Besonderheiten bei der Entwicklung von Java-Anwendungen für die OracleJVM beziehen sich auf Java-Klassen, die einen Datenbankzugriff enthalten. Java-Klassen, die über keinen Datenbankzugriff verfügen, müssen nicht angepasst werden. Sie können ohne Änderungen in der OracleJVM-Umgebung verwendet werden.

Erstellung von Java-Klassen

Java-Klassen, die aus einer Java Stored Procedure aufgerufen werden, können mit Hilfe einer beliebigen Java-Entwicklungsumgebung wie z. B. JDeveloper erstellt, kompiliert und getestet werden. Der Quellcode in Abbildung 1 stellt eine typische Client/Server Java-Datenbankanwendung dar, die auf einem Client mit einer Java Virtual Machine lokal aufgerufen wird.

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

public class Mitarbeiter extends Object {
  public static void setGehalt(int ma_nr, int gehalt) {
    try {
      DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
      Connection conn = DriverManager.getConnection(
      "jdbc:oracle:thin:@localhost:1521:orcl10","mf","mf");
      String sqlUpdate = "UPDATE ma SET gehalt = ? WHERE ma_nr = ?";
      PreparedStatement pstmt = conn.prepareStatement(sqlUpdate);
      pstmt.setInt(1, gehalt);
      pstmt.setInt(2, ma_nr);
      pstmt.executeUpdate();
      conn.close();
      pstmt.close();
    } catch(Exception e) { e.printStackTrace(); }
  }
  public static void main(String[] args) { setGehalt(1, 900); }
}
Abb. 1: Eine typische Client/Server Java-Datenbankanwendung(vergrößern!).

Um das in Abbildung 1 dargestellte Programm in eine Oracle Datenbank zu verlagern, müssen die zuvor dargestellten Besonderheiten bei der Entwicklung von Java-Anwendungen für die OracleJVM berücksichtigt werden.

Der Quellcode in Abbildung 2 zeigt die bereits für die Ausführung in der Datenbank veränderte Klasse Mitarbeiter. In der Klasse Mitarbeiter sind zum einen die Regis­trierung des JDBC-Treibers entfernt und die Ermittlung der serverseitigen Datenbankanbindung verändert worden. Zum anderen wurde die close Anweisung zum Schließen der Datenbankanbindung entfernt.

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

public class Mitarbeiter extends Object {
  public static void setGehalt(int ma_nr, int gehalt) {
   try {
     //DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
     Connection conn = DriverManager.getConnection(
     "jdbc:default:connection:");
     String sqlUpdate = "UPDATE ma SET gehalt = ? WHERE ma_nr = ?";
     PreparedStatement pstmt = conn.prepareStatement(sqlUpdate);
     pstmt.setInt(1, gehalt);
     pstmt.setInt(2, ma_nr);
     pstmt.executeUpdate();
     //conn.close();
     pstmt.close();
   } catch(Exception e) { e.printStackTrace(); }
  }
}
Abb. 2: Für die Ausführung in der Datenbank veränderte Klasse Mitarbeiter(vergrößern!).

Laden der Java-Klassen in die Datenbank

Um eine Java-Klasse in einer OracleJVM-Umgebung zu starten, muss diese zunächst in ein Datenbankschema geladen werden. Dabei akzeptiert ein Datenbankschema folgende Dateiarten:

  • Java-Sourcecode-Datei (*.java)
  • Java-Class-Datei (*.class)
  • Java-Ressource-Datei (*.jar bzw. *.zip)

Wird eine Java-Sourcecode-Datei in ein Datenbankschema geladen, so wird diese implizit mit dem in der Datenbank verfügbaren Java-Compiler in eine Java-Class-Datei übersetzt.

Mit der Anweisung in Abbildung 3 wird eine Java-Klasse Jora in ein Datenbankschema mf geladen.

loadjava -user mf/mf@orcl10 -resolve -verbose C:\java\Jora.class
Abb. 3: Die Klasse Jora wird in das Datenbankschema mf geladen.

Das Setzen der Option -resolve des Kom­man­dos loadjava legt fest, dass nach dem Laden und Kompilieren von Java-Klassen die Ver­füg­barkeit der abhängigen Java-Klas­sen überprüft werden soll. Die Option -verbose aktiviert die Ausgabe der Meldungen auf dem Bildschirm, während die Klassen in die Datenbank hoch­ge­laden werden.

Alternativ zum loadjava Kommando lässt sich das DBMS_JAVA Paket verwenden. Dieses Paket verfügt über Prozeduren, mit denen unter anderem Java-Klassen in die Datenbank geladen und aus der Datenbank entfernt werden können. Mit dem in Abbildung 4 gezeigten Kommando wird eine Jora Klasse in das Datenbankschema mf der orcl10 Datenbank geladen.

exec dbms_java.loadjava('-user mf/mf@orcl10 -resolve 
-verbose C:\java\Jora.class')
Abb. 4: Kommando, mit dem die Jora Klasse in das Datenbankschema mf der orcl10 Datenbank geladen wird.

exec dbms_java.grant_permission(
'MF','java.io.FilePermission','C:\java\*','read')
Abb. 5: Der Benutzer MF erhält ein Leserecht auf alle Dateien im Verzeichnis C:\java.

Java-Klassen in einer Oracle Datenbank
Abb. 6: Laden von Java-Klassen in
eine Oracle Datenbank..

Voraussetzung für den Ladevorgang mit dem DBMS_JAVA Paket ist allerdings das Leserecht des Datenbankbenutzers auf die zu ladende Datei. In der OracleJVM-Umgebung wird der Zugriff auf Ressourcen mit einer sogenannten Java-Policy Tabelle sichergestellt. Diese Tabelle gehört dem Benutzer SYS und kann über die Views DBA_JAVA_POLICY und USER_JAVA_POLICY abgefragt werden.

Mit dem Kommando in Abbildung 5 wird dem Benutzer mf ein Leserecht auf alle in dem Verzeichnis C:\java vorhandenen Dateien erteilt.

Bei der Angabe des Verzeichnisses kann mit der Syntax C:\java\- festgelegt werden, dass das Leserecht auch noch für die im Verzeichnis vorhandenen Unterverzeichnisse gel­ten soll.

Abbildung 6 veranschaulicht das Laden von Java-Klassen in eine Oracle Datenbank.

Java-Klassen aus der Datenbank entfernen

Wird eine Java-Klasse in der Datenbank nicht mehr benötigt, so kann diese mit dem dropjava Kommando aus der Datenbank entfernt werden. Die beiden in Abbildung 7 aufgeführten Kommandos entfernen die Jora Klasse aus der orcl10 Datenbank.

dropjava -user mf/mf@orcl10 -verbose Jora
oder
exec dbms_java.dropjava('-user mf/mf@orcl10 -verbose Jora')
Abb. 7: Zwei alternative Kommandos, die die Jora Klasse aus der orcl10 Datenbank entfernen.

Ausblick

In der nächsten Ausgabe erläutern wir unter anderem das Resolving-Konzept und die Veröffentlichung von Java-Klassen in der Datenbank. Außerdem stellen wir einige Anwendungsmöglichkeiten von Java Stored Procedures vor.

Markus Fiegler (info@ordix.de).