Datenbankanbindung: JDBC und SQLJ

Werbung
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
Prof. Dr. Klaus Dittrich
Datenbankanbindung: JDBC und SQLJ
Betreuung: Anca Dobre
Jussi Prevost
Borrweg 60
8055 Zürich
[email protected]
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
Inhaltsverzeichnis
1.
Einleitung ............................................................................................. 3
2.
Grundlagen .......................................................................................... 3
2.1.
Einsatzfälle........................................................................................................... 3
2.2.
Die Architektur von JDBC...................................................................................4
A.
Treiber ..............................................................................................................4
B.
Programmierkonzept ........................................................................................5
2.3.
SQLJ .................................................................................................................... 8
A.
Zweck ...............................................................................................................9
B.
Überblick .......................................................................................................... 9
C.
Hostausdrücke ................................................................................................10
D.
In or Out?........................................................................................................11
E.
Iteratoren.........................................................................................................11
3.
Vergleich ............................................................................................ 12
3.1.
Verbindungsaufbau ............................................................................................14
3.2.
Insert ..................................................................................................................14
3.3.
Select.................................................................................................................. 14
3.4.
Online-Überprüfung...........................................................................................14
3.5.
Geschwindigkeit ................................................................................................15
4.
Zusammenfassung............................................................................. 15
Literaturverzeichnis.................................................................................. 16
2
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
1. Einleitung
JDBC (Java DataBase Connectivity)1 ist eine herstellerneutrale Programmierschnittstelle
(Application programming interface, API). Sie erlaubt den Programmierern, Verbindung zu
einer Datenbank herzustellen und mittels SQL die Datenbank abzufragen oder zu
aktualisieren, ohne sich um die spezifischen Eigenheiten eines bestimmten Datenbanksystems
zu kümmern. Um die Unterschiede kümmern sich sogenannte JDBC-Treiber, welche von den
jeweiligen Datenbankherstellern bereitgestellt werden. Die JDBC-API ist vollständig in Java
implementiert und ist somit auch plattformunabhängig.
SQLJ befasst sich mit verschiedenen Aspekten der Integration von SQL und Java. SQLJ
besteht aus drei Teilen: Der erste Teil (SQLJ Teil 0) stellt eine standardisierte Syntax und
Semantik für die Einbettung von SQL in Java-Programmen zur Verfügung. Dabei bietet er
gegenüber dem JDBC-Kit eine einfachere Verwendung und Überprüfungsmöglichkeiten der
SQL Abfragen schon zu Übersetzungszeit. Der zweite Teil (SQLJ Teil 1) befasst sich mit der
Implementierung von Prozeduren und Funktionen in Java, welche in einer Datenbank
gespeichert werden können. Der dritte Teil (SQLJ Teil 2) beschreibt Möglichkeiten, wie man
Java-Datentypen und -Klassen als benutzerdefinierte SQL-Datentypen in einer Datenbank
ablegen kann.
In dieser Arbeit werde ich mich auf JDBC und SQLJ Teil 02 konzentrieren, weil es um einen
Überblick von Datenbankanbindung in Java geht. Wer sich für weitere Details von SQLJ Teil
1 und 2 interessiert, findet im Literaturverzeichnis weiterführende Referenzen.
2. Grundlagen
2.1. Einsatzfälle
Wer eine Anwendung oder ein Applet in Java realisiert und dabei eine Datenbankanbindung
benötigt, braucht JDBC oder SQLJ. Anwendungen haben dabei vollkommen freie Hand, um
auf (verteilte) Datenbankserver zuzugreifen. Applets können nur eine Datenbankverbindung
zu dem Server öffnen, von dem sie der Benutzer heruntergeladen hat. Das heisst Datenbankund Webserver müssen sich auf demselben Computer befinden, was kein typisches Setup ist.
Für signierte Applets sind diese Restriktionen gelockert.
Für die Realisierung der Anwendung spielt es keine Rolle, ob eine herkömmliche Client/Server-Architektur oder eine Multi-Tier-Architektur verwendet wird.
1
JDBC ist ein geschützter Markenname und eigentlich keine Abkürzung. Trotzdem wird JDBC vielfach als eine
Abkürzung für Java Database Connectivity betrachtet.
2
Ich verwende ab jetzt nur noch die Bezeichnung SQLJ.
3
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
2.2. Die Architektur von JDBC
JDBC besteht aus zwei Schichten. Die obere Schicht ist die eigentliche JDBC–API. Diese
kommuniziert mit der unteren Schicht, dem JDBC-Treibermanager-API, und sendet dieser die
verschiedenen SQL-Anweisungen. Der Treibermanager sollte mit den verschiedenen Treibern
von Drittherstellern kommunizieren, welche die eigentliche Verbindung zur Datenbank
herstellen.
Der Programmierer muss sich nicht um herstellerspezifische Eigenheiten von DBMS
kümmern. Diese Unterschiede übernimmt ein JDBC-Treiber vom jeweiligen Datenbankhersteller. Abbildung 1 gibt einen Überblick.
Abbildung 1: Die Architektur von JDBC
A. Treiber
Wie aus Abbildung 1 ersichtlich ist, werden vier verschiedene Typen von Treibern unterschieden, die zumindest den SQL-92 Entry Level unterstützen müssen.
1. Ein Typ 1 Treiber (JDBC-ODBC Brücke) benützt die ODBC3-Schnittstelle, um mit
der Datenbank zu kommunizieren. Ein solcher Treiber wird im JDK angeboten.
Allerdings muss auf allen Clients eine ODBC-Schnittstelle installiert sein, was nicht
immer ist bzw. möglich wäre.
2. Ein Typ 2 Treiber (Native API Treiber) ist zum Teil in Java geschrieben, benutzt
aber bestimmte Schnittstellen von DBMS. Für eine Installation wird nicht nur die
3
Open Database Connectivity; DB-Zugriffsschnittstelle von Microsoft
4
Universität Zürich
Institut für Informatik
Java-Bibliothek benötigt,
Datenbankhersteller.
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
sondern
auch
Bibliotheken
vom
jeweiligen
3. Ein Typ 3 Treiber (JDBC-Net Treiber) ist eine reine Client-Bibliothek in Java, die
über ein datenbankunabhängiges Protokoll Datenbankanforderungen an eine Serverkomponente schickt, die wiederum die Anforderungen in ein datenbankspezifisches
Protokoll umsetzt. Die Client-Bibliothek ist unabhängig von der eigentlichen
Datenbank und vereinfacht somit die Entwicklung.
4. Ein Typ 4 Treiber (Native-Protokoll Treiber) ist auch eine reine Java-Bibliothek, die
JDBC-Anforderungen direkt in ein datenbankspezifisches Protokoll übersetzt.
Die meisten Datenbankhersteller stellen ihren Datenbanken entweder einen Typ 3 oder Typ 4
Treiber zur Verfügung. Typ 1 und Typ 2 Treiber werden oft für das Testen verwendet oder in
Unternehmensnetzwerken, wo die zusätzliche Installation der notwendigen Bibliotheken kein
grosses Problem sein sollte.
B. Programmierkonzept
Die für die JDBC-Programmierung verwendeten Klassen sind im Paket java.sql enthalten.
Abbildung 2 zeigt die wichtigsten Klassen.
Abbildung 2: Die wichtigsten Klassen in JDBC
Bei der Programmierung geht man nach folgenden Schritten vor:
1. Verbindung zur Datenbank herstellen (über den JDBC-Treiber)
2. SQL-Anweisung an ein sogenanntes Statement-Objekte übergeben
3. Statement ausführen
4. evt. Abfrageergebnis verarbeiten
5. nicht mehr verwendete Objekte freigeben
Auf diese fünf Punkte werde ich jetzt kurz eingehen. In Kapitel 3 folgt dann ein
zusammenhängendes Beispiel.
5
Universität Zürich
Institut für Informatik
1)
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
Verbindung herstellen
Über die Klasse DriverManager wählt man einen Datenbanktreiber aus und erstellt eine neue
Datenbankverbindung. Der Treibermanager muss einen Treiber allerdings erst registrieren,
bevor er ihn aktivieren kann. Das folgende Beispiel registriert die JDBC-ODBC-Brücke von
Sun:
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Jetzt kann die Verbindung zur Datenbank hergestellt werden. Die Verbindung wird dabei als
Objekt vom Typ Connection repräsentiert. Zum Verbindungsaufbau werden drei Parameter
benötigt. Der erste Parameter ist eine URL4, welche den Verbindungsmechanismus, den
Treiber und den Namen der Datenbank beinhaltet. Die beiden anderen Parameter sind
Benutzername und Passwort.
String url = "jdbc.odbc.seminarDB";
String username = “Jussi”;
String pw = "seminar";
Connection con = DriverManager.getConnection(url, username, pw);
Die Verbindung zur Datenbank steht nun.
2)
SQL-Anweisungen
SQL-Anweisungen werden als Statement-Objekte behandelt. Diese Objekte senden
Anweisungen zur Datenbank und empfangen die zurückgelieferten Ergebnisse. Dabei werden
drei verschiedene Arten von Statements unterschieden:
1. java.sql.Statement ist die Basisklasse für die Verarbeitung von Anweisungen. Ein
Statement-Objekt kann dabei für mehrere Abfragen und Anweisungen benutzt werden
(z.B. innerhalb einer Transaktion).
2. java.sql.PreparedStatement ist eine Klasse, welche die Verarbeitung von
vorbereiteten Anweisungen zulässt. Dabei wird ein sogenannter IN-Parameter (vgl. zu
IN und OUT Seite 11) in der SQL-Anweisung durch eine Hostvariable ersetzt. Als
Platzhalter dient ein „?“. Bevor man nun die vorbereitete Anweisung ausführt, muss
man die Hostvariable mit der passenden set-Methode an den tatsächlichen Wert
binden. Es gibt für jeden Datentyp eine set-Methode.
3. java.sql.CallableStatement ist eine Klasse, welche die Verarbeitung von
gespeicherten Prozeduren ermöglicht. Darauf gehe ich nicht weiter ein.
3)
Ausführen von Anweisungen
Zum Ausführen der Anweisungen benötigt man noch zusätzliche Methoden. Dabei muss man
zwischen Abfragen (SELECT) und anderen Anweisungen (Datendefinition und
Datenmanipulation) unterscheiden.
6
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
Für Abfragen dient executeQuery(String sql) Im Erfolgsfalle wird das Ergebnis in
einem java.sql.ResultSet zurückgeliefert. Die Ergebnismenge kann dabei durch den
Aufruf von next() zeilenweise durchlaufen werden.
Statement stmt = con.createStatement();
String query = "SELECT Nachname, Gehalt FROM Angestellter "
+ "WHERE Gehalt > 5000";
ResultSet rs = stmt.executeQuery(query);
while (rs.next())
System.out.println("Name: " + rs.getString(1) + " | "
+ "Gehalt: " + rs.getDouble("Gehalt"));
rs.close();
Für Datendefinitionen und Datenmanipulationen muss hingegen executeUpdate(String
command) verwendet werden. Diese Methode liefert im Erfolgsfall die Anzahl veränderter
Zeilen zurück, die von der SQL-Anweisung betroffen waren.
Statement stmt = con.createStatement();
String cmd = "UPDATE Angestellte SET Gehalt = Gehalt * 1.1"
+ "WHERE Nachname = 'Prevost'";
stmt.executeUpdate(cmd);
Daneben gibt es noch eine allgemeine Methode execute, mit der sich beliebige SQLAnweisung ausführen lassen. Die Methode liefert true, wenn das Ergebnis ein ResultSet ist,
und false, wenn es ein Integer-Wert ist.
4)
Ergebnisse von Abfragen
Wie wir gesehen haben, werden Abfrageergebnisse in einem java.sql.ResultSet
dargestellt. Ein ResultSet verwaltet einen internen Zeiger, der einen tupelweisen Zugriff auf
die Ergebnismenge erlaubt (Ähnlich dem java.util.Iterator). Der Zeiger ist dabei zu
Beginn auf eine Position vor der ersten Zeile gesetzt. Folglich muss man die Methode next5
einmal aufrufen, um den Iterator auf die erste Zeile zu setzen. Dies wird normalerweise in
einer while-Schleife gemacht:
while (rs.next()) {
Zeile der Ergebnismenge betrachten
}
Für den Zugriff auf den Inhalt einer Spalte gibt es verschiedene Zugriffsmethoden. Für jeden
Java-Typ gibt es Zugriffsmethoden wie getString und getDouble. Jede Zugriffsmethode
hat zwei Formen, eine mit einem numerischen Parameter und eine mit einer Zeichenfolge.
Übergibt man ein numerisches Argument, bezieht man sich auf die Spalte mit dieser Nummer
(z.B. rs.getString(1);). Zu beachten ist, dass die Nummerierung der Datenbankspalten
4
5
Uniform Resource Locator
Im JDBC 2 wurden auch andere Navigationsmethoden eingeführt.
7
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
mit 1 beginnt und nicht mit 0. Übergibt man eine Zeichenfolge, bezieht man sich auf die
Spalte mit diesem Namen (z.B. rs.getDouble("Gehalt");).
Numerische Argumente sind zwar etwas effizienter, aber mit Zeichenfolgen ist der Code
verständlicher und leichter zu pflegen.
SQL- und Java-Datentypen sind nicht genau gleich. Jede get-Methode führt eine sinnvolle
Typumwandlung durch, wenn der Typ der Methode nicht dem Typ der Spalte entspricht. Z.B.
können fast alle Typen als Strings dargestellt werden. Tabelle 1 gibt einen Überblick über die
wichtigsten Typumwandlungen.
SQL-Datentyp
Java-Datentyp
CHAR
String
VARCHAR
String
BIT
boolean
INTEGER
int
BIGINT
long
REAL
float
FLOAT
double
DOUBLE
double
BINARY
byte[]
DATE
java.sql.Date
TIME
java.sql.Time
Tabelle 1: Typabbildungen zwischen SQL und Java
5)
Freigabe von Objekten
Objekte, wie z.B. Connection, Statement oder ResultSet, welche nicht mehr gebraucht
werden, sollten durch die Methode close freigegeben werden. Ein ResultSet wird
üblicherweise nach Beenden der while-Schleife geschlossen. Somit wird es sofort
freigegeben und kann durch die automatische Speicherbereinigung entsorgt werden, was
nebenbei auch DBMS-Ressourcen freimacht. Statement-Objekte werden oft am Ende der
Methode geschlossen. Connection-Objekte werden meistens erst bei Beenden der Anwendung
geschlossen, da diese ständig gebraucht werden.
2.3. SQLJ
Es stellt sich die Frage, wieso man überhaupt SQLJ braucht. JDBC alleine reicht vollkommen
aus. Es zeigen sich allerdings zwei Schwächen:
1. Der (SQL-relevante) Code wird lange und unhandlich.
2. Fehler innerhalb SQL-Operationen werden erst zur Laufzeit entdeckt.
Wie wir sehen werden, löst SQLJ beide Probleme. Vom ersten Punkt und dessen Lösung
können wir uns in Kapitel 3 überzeugen.
8
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
A. Zweck
Java gehört zu den objektorientierten Programmiersprachen. SQL ist dagegen eine nichtprozedurale Sprache zur Definition und Manipulation von Daten in relationalen Datenbanken.
SQLJ ist der ANSI-Standard6 für die Einbettung von statischen SQL-Anweisungen in Java
Programmen.
Es ist das Produkt der Zusammenarbeit von Oracle, IBM, Sybase, Tandem, Informix,
JavaSoft, XDB und Microsoft.
B. Überblick
SQLJ ersetzt JDBC nicht, sondern benutzt es weiterhin. Für statische SQL-Anweisungen,
welche schon zum Entwicklungszeitpunkt bekannt sind, liefert SQLJ aber einen einfacheren
und robusteren Weg. Für dynamische SQL-Anweisungen, welche z.B. während der Laufzeit
von einem Endbenutzer kreiert werden, muss der bisherige „JDBC-Weg“ verwendet werden.
Natürlich können beide Möglichkeiten nebeneinander eingesetzt werden. JDBC und SQLJ
sind also keine Substitute, sondern Komplementärprodukte.
SQLJ-Anweisungen werden grundsätzlich mit
#sql { SQL-Ausdruck };
gekennzeichnet. Java-Code mit solchen Anweisungen kann somit nicht mehr in einem
herkömmlichen Java-Compiler übersetzt werden, sondern benötigt einen SQLJ-Translator.
Abbildung 3 zeigt das Vorgehen im Überblick.
Abbildung 3: Übersetzung eines SQLJ-Programms
6
seit 1999
9
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
Der Quellcode wird nicht in einer .java-Datei erfasst, sondern in einer .sqlj-Datei. Diese Datei
wird von einem Präprozessor, dem SQLJ-Translator, übersetzt. Dabei werden die SQLOperationen auf ihre Syntax und Semantik überprüft. Zusätzlich besteht die Möglichkeit des
Online-Überprüfens, d.h. der Translator kann die Anweisungen gegen das Datenbankschema
testen. Während der Übersetzung werden (mindestens) zwei Dateien generiert:
1. Eine Java-Quellcodedatei (.java-Datei): Sie enthält a) die SQL-Anweisungen, welche
durch Aufrufe an das SQLJ-Laufzeitsystems ersetzt wurden; und b) den „normalen“
Quellcode des Programms.
2. Ein sogenanntes Profil (Datei mit der Endung .ser), welches eine Beschreibung der
SQL-Anweisungen beinhaltet.
Nun kann der generierte Java-Quellcode mit einem Standard-Compiler (z.B. javac aus dem
JDK) übersetzt werden. Auch das Starten der Anwendung ist normal möglich. Das SQLJLaufzeitsystem wird dabei automatisch aufgerufen, wenn es sich um eine SQLJ-Anwendung
handelt. Zur Verbindung mit der Datenbank wird der JDBC-Treiber benutzt.
C. Hostausdrücke
Der zentrale Bestandteil der Integration von SQL in Java als Wirtssprache bilden die
sogenannten Hostausdrücke. Die einfachste Form davon sind die Hostvariablen. Das sind
normale Java-Variablen, welche den Konventionen der Typenabbildung zwischen Java und
SQL genügen (vgl. Tabelle 1). Es versteht sich von selbst, dass diese Variablen innerhalb des
Blockes sichtbar sein müssen. Hostvariablen werden in SQLJ-Anweisungen mit einem
vorangestellten Doppelpunkt gekennzeichnet. Z.B.:
double salaer; String name = "Prevost";
#sql {
SELECT Gehalt INTO :salaer
FROM Angestellter WHERE Nachname = :name };
Neben Hostvariablen können auch Hostausdrücke verwendet werden. Das sind u.a.:
•
Zuweisungen: z.B. Gehalt = Gehalt * (1.0 + bonus/100)
•
Inkrement und Dekrement: z.B. i++
•
Methodenaufrufe: z.B. name.toUpperCase()
Dabei müssen diese Ausdrücke innerhalb einer runden Klammer mit einem Doppelpunkt
davor stehen. Als Beispiel:
String[] namen = new String[] { "Prevost", "Scheff", "Knaller"};
double[] bonus = new double[] { 10.0, 5.0, 2.5 };
for (int i=0; i<namen.length; i++ ) {
#sql { UPDATE Angestellter
SET Gehalt = Gehalt * :(1.0 + bonus[i]/100)
WHERE Nachname = :(namen[i].toUpperCase()) };
}…
10
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
Doch noch nicht genug mit der Flexibilität von SQLJ. Es können auch gespeicherte
Prozeduren und Funktionen als Hostausdrücke verwendet werden. Dabei werden die
Schlüsselworte CALL für Prozeduren und VALUES für Funktionen benutzt. Das folgende
Beispiel ruft die (eingebaute) SQL-Funktion SYSDATE()7 auf und weist den Wert einer
Variable zu:
java.sql.Date heute;
#sql heute = { VALUES(SYSDATE()) };
System.out.println("Die Datenbank denkt, heute ist der " + heute);
D. In or Out?
Im vorherigen Abschnitt habe ich die Tatsache weggelassen, dass Hostvariablen (oder
-ausdrücke) in drei verschiedenen Modi benutzt werden können. IN bezeichnet Werte, die an
die Datenbank gesendet werde. Dagegen beschreibt OUT Variablen, die ihren Wert aus der
Datenbank bekommen. INOUT umfasst beide.
Normalerweise haben Hostausdrücke den Modus IN. Die Ausnahme sind Hostausdrücke in
der SELECT INTO Anweisung, welche OUT-Variablen sind. In diesen Fällen ist der Modus
implizit vorgegeben. In allen anderen Fällen, v.a. bei gespeicherten Prozeduren, muss der
Modus explizit festgelegt werden.
E. Iteratoren
Hostausdrücke eignen sich nur für den Austausch einzelner Werte zwischen SQL und Java.
Zur Speicherung von ganzen Ergebnismengen wird dagegen ein Iterator verwendet, ähnlich
einem java.sql.ResultSet. Im Unterschied zum JDBC, muss ein Iterator vorher deklariert
werden. Dabei wird zwischen zwei unterschiedlichen Arten unterschieden:
•
Positionsiteratoren: Sie werden durch den (Java-)Datentyp der Spalte charakterisiert.
Zum Abrufen der Werte wird die FETCH-Anweisung verwendet. Dies ist eine übliche
Vorgehensweise auch in anderen Sprachen mit eingebettetem SQL.
#sql iterator MyPosIter(String, double);
•
Benannte Interatoren: Hier kann neben dem Datentyp auch der Bezeichner gewählt
werden. Der Bezeichner liefert denn auch gerade noch den Namen für die
Zugriffsmethode auf den jeweiligen Spaltenwert. JDBC-Programmier sollten sich
unmittelbar mit dieser Art vertraut fühlen.
#sql iterator MyNamedIter(String name, double gehalt);
Dabei muss die Anzahl der Spalten der Ergebnisrelation mit der Anzahl der Attribute der
Iteratoren übereinstimmen. In unserem Fall muss die Ergebnisrelation zweidimensional sein.
Der SQLJ-Translator generiert aus der Iterator-Definition eine Iterator-Klasse mit dem
angegebenen Namen. Z.B. ergibt sich für den Positionsiterator eine Klasse MyPosIter. Bei
7
Zumindest bei Oracle
11
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
den benannten Iteratoren wird zusätzlich noch für jede Spalte eine Zugriffsmethode
geschaffen, also in unserem Beispiel zwei Methoden mit den Namen name() und gehalt().
3. Vergleich
Nun wissen wir genug, um SQLJ und JDBC zu vergleichen. Am Besten können wir das ganze
anhand eines Beispiels machen. Wie schon erwähnt, muss bei SQLJ das Datenbankschema
schon bekannt sein. Ich verwende folgende zwei Relationen:
1. Angestellter(Vorname, Nachname, AHV_Nr, Gehalt, Abt_Nr)
2. Abteilung(Abt_Name, Nr, Ort)
Im Beispiel wird zuerst ein INSERT und danach ein SELECT ausgeführt.
import java.sql.*;
import sqlj.runtime.*;
import sqlj.runtime.ref.*;
public class SeminarExample {
// Die beiden folgenden Variablen sind für die Abfrage vorgesehen
String name = "Müller";
double salary = 4000.0;
Connection con;
public static void main(String[] args) throws SQLException {
…// Treiber registrieren, Verbindung herstellen usw. (Vgl. Seite 6)
if (args[0].equals("jdbc"))
jdbcExample();
else if (args[0].equals("sqlj"))
sqljExample();
…
}
public void jdbcExample() throws SQLException {
// Zuerst INSERT durchführen
// SQL-Anweisung als Objekt vorbereiten
Statement stmt = con.createStatement();
// INSERT-Anweisung erstellen
String command = "INSERT INTO Angestellter "
+ "VALUES ('Jussi', 'Prevost', 2606, 10000.0, 3)";
// Anweisung ausführen
stmt.executeUpdate(command);
// Statement schliessen
stmt.close();
// SELECT ausführen
// Abfrage erstellen
String query = "SELECT Vorname, Nachname, AHV_Nr, Gehalt, Ort "
+ "FROM Angestellter, Abteilung "
+ "WHERE Nachname=? AND "
+ "Gehalt>? AND " + "Abt_Nr=Nr";
12
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
// PreparedStatements vorbereiten und Hostvariablen einbinden
PreparedStatement pstmt = con.prepareStatement(query);
pstmt.setString(1, name);
pstmt.setInt(2, salary);
// Abfrage ausführen
ResultSet rs = pstmt.executeQuery();
// und Resultate anzeigen
while(rs.next()) {
System.out.println("Vorname = " + rs.getString(1) + " | "
+ "Nachname = " + rs.getString(2) + " | " + "AHV_Nr = "
+ rs.getInt(3) + " | " + "Gehalt = " + rs.getDouble("Gehalt")
+ " | " + "Ort = " + rs.getString("Ort"));
}
// ResultSet- und Statement-Objekte schliessen
rs.close();
pstmt.close();
}
public void sqljExample() throws SQLException {
// Zuerst INSERT durchführen
// INSERT-Anweisung in der SQLJ-Syntax erstellen
#sql { INSERT INTO Angestellter(Vorname, Nachname, AHV_Nr, Gehalt, Abt_Nr)
values('Jussi', 'Prevost', 2606, 10000.0, 3) };
// SELECT ausführen
/* Iterator für das Speichern der Abfrage definieren
* ACHTUNG: MÜSSTE AUSSERHALB DES PROGRAMMS DEFINIERT WERDEN,
* z.B. nach den IMPORT-Anweisungen!!!!!!!
* Aus Übersichtsgründen habe ich es hier hergenommen
*/
#sql iterator MyIter(String Firstname, String Name,
int AHV_NR, double Salary, String City);
// Iterator-Objekt erstellen
MyIter iter;
// Abfrage ausführen und Ergebnis dem Iterator zuweisen
#sql iter = {
SELECT Vorname, Nachname, AHV_Nr, Gehalt, Ort
FROM Angestellter, Abteilung
WHERE Nachname = :name AND
Gehalt> :salary AND
Abt_Nr=Nr };
// Ergebnis Anzeigen
while (iter.next()) {
System.out.println("Vorname = " + iter.FirstName() + " | "
+ "Nachname = " + iter.Name() + " | " + "AHV_Nr = "
+ iter.AHV_NR()+ " | " + "Gehalt = " + iter.Salary ()
+ " | " + "Ort = " + iter.City());
}
// Iterator-Objekt freigeben
iter.close();
}
}
13
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
3.1. Verbindungsaufbau
Der Verbindungsaufbau funktioniert in beiden Varianten gleich.
Eine geschicktere Variante anstelle der manuellen Registrierung des Treibers, wäre die
Verwendung der Systemeigenschaft jdbc.drivers. Dabei könnte auch die URL, der
Benutzername und das Passwort in einer Eigenschaftsdatei (Datei mit der Endung .properties)
gespeichert werden und bequem auf die Endbenutzer eingestellt werden.
3.2. Insert
Eine Insert-Anweisung auf dem „JDBC-Weg“ umfasst mehrere Schritte, welche den Code
aufblähen: Zuerst muss ein Statement-Objekt erzeugt werden, dann ausgeführt und zum
Schluss sollte es wieder geschlossen werden. SQLJ braucht hingegen „nur“ eine Anweisung,
welche (beinahe) die reine SQL-Anweisung in der SQLJ-Syntax beinhaltet. Es wird auch
keine execute-Methode gebraucht.
Die Verwendung von SQLJ ist also viel einfacher, kürzer und deshalb auch einfacher zu
Warten.
3.3. Select
Bei der Select-Anweisung sieht es ähnlich aus. Die JDBC-Methode benötigt wieder mehrere
Schritte: (Prepared)Statement-Objekt schaffen, Variablen zuweisen, ausführen, Ergebnis
einem ResultSet zuweisen. Danach Ergebnis bearbeiten und zum Schluss ResultSet- und
Statement-Objekte schliessen.
Bei der SQLJ-Methode muss zuerst der Iterator für die Ergebnismenge definiert werden. Dies
sieht auf den ersten Blick nach zusätzlicher Arbeit aus, ist es aber nicht. Denn ein solcher
Iterator liefert intuitivere Zugriffsmethoden auf die einzelnen Ergebnisspalten, als diejenigen
von ResultSet. Auch hier wird keine execute-Methode verwendet. Zudem entfällt das Setzen
der Platzhalter und Zuweisen der (Host-)Variablen durch Methodenaufruf. Die HostAusdrücke können direkt in der SQL-Anweisung verwendet werden.
Das SQLJ-Verfahren ist wiederum kürzer und einfacher zu Bedienen.
3.4. Online-Überprüfung
Bei SQLJ besteht die Möglichkeit, SQL-Anweisungen zur Übersetzungszeit anhand des
Datenbankschemas zu prüfen. Dabei werden nicht schon die Daten benötigt, sondern das
Schema mit den richtigen Attributen und Datentypen genügt. Eine solche OnlineÜberprüfung kann folgendermassen aussehen:
sqlj –user=Jussi/seminar –url=jdbc:odbc:seminardb SeminarExample.sqlj
So werden SQL-spezifische Fehler schon zur Übersetzungszeit erkannt und können behoben
werden. JDBC (bzw. der Java-Compiler) bietet keine solchen Möglichkeiten.
14
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
3.5. Geschwindigkeit
Da SQLJ über JDBC mit einer Datenbank kommuniziert, kann es kaum schneller sein als
JDBC alleine.
In beiden Technologien besteht die Möglichkeit, Datenmanipulationsanweisungen zu
sammeln und in Form eines Stapels (Batch) an die Datenbank zu senden. Abfragen kann man
nicht in einem Stapel einbinden, da sie Ergebnismengen zurückliefern. Hat man z.B. 20 Tupel
einzufügen, so würde man diese in einem Stapeln sammeln und dann als eine Transaktion an
die Datenbank senden, anstatt alle 20 Anweisungen einzeln zu übermitteln. Die Vorteile
liegen auf der Hand.
4. Zusammenfassung
Programmierer, die in ihrer Anwendung eine Datenbankanbindung brauchen, benötigen aus
meiner Sicht JDBC und SQLJ. Dabei wird eine Anwendung geschaffen, die sowohl
plattformunabhängig wie auch DBMS-unabhängig ist. JDBC alleine würde genügen, weist
aber zwei Schwächen aus:
1. Der Quellcode wird lange und unübersichtlich, was Fehlersuche und Unterhalt
erschwert.
2. Fehler innerhalb von SQL-Anweisungen werden erst zur Laufzeit erkannt.
SQLJ löst beide Probleme folgendermassen:
1. SQLJ erlaubt die Verwendung von Hostausdrücken direkt in SQL-Anweisungen, was
den Code kürzer und einfacher macht.
2. Der SQLJ Translator überprüft die Syntax und Semantik von SQL-Anweisungen. Es
besteht die Möglichkeit die Anweisungen gegen das Datenbankschema zu prüfen,
wobei SQL-Fehler frühzeitig entdeckt und behoben werden können.
Voraussetzung für den Einsatz von SQLJ ist, dass die Datenbank schon vorhanden ist, oder
zumindest das Schema. Ansonsten wäre es nicht möglich eine Online-Überprüfung
vorzunehmen, oder sogar Abfragen vorzubereiten! Allgemein kann man sagen, dass JDBC
und SQLJ komplementäre Technologien sind, weil:
•
SQLJ benutzt zur Kommunikation mit der Datenbank JDBC.
•
JDBC wird für dynamisches SQL gebraucht, wo die Anweisungen erst zur Laufzeit
bekannt werden.
•
JDBC und SQLJ können miteinander eingesetzt werden.
15
Universität Zürich
Institut für Informatik
Sommersemester 2001
Seminar Datenbanktechnologie für das Web
Literaturverzeichnis
•
Estermann, Conny. SQLJ. Seminar Datenbanktechnologie für das Web, Universität
Zürich, Sommersemester 2001
•
Horstmann, Cay S. und Cornell, Gary. Core Java 2 – Advanced Features. Prentice
Hall, 1999
•
JDBC API Documentation. http://java.sun.com/j2se/1.3/docs/guide/jdbc
•
Khan, Salman & Kurian, Thomas & Wright, Brian. SQLJ: It’s Javalicious!.
http://technet.oracle.com/, 1999
•
Lagler, Severin. JDBC und JavaBlend. Seminar Datenbanktechnologie für das Web,
Universität Zürich, Sommersemester 2001
•
Rohwelder, Ekkehard. SQLJ: Tricks, Traps and Gems. http://technet.oracle.com/,
1999
•
Saake, Gunter und Sattler, Kai-Uwe. Datenbanken & Java: JDBC, SQLJ und ODMG.
dpunkt.verlag, 2000
•
SQLJ Konsortium. http://www.sqlj.org/
•
SQLJ Standards. http://otn.oracle.com/tech/java/sqlj_jdbc/htdocs/standards.html
•
Taylor, Blair und Woodger, James. SQLJ: An Easier Way to Access SQL Data. Java
Enterprise Developer online
(http://www.pinnaclepublishing.com/je/JEmag.nsf/0/8C6831AB722AAD8E85256981
006F42A0), November 2000
16
Herunterladen