Verlagerung von Java-Anwendungen in die Datenbank (Teil I) Autor: Markus Fiegler, ORDIX AG DOAG News Q2_2005 Dieses Werk ist urheberrechtlich geschützt. Die dadurch begründeten Rechte, insbesondere die der Übersetzung, des Nachdrucks, des Vortrags, der Entnahme von Abbildungen und Tabellen, der Funksendung, der Mikroverfilmung oder der Vervielfältigung auf anderen Wegen und der Speicherung in Datenverarbeitungsanlagen, bleiben, bei auch nur auszugsweiser Verwertung, vorbehalten. Eine Vervielfältigung dieses Werkes oder von Teilen dieses Werkes ist auch im Einzelfall nur in den Grenzen der gesetzlichen Bestimmungen des Urheberrechtes der Bundesrepublik Deutschland vom 9. September 1965 in der jeweils geltenden Fassung zulässig. Sie ist grundsätzlich vergütungspflichtig. Zuwiderhandlungen unterliegen den Strafbestimmungen des Urheberrechtsgesetzes. ©2005 Oracle stellt seit der Datenbank-Version 8.1.5 eine Java Virtual Machine (JVM) zur Verfügung. Damit lassen sich Java-Anwendungen innerhalb der Datenbank ausführen. Beispiele zeigen, wie Java-Anwendungen in eine Oracle-10g-Datenbank geladen und ausgeführt werden. Gleichzeitig wird ein Sicherheitskonzept in Bezug auf die Java-Klassen und die Anwendungsmöglichkeiten von Java Stored Procedures erläutert. Einsatz von Java Stored Procedures In den meisten Fällen wird die Geschäftslogik sowohl von einer Datenbank-Anwendung als auch von Batch-Programmen aufgerufen. Mithilfe der Stored Procedures kann die Funktionalität zentral in der Datenbank abgelegt und von einer Dialog-Anwendung oder einem Batch-Programm 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 bereits kompiliert in der Datenbank aufbewahrt werden, ist außerdem eine schnelle Ausführung und ein direkter Datenzugriff möglich, was eine gute Performance sicher stellt. Ein großer Nachteil von Stored Procedures ist jedoch die Datenbank-Abhängigkeit, denn die meisten Datenbank-Hersteller stellen dafür eine eigene Datenbank-Programmiersprache zur Verfügung. Stored Procedures lassen sich deshalb nur mit hohem Aufwand von einem Datenbank-System auf ein anderes migrieren. Um dieses Problem zu lösen, greift man auf die so genannten Java Stored Procedures zurück. Sie werden in Java erstellt und können auf jedem Rechner-System mithilfe einer Java Virtual Machine interpretiert und ausgeführt werden. Der Datenbank-Zugriff einer Java Stored Procedure wird mit einem JDBC-Datenbank-Treiber sicher gestellt, der für alle herkömmlichen Datenbank-Systeme verfügbar ist. OracleJVM OracleJVM ist eine in den Kernel von Oracle implementierte Java Virtual Machine (JVM). Ab der Version 10g stehen J2SE 1.4 (Java 2 Standard Edition) und der Datenbank-Treiber JDBC 3.0 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 liegen in einem $ORACLE_HOME/javavm/admin-Verzeichnis auf dem Datenbank-Rechner. Da der vom Java-Compiler erstellte Java-Bytecode zur Laufzeit vom Interpreter (OracleJVM) interpretiert und ausgeführt wird, entstehen Ineffizienzen bei der Programmausfü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 JavaKlasse in einen Betriebssystem-abhängigen Programm-Code 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. ©2005 Java-Anwendungen für die OracleJVM Bei der Entwicklung sind folgende Besonderheiten zu beachten: - Jede statische Methode kann als Programm-Einstiegspunkt verwendet werden. - Es gibt keine Unterstützung für GUI-Komponenten wie AWT und Swing. - Multi-Threading wird nicht unterstützt. - Es muss der Server-seitige JDBC-Treiber verwendet werden. Der Programm-Einstiegspunkt In einer herkömmlichen JVM-Umgebung stellt die main-Methode einer Java-Klasse den Einstiegspunkt eines Programms. 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 innerhalb einer OracleJVM ablaufen, um Server-seitige Anwendungen handelt, ist die Verwendung von grafischen Komponenten (GUIs) nicht sinnvoll. Daher wird die Erzeugung von Instanzen der GUI-Objekte wie z. B. AWT und Swing nicht unterstützt. Allerdings kann die GUI-Funktionalität wie z.B. die Erstellung und Manipulation von Bildern verwendet werden, solange die erstellten beziehungsweise veränderten Bilder nicht angezeigt werden. Fehlende Unterstützung für Multi-Threading-Verfahren Eine weitere Besonderheit bei der Entwicklung von Java-Anwendungen für die OracleJVM ist die fehlende Unterstützung für das Multi-Threading-Verfahren. In einer OracleJVM werden alle Threads nacheinander statt gleichzeitig ausgeführt. Java-Anwendungen müssen allerdings wegen dieser Besonderheit nicht extra angepasst werden; sie können unverändert in der Server-seitigen OracleJVM ausgeführt werden. Server-seitige JDBC-Treiber Beim Zugriff von Java Stored Procedures auf die Datenbank-Inhalte muss ein Server-seitiger JDBCTreiber benutzt werden. Bei der Verwendung dieses Datenbank-internen JDBC-Treibers sind folgende Punkte zu berücksichtigen: - Der Server-seitige JDBC-Treiber muss im Programm nicht registriert werden, da dieser bereits im Oracle-Server integriert ist. - Die Datenbank-Verbindung kann nicht physisch geschlossen werden. - Die Verwendung von Auto-Commits wird nicht unterstützt. - Eine Verbindung zu einer entfernten Datenbank ist mit dem Server-seitigen JDBC-Treiber nicht möglich. - Die JDBC-Datenbank-Verbindung wird mit der Zeichenkette „jdbc:default:connection:“ ermittelt, z.B. DriverManager.getConnection („jdbc:default:connection:“). ©2005 Java-Klassen ohne Datenbank-Zugriff Die oben beschriebenen Besonderheiten bei der Entwicklung von Java-Anwendungen für die OracleJVM beziehen sich auf Java-Klassen, die einen Datenbank-Zugriff enthalten. Java-Klassen, die über keinen Datenbank-Zugriff verfügen, muss man nicht anpassen. 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 nachfolgende Quellcode stellt eine typische Client/Server-Java-Datenbank-Anwendung 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); } } Um das 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 nachfolgende Quellcode zeigt die bereits für die Ausführung in der Datenbank veränderte Klasse Mitarbeiter. ©2005 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(); } } } In der Klasse Mitarbeiter, die für die Ausführung im OracleJVM angepasst wurde, sind zum einen die Registrierung des JDBC-Treibers entfernt und die Ermittlung der Server-seitigen Datenbank-Anbindung verändert worden. Zum anderen wurde die close-Anweisung zum Schließen der DatenbankAnbindung entfernt. Laden der Java-Klassen in die Datenbank Um eine Java-Klasse in einer OracleJVM-Umgebung zu starten, muss diese zunächst in ein Datenbank-Schema geladen werden. Dabei sind folgende Dateiarten akzeptiert: - Java-Sourcecode-Datei (*.java) - Java-Class-Datei (*.class) - Java-Ressource-Datei (*.jar bzw. *.zip) Wird eine Java-Sourcecode-Datei in ein Datenbank-Schema geladen, so wird diese implizit mit dem in der Datenbank verfügbaren Java-Kompiler in eine Java-Class-Datei übersetzt. Mit der folgenden Anweisung wird eine Java-Klasse Jora in ein Datenbank-Schema MF geladen: loadjava -user mf/mf@orcl10 -resolve -verbose C:\java\Jora.class Die Option –resolve des loadjava-Kommandos besagt, dass nach dem Laden und Kompilieren von ©2005 Java-Klassen die Verfügbarkeit der abhängigen Java-Klassen überprüft werden soll. Die Option –verbose aktiviert die Ausgabe der Meldungen auf dem Bildschirm, während die Klassen in die Datenbank hochgeladen 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 folgenden Kommando wird eine Jora-Klasse in das Datenbank-Schema MF der ORCL10-Datenbank geladen: exec dbms_java.loadjava( '-user mf/mf@orcl10 -resolve -verbose C:\java\Jora.class') Voraussetzung für den Ladevorgang mit dem DBMS_JAVA-Paket ist allerdings das Leserecht des Datenbank-Benutzers 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 folgenden Kommando wird dem Benutzer MF ein Leserecht auf alle in dem Verzeichnis C:\java vorhandenen Dateien erteilt: exec dbms_java.grant_permission( 'MF','java.io.FilePermission','C:\java\*','read') 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 gelten soll. Die nachfolgende Abbildung veranschaulicht das Laden von Java-Klassen in eine Oracle-Datenbank. Abb. 1: Laden von Java-Klassen in eine Oracle-Datenbank. ©2005 Java-Klassen aus der Datenbank entfernen Wird eine Java-Klasse in der Datenbank nicht mehr benötigt, so kann diese mit dem dropjavaKommando aus der Datenbank entfernt werden. Das folgende Kommando entfernt 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') Ausblick In der nächsten Ausgabe werden unter anderem das Resolving-Konzept und die Veröffentlichung von Java-Klassen in der Datenbank erläutert sowie einige Anwendungsmöglichkeiten von Java Stored Procedures vorgestellt. Literaturhinweise - Oracle und Java, Datenbankentwicklung für E-Commerce-Anwendungen, Elion Bonazzi, Glenn Stokol, Markt &Technik Verlag, 2002 - Unleash the Power of Java Stored Procedures, Oracle Corporation, http://otn.oracle.com/, 2002 - Simplify with Java Stored Procedures, Oracle Corporation, http://otn.oracle.com/, 2004 - Oracle10g, Java Developer's Guide Release 1 (10.1), Oracle Corporation, 2004 Kontakt: Markus Fiegler [email protected] ©2005