149 13 3MB
German Pages 423 Year 2002
Java in Datenbanksystemen
Programmers Choice
Duan Petkovic´, Markus Brüderl
Java in Datenbanksystemen JDBC, SQLJ, Java DB-Systeme und -Objekte
An imprint of Pearson Education München • Boston • San Francisco • Harlow, England Don Mills, Ontario • Sydney • Mexico City Madrid • Amsterdam
Die Deutsche Bibliothek – CIP-Einheitsaufnahme Ein Titeldatensatz für diese Publikation ist bei Der Deutschen Bibliothek erhältlich. Die Informationen in diesem Produkt werden ohne Rücksicht auf einen eventuellen Patentschutz veröffentlicht. Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutzt. Bei der Zusammenstellung von Abbildungen und Texten wurde mit größter Sorgfalt vorgegangen. Trotzdem können Fehler nicht vollständig ausgeschlossen werden. Verlag, Herausgeber und Autoren können für fehlerhafte Angaben und deren Folgen weder eine juristische Verantwortung noch irgendeine Haftung übernehmen. Für Verbesserungsvorschläge und Hinweise auf Fehler sind Verlag und Herausgeber dankbar. Alle Rechte vorbehalten, auch die der fotomechanischen Wiedergabe und der Speicherung in elektronischen Medien. Die gewerbliche Nutzung der in diesem Produkt gezeigten Modelle und Arbeiten ist nicht zulässig. Fast alle Hardware- und Softwarebezeichnungen, die in diesem Buch erwähnt werden, sind gleichzeitig eingetragene Warenzeichen oder sollten als solche betrachtet werden. Umwelthinweis: Dieses Produkt wurde auf chlorfrei gebleichtem Papier gedruckt. Die Einschrumpffolie – zum Schutz vor Verschmutzung – ist aus umweltverträglichem und recyclingfähigem PE-Material.
5 05
4
3 04
2
1
03
02
ISBN 3-8273-1889-0
© 2002 by Addison-Wesley Verlag, ein Imprint der Pearson Education Deutschland GmbH, Martin-Kollar-Straße 10–12, D-81829 München/Germany Alle Rechte vorbehalten Einbandgestaltung: Christine Rechl, München Titelbild: Papaver orientale, Orientalischer Mohn. © Karl Blossfeldt Archiv – Ann und Jürgen Wilde, Zülpich/VG Bild-Kunst Bonn, 2001 Lektorat: Martin Asbach, [email protected] Korrektorat: Sibille Mozer-Petkovic, München Herstellung: Monika Weiher, [email protected] Satz: reemers publishing services gmbh, Krefeld, www.reemers.de Druck und Verarbeitung: Kösel, Kempten, www.Koeselbuch.de Printed in Germany
Inhalt
Vorwort
11
Teil I: Einführung
15
1
Relationale Datenbanken eine Einführung
17
1.1 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.2 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.5 1.6
Datenbanken – allgemein Logische und physikalische Datenunabhängigkeit Prozedurale und nichtprozedurale Schnittstellen Effiziente Abarbeitung der Datenbankoperationen Datenintegrität Konkurrierender Datenzugriff Datensicherheit Datenschutz Relationale Datenbanken Datenbankdesign Allgemeine Hinweise zur Normalisierung Erste Normalform Zweite Normalform Dritte Normalform Das Entity/Relationship-Modell Die Datenbanksprache SQL SQL-Anweisungen für Datendefinition SQL-Anweisungen für Datenmodifikation Sichten (Views) Der Systemkatalog und das Informationsschema Gespeicherte Prozeduren Datenbankanwendungen programmieren Zusammenfassung
17 18 18 18 19 19 20 20 20 23 24 25 25 26 26 29 29 31 34 35 37 39 39
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
41
2.1 2.1.1 2.1.2 2.1.3
Objektorientierte Erweiterungen bei Oracle Der Objekttyp Kollektionen Der Datentyp REF
41 42 45 50
6 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.3 2.4
Inhalt Objektorientierte Erweiterungen bei Informix Benutzerdefinierte Datentypen Der Reihentyp Kollektionen Typ- und Tabellenhierarchien Objektorientierte Erweiterungen bei DB2 Zusammenfassung
53 54 56 60 66 69 71
3
Die Sprache Java eine Einführung
73
3.1 3.2 3.2.1 3.3 3.4 3.4.1 3.4.2 3.4.3 3.5 3.5.1 3.5.2 3.5.3 3.6 3.6.1 3.6.2 3.6.3 3.6.4 3.7 3.7.1 3.7.2 3.8
Einleitung Variablen, Konstanten und Datentypen Vektoren (Arrays) und Strings Schleifenanweisungen Übersetzung und Ausführung eines Java-Programms Java-Pakete Java-Dateien Java-Umgebung und -Umgebungsvariablen Objektorientierte Sprachelemente bei Java Erstellung neuer Typen Sichtbarkeit Konstruktore Klassenhierarchien in Java Die Klasse Object Abstrakte Klassen und Methoden Schnittstellen (Interfaces) Polymorphismus in Java Weitere Eigenschaften der Java-Sprache Ausnahmen Ein- und Ausgabe von Daten Zusammenfassung
73 74 76 77 79 79 80 81 82 83 86 87 89 91 92 93 95 97 97 100 103
Teil II: JDBC
105
4
Was ist JDBC?
107
4.1 4.1.1 4.1.2 4.2 4.2.1 4.2.2 4.2.3
Einleitung JDK und JDBC-API Das JDBC-Treiberkonzept Der Aufbau eines JDBC-Programms Treiber laden Die Herstellung der Verbindung zu einer Datenbank Erstellung der SQL-Anweisungen und ihr Senden an einen Datenbank-Server Reihenweise Verarbeitung ResultSet-Erweiterungen Die Ausgabe der Ergebnisse Schließen der Verbindung zum Datenbank-Server Das Schlüsselwort ESCAPE JDBC und die Fehlerbehandlung
4.2.4 4.2.5 4.2.6 4.2.7 4.3 4.4
107 108 109 112 113 117 119 122 123 127 128 129 130
Inhalt 4.4.1 4.4.2 4.5 4.6 4.6.1 4.6.2 4.6.3 4.6.4 4.7 4.8 4.9 4.10 4.11 4.11.1 4.11.2 4.11.3 4.12 4.13
7 Die Klasse SQLException Die Klasse SQLWarning Übersetzung und Ausführung eines JDBC-Programms Zur Übersetzungszeit formulierte SQL-Anweisungen Die Verwendung der Methoden der ResultSet-Schnittstelle JDBC und Metadaten Scroll-Cursor Positionierte Änderung (Positioned update) JDBC und vorbereitete SQL-Anweisungen Die Klasse CallableStatement und gespeicherte Prozeduren Applet-Programmierung Große Objekte und JDBC JDBC und Transaktionen Einleitung Die JDBC-Methoden commit() und rollback() Isolierungsstufen Typumwandlung zwischen SQL und Java Zusammenfassung
5
JDBC und objektorientierte Erweiterungen
5.1 5.1.1 5.1.2 5.1.3 5.2 5.2.1 5.2.2 5.3 5.4
JDBC und objektorientierte Erweiterungen bei Oracle Der Objekttyp und der Datentyp REF VARRAY-Datentyp bei Oracle Geschachtelte Tabellen JDBC und objektorientierte Erweiterungen bei INFORMIX Der Reihentyp und Kollektionen bei Informix Informix-Typhierarchien JDBC und objektorientierte Erweiterungen bei DB2 Zusammenfassung
6
Performance-Aspekte bei JDBC
6.1 6.1.1 6.1.2 6.2 6.3 6.4
Performance – eine Einleitung Die Effizienz des Benutzer-Codes Das Datenbankmanagementsystem Die Durchführung von Performance-Tests Die Optimierung der Datenbankzugriffe Bearbeitung von vorbereiteten und nichtvorbereiteten SQL-Anweisungen Scroll- und Nicht-Scroll-Cursor Ausführung der SQL-Anweisungen mittels batch Explizite und implizite Transaktionen Statement-Objekte Untersuchte Fälle ohne Performance-Unterschiede Die Methode setFetchDirection() Die Methode setFetchSize() Zusammenfassung
6.5 6.6 6.7 6.8 6.9 6.9.1 6.9.2 6.10
130 131 132 133 141 143 148 150 154 159 162 167 173 173 174 177 178 179
181 181 181 188 193 198 198 205 212 212
215 215 215 216 216 217 221 224 229 232 236 239 239 240 241
8
Inhalt
Teil III: SQLJ 7
Was ist SQLJ?
7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.2 7.2.1 7.2.2 7.2.3 7.2.4 7.2.5 7.2.6 7.2.7 7.2.8 7.2.9 7.2.10 7.2.11 7.3
Einbettung der SQL-Sprache – eine Einführung Kennzeichnung der SQL-Anweisungen Host-Variablen Eingebettete SQL-Anweisungen und der Cursor Die dynamische SQL-Sprache SQLJ – die Grundlagen Vorteile von SQLJ im Vergleich zu JDBC SQLJ-Anweisungen SQLJ-Host-Ausdrücke Fehlerbehandlung bei SQLJ Übersetzung und Ausführung eines SQLJ-Programms Eingebettete SQL-Anweisungen ohne Ergebnismenge Die Einbettung einer SELECT-Anweisung ohne Iterator Die Einbettung der SELECT-Anweisung mit Iterator Verwendung eines Iterators mit der UPDATE- bzw. DELETE-Anweisung Gespeicherte Prozeduren und SQLJ Große Objekte in SQLJ Zusammenfassung
8
SQLJ und objektorientierte Erweiterungen
8.1 8.1.1 8.1.2 8.1.3 8.2 8.2.1 8.2.2 8.3
SQLJ und objektorientierte Erweiterungen bei Oracle Oracle-REF-Zugriff und Objekttyp-Änderung VARRAY-Datentyp bei Oracle Geschachtelte Tabelle mit Oracle SQLJ und objektorientierte Erweiterungen bei Informix Der Reihentyp und Kollektionen bei Informix Informix-Typhierarchien Zusammenfassung
9
Performance-Aspekte bei SQLJ
9.1 9.2 9.3 9.4 9.5 9.6
Die Optimierung der Datenbankzugriffe Blockweise Bearbeitung von SQL-Anweisungen bei Oracle Verwendung des Datentyps REF bei Oracle Ausführen von SQL-Anweisungen vs. gespeicherte Prozeduren Row-Prefetching bei Oracle Zusammenfassung
Teil IV: Serverseitige Java-Programmierung 10
Java-Programmierung der serverseitigen Datenbankobjekte
10.1 Entwicklung von gespeicherten Java-Programmen mit Oracle 10.1.1 Ein Quellprogramm übersetzen 10.1.2 Die Klassen in ein Archiv zusammenfassen
243 245 245 246 246 247 248 248 251 252 253 255 255 257 259 262 270 273 277 288
289 289 289 296 300 304 305 311 315
317 317 320 324 328 331 335
337 339 340 341 344
Inhalt 10.1.3 10.1.4 10.1.5 10.1.6 10.1.7 10.1.8 10.2 10.2.1 10.2.2 10.2.3 10.2.4 10.2.5 10.2.6 10.3
9 Vorher registrierte Objekte entfernen Klassen im Datenbank-Server registrieren Testen, ob die Registrierung erfolgreich war Erstellen eines Wrappers mit der CREATE FUNCTION-Anweisung Aufrufen eines gespeicherten Programms Erstellung eines Triggers mit Hilfe von Java Entwicklung von gespeicherten Java-Programmen mit DB2 Implementierung des gespeicherten Programms Speichern der Klassen in einem Archiv und Übertragung auf den Server Aktualisieren des gespeicherten Programms Erstellung eines Wrappers Aufruf des gespeicherten Programms Gespeicherte Programme und SQLJ Zusammenfassung
Teil V: Java-Datenbanksysteme 11
Cloudscape
11.1 11.2 11.3 11.3.1 11.3.2 11.3.3 11.4 11.4.1 11.4.2 11.5 11.6 11.7
Entwicklungsgeschichte von Cloudscape Cloudscape als objektrelationales DBS Cloudscape-Komponenten Cloudview Cloudsync Cloudconnector Cloudscape-Modelle Die Einbettung von Cloudscape in eine Anwendung Cloudscape im Client/Server-Modus Cloudscape-Archive (Bibliotheken) Anwendungsmöglichkeiten von Cloudscape Zusammenfassung
12
PointBase
12.1 12.1.1 12.2 12.2.1 12.2.2 12.2.3 12.3 12.3.1 12.3.2 12.3.3 12.3.4 12.4 12.5
Entwicklungsgeschichte von PointBase PointBase als Datenbanksystem PointBase-Editionen PointBase Micro PointBase Embedded Server-Edition PointBase-Komponenten UniSync PointBase Server PointBase Console PointBase Commander PointBase-Bibliotheken Anwendungsmöglichkeiten von PointBase
344 345 345 346 348 355 360 361 362 363 363 364 369 374
375 377 377 379 383 384 385 386 387 388 389 391 391 398
401 401 401 403 403 404 404 406 406 406 407 408 408 409
10
Inhalt
12.5.1 Erstellung einer Datenbank 12.5.2 PointBase-Beispiel 12.6 Zusammenfassung
13
409 409 415
Literatur
417
Index
419
Vorwort Relatíonale Datenbanksysteme sind seit mehr als 25 Jahren mit Abstand die wichtigsten Datenbanksysteme, die in der Praxis angewendet werden. Die Gründe für die Etablierung dieser Systeme sind mannigfaltig: die Verwendung der standardisierten Datenbanksprache (SQL), die hohe Portierbarkeit der Anwendungen usw. Der Siegeszug der relationalen Datenbanksysteme hat ziemlich lange gedauert, weil sie ursprünglich sehr viele Kinderkrankheiten gehabt haben. (Der größte Nachteil dieser Systeme war die schlechte Performance der Anwendungen, was im Laufe der Zeit durch die Verfeinerung der Optimiererungstechniken ausgemerzt wurde.) Auf der anderen Seite gibt es die Sprache Java, die erst seit einigen Jahren existiert und in dieser Zeit einen Durchbruch als Programmiersprache für viele sehr unterschiedliche Anwendungen erlebt hat. Java hat viele herausragende Eigenschaften, von denen die Rechnerunabhängigkeit und Objektorientiertheit wahrscheinlich die wichtigsten sind. (Die Rechnerunabhängigkeit ermöglicht die Ausführung von Code auf entfernten Rechnern, was die Grundlage für die Verwendung dieser Sprache im Internet und jeder verteilten Umgebung ist.)
erstellt von ciando
Obwohl diese beiden Welten verschiedene Ursprünge und Geschichten haben, koexistieren sie heute ausgezeichnet. Es gibt insgesamt vier Bereiche, wo sie gemeinsam angewendet werden können: 1. die JDBC-Unterstützung 2. die SQLJ-Unterstützung 3. die Implementierung der gespeicherten Prozeduren und Trigger mit Hilfe von Java 4. in Java implementierte Datenbanksysteme. Den Anfangspunkt aller in Java geschriebenen Datenbankanwendungen stellt JDBC dar. Diese Schnittstelle kann mit Hilfe eines Treibers zu allen Zielsystemen verbunden werden, die SQL-Anweisungen unterstützen. Abhängig vom Treiber (es gibt, genauso wie bei ODBC gute und weniger gute Treiber) ist es möglich sehr aufwendige und komplexe Datenbankanwendungen zu schreiben.
12
Vorwort
Eine zweite Java-Schnittstelle für die Datenbankprogrammierung bietet SQLJ. Obwohl SQLJ eine signifikante Einschränkung gegenüber JDBC hat (mit dieser Schnittstelle ist es nur möglich SQL-Anweisungen statisch d.h. zur Übersetzungszeit zu implementieren), wird sie sehr oft in der Praxis verwendet. Die Gründe für die Beliebtheit dieser Programmierschnittstelle liegen sowohl in ihrer Standardisierung (durch das ISO- und ANSI-Gremium) als auch in der Existenz eines gemeinsamen Vorübersetzers für alle Datenbanksysteme. In der Welt der Programmierung der gespeicherten Prozeduren und Trigger, wo jedes Datenbanksystem eine herstellerspezifische Sprache verwendet, verspricht Java eine Standardisierung des Implementierungsverfahrens. Weil bei den meisten großen Unternehmen eine Mischung der Datenbanksysteme verschiedener Hersteller existiert, wird die Verwendung von nur zwei Sprachen – Java und SQL – sicher von Vorteil sein. Schließlich haben in Java geschriebene Datenbanksysteme einige Vorteile, die die verwandten Systeme nicht haben. Dazu zählen sowohl die geringe Speichergröße, die für ihr Laden notwendig ist, als auch ihre Plattformunabhängigkeit. Das PointBase-Datenbanksystem braucht z.B. bei der minimalen Ausprägung nur 64 KB Speicherplatz zum Laden, was dieses System als Wahldatenbanksystem für alle Arten mobiler Rechner prädestiniert. Dieses Buch führt den Leser genau in die vier oben genannten Bereiche ein. Dementsprechend ist das Buch in fünf Teile unterteilt: 1. Einführung 2. JDBC 3. SQLJ 4. Serverseitige Java-Programmierung 5. Java-Datenbanksysteme. Der erste Teil des Buches umfasst Kapitel 1 bis 3 und dient als Einführung sowohl in Datenbanksysteme als auch in Java. Kapitel 1 beschreibt skizzenhaft die wichtigsten Konzepte der relationalen Datenbanksysteme und führt die Beispieldatenbank ein. Kapitel 2 stellt eine Fortsetzung des ersten Kapitels dar, wo die objektorientierten Erweiterungen der im Buch verwendeten relationalen Datenbanksysteme beschrieben sind. (Weil jedes Datenbanksystem verschiedene objektorientierte Konzepte unterstützt bzw. die identischen Konzepte unterschiedlich implementiert sind, ist dieses Kapitel in drei Abschnitte (für Oracle, Informix und IBM DB2) unterteilt.) In Kapitel 3 werden diejenigen Eigenschaften der Java-Sprache, die für das weitere Verständnis der Buchinhalte notwendig sind, erörtert.
Vorwort
13
Der zweite Teil des Buches behandelt JDBC. Dieser Teil hat drei Kapitel. Das erste Kapitel dieses Teils – Kapitel 4 – erörtert JDBC als Programmierschnittstelle für Datenbankanwendungen. In Kapitel 5 werden objektorientierte Erweiterungen einzelner Datenbanksysteme (Oracle und Informix) in separaten Abschnitten beschrieben und durch Beispiele vertieft. Das letzte Kapitel dieses Teils ist der Performance der Datenbankanwendungen, die in JDBC implementiert sind, gewidmet. Der Aufbau des dritten Teils des Buches entspricht genau dem Aufbau des zweiten, bis auf die Tatsache, dass dieser Teil der Programmierung mit SQLJ gewidmet ist. Kapitel 7 gibt allgemeine Hinweise auf die Datenbankprogrammierung mit SQLJ, während Kapitel 8 die objektorientierten Erweiterungen in Bezug auf SQLJ aufzeigt. Das letzte Kapitel dieses Teils – Kapitel 9 – behandelt die Performance-Aspekte von SQLJ. Der vierte Teil des Buches ist der Entwicklung der Datenbankobjekte (wie gespeicherte Prozeduren, benutzerdefinierte Funktionen und Trigger) gewidmet. Java soll in diesem Bereich der Vereinheitlichung der Implementierung der o.g. Objekte, die auf dem Datenbank-Server gespeichert werden, dienen. Obwohl dieses Ziel noch immer nicht erreicht ist (was auch die Unterteilung des Kapitels in separate Abschnitte für verschiedene Datenbanksysteme zeigt), gibt es berechtigte Hoffnung, dass eines Tages solche Objekte mühelos von einem Datenbanksystem zu einem anderen portiert werden können. Der letzte Teil des Buches beschreibt zwei auf dem Markt befindliche Datenbanksysteme, die komplett in Java geschrieben sind. Kapitel 11 beschreibt Cloudscape, während Kapitel 12 das PointBase-Sytem erörtert. (Die beiden Kapitel haben denselben Aufbau.) Das Buch ist als praktisches Lehrbuch gedacht. Diesem Ziel dienen sowohl zahlreiche Beispiele als auch eine sehr kleine Datenbank. Dem Leser wird empfohlen, die Beispieldatenbank mit dem auf unserer Internet-Seite zur Verfügung stehenden SQLJbzw. JDBC-Quellprogrammen zu erstellen und danach alle angegebenen Beispiele nachzuvollziehen. Alle im Buch befindlichen Programme können auch von der mitgelieferten CD-ROM geladen werden. Außerdem befinden sich auf der CD-ROM einige hilfreiche Tools, die Sharewareprogramme UltraEdit, Jeevan und MM.MySQL. Ein großer Vorteil der in diesem Buch existierenden SQLJ- und JDBC-Programme ist, dass sie mit vier etablierten Datenbanksystemen (Oracle, MySQL, IBM DB2 und Informix) ablauffähig sind. Zu diesem Zweck haben wir ein Dienstprogramm namens DBUtils erstellt, um die Zugriffe zu den o.g. Datenbanksystemen zu ermöglichen. Durch eine einfache Spezifikation des entsprechenden Datenbanksystems in jedem Programm (z.B. DBUtils.ORACLE) ist es möglich, jedes Programm für das gewünschte Zieldatenbanksystem ablauffähig zu machen. Weil verschiedene Systeme unterschiedliche Funktionalitäten in Bezug auf JDBC und SQLJ aufweisen, gibt es einzelne Programme, die nicht mit allen vier erwähnten Systemen verwendet werden können.
14
Vorwort
Dieses Buch wendet sich primär an alle Datenbankprogrammierer, die mit einer fortschrittlichen Programmiersprache wie Java Anwendungen für Datenbanksysteme implementieren wollen. Weil das Buch aus der Vorlesung »Java in Datenbanksystemen« an der Fachhochschule Rosenheim entstanden ist, kann es genauso gut als Lehrbuch verwendet werden. Vom Leser werden keine tieferen Kenntnisse der relationalen Datenbanksysteme und der Sprache Java erwartet; sie werden aber sicher vom Vorteil beim Lesen des Buches sein. Allen Lesern, die wenig Kenntnisse dieser beiden Säulen des Buches haben, empfehlen wir das genaue Studium der Kapitel 1 und 3. An dieser Stelle möchten wir uns bei allen, die zur Entstehung dieses Buches einen Beitrag geleistet haben, herzlich bedanken. Namentlich gilt unser Dank Herrn Martin Asbach von Addison-Wesley für die optimale Betreuung bei der Erstellung des Manuskripts. Alle Programme, die in diesem Buch aufgelistet sind sowie die SQLJ- bzw. JDBC-Programme für die Erstellung der Beispieldatenbank können auch auf unseren InternetSeiten: www.fh-rosenheim.de/~petkovic und www.ravedesign.de
gefunden und geholt werden. Kommentare und Kritiken von Lesern dieses Buches sind willkommen. Bitte schicken Sie sie an: [email protected] oder [email protected].
Teil I: Einführung
1 Relationale Datenbanken eine Einführung In diesem Kapitel werden die wichtigsten Datenbankbankeigenschaften beschrieben. Der erste Teil des Kapitels erörtert die Funktionalität eines Datenbankmanagementsystems. Danach wird das relationale Modell dargestellt und die in den Beispielen des Buches verwendete Datenbank erläutert. Nach einer kurzen Beschreibung des Datenbankentwurfs werden die wichtigsten Anweisungen der SQL-Sprache durch die Beispiele gezeigt. Am Ende des Kapitels werden die unterschiedlichen Möglichkeiten gezeigt, wie eine Datenbankanwendung programmiert werden kann.
1.1 Datenbanken allgemein Eine Datenbank kann aus verschiedenen Blickwinkeln betrachtet werden, abhängig davon, in welchem Zusammenhang sie gesehen wird. Aus der Sicht eines Managers ist die Datenbank eine Sammlung logisch zusammenhängender Daten, die ein Modell der Aktivitäten seines Unternehmens darstellen. Aus der Sicht eines Datenbankmanagementsystems ist die Datenbank eine Sammlung von physikalischen Daten. Obwohl beide Betrachtungsweisen diametral verschieden sind, haben sie etwas gemeinsam: Sie erfordern Funktionen, mit denen eine Datenbank abgefragt und modifiziert werden kann sowie Schnittstellen, die maschinenunabhängig sind. Alle diese Funktionen und Schnittstellen soll ein Datenbankmanagementsystem bieten. Zusammenfassend soll ein Datenbankmanagementsystem folgendes gewährleisten: - logische und physikalische Datenunabhängigkeit - prozedurale und nichtprozedurale Schnittstellen - effiziente Abarbeitung von Datenbankoperationen - Datenintegrität - konkurrierender Datenzugriff - Datensicherheit - Datenschutz.
18
1
Relationale Datenbanken eine Einführung
Jede dieser Grundeigenschaften eines Datenbankmanagementsystems soll nachfolgend erläutert werden.
1.1.1 Logische und physikalische Datenunabhängigkeit Die logische Datenunabhängigkeit bezieht sich auf unterschiedliche logische Sichten einer Datenbank. Einerseits existiert die logische Struktur einer Datenbank mit allen dazugehörigen Objekten und Zusammenhängen, die zwischen diesen Objekten bestehen; andererseits sieht jeder Benutzer, der eine Datenbankanwendung programmiert oder benutzt, nur den für ihn relevanten Ausschnitt der gesamten logischen Struktur. Die logische Unabhängigkeit bedeutet, dass jeder Benutzer seine Sicht der Datenbank erzeugen und modifizieren kann, ohne dass die logische Gesamtstruktur geändert werden müsste. Unter der physikalischen Datenunabhängigkeit versteht man die Unabhängigkeit zwischen logischer und physikalischer Struktur einer Datenbank. Die physikalische Datenunabhängigkeit garantiert, dass die physikalische Struktur einer Datenbank beliebig geändert werden kann, ohne dass dadurch die logische Struktur berührt wird.
1.1.2 Prozedurale und nichtprozedurale Schnittstellen Bei Datenbankmanagementsystemen existieren u.a. zwei Arten von Benutzern, nämlich der Programmierer und der Endbenutzer. Die Aufgabe eines Programmierers ist es, Programme zu schreiben, mit denen eine Datenbank abgefragt oder modifiziert werden kann. Endbenutzer sind in der Regel keine DV-Fachleute. Sie greifen auf die Datenbank über eine leicht erlernbare Kommandosprache zu. Falls auf der Ebene dieser Kommandosprache Kontrollstrukturen angeboten werden, wie z.B. IF bedingung THEN wahr_zweig ELSE falsch_zweig
spricht man von einer prozeduralen, ansonsten von einer nichtprozeduralen Schnittstelle. Ein Programmierer hat in den meisten Fällen weitaus komplexere Aufgabenstellungen zu erledigen als ein Endbenutzer und benötigt daher nahezu immer Programmiersprachen mit einem umfangreichen Spektrum an Kontrollstrukturen. Dementsprechend sind alle bekannteren Datenbankschnittstellen für Programmierer prozedurale Schnittstellen.
1.1.3 Effiziente Abarbeitung der Datenbankoperationen Mit der Verwendung mächtiger Datenbankprogrammiersprachen wird die Entwicklungszeit von Datenbankanwendungen in der Regel reduziert, oft allerdings auf Kosten
Datenbanken allgemein
19
von zusätzlichen E/A-Operationen und längerer Verarbeitungszeit. Das Datenbankmanagementsystem sollte daher für die Abarbeitung der jeweiligen Datenbankoperation eine optimale Strategie entwickeln können.
1.1.4 Datenintegrität Ein Datenbankmanagementsystem sollte offensichtlich unsinnige Daten erkennen und abweisen können. (Das Datum 30. Februar oder die Uhrzeit 17:77:00 sind typische Beispiele.) Des weiteren ist es wünschenswert, gegenüber dem Datenbankmanagementsystem Begrenzungen oder Formatangaben zu Eingabedaten deklarieren zu können, z.B.: Jahrgang > 1959 Ausgereifte Datenbankmanagementsysteme stellen außerordentlich komplexe Mechanismen zur Plausibilitätsprüfung von Eingabedaten zur Verfügung. Dazu gehört insbesondere die Fähigkeit, bereits in der Datenbank vorhandene Daten in die Plausibilitätsprüfung mit einzubeziehen.
1.1.5 Konkurrierender Datenzugriff Der Umstand, dass im Regelfall viele Benutzer gleichzeitig auf eine Datenbank zugreifen, wirft eine Reihe von Problemen auf. Ein besonders gravierendes Problem wird durch das folgende Beispiel erläutert: 1. Auf dem Konto 4711 der Bank X befinden sich 2.000,- DM. 2. Die Kunden A und B gehen in zwei verschiedene Filialen der Bank X und heben gleichzeitig 1.000,- DM vom Konto 4711 ab. 3. Die Kassierer in beiden Bankfilialen bekommen vom Datenbanksystem den Kontostand 2.000,- DM gezeigt. 4. Beide Kassierer zahlen jeweils 1.000,- DM aus und ändern das Konto 4711 mit dem Wert 2.000,- DM minus 1.000,- DM gleich 1.000,- DM. 5. Es ist nun unerheblich, in welcher Reihenfolge diese beiden Änderungen ausgeführt werden; das Konto 4711 steht auf 1000,- DM statt auf 0,- DM. Der ganze Problemkreis des konkurrierenden Datenzugriffs, der hier aufgezeigt wurde, muss von einem Datenbankmanagementsystem natürlich mit völliger Korrektheit abgehandelt werden. Bezogen auf das vorangehende Beispiel bedeutet dies, dass ein Datenbankmanagementsystem den Kontostand von 0 DM garantieren muss, nachdem den beiden Kunden je 1.000,- DM ausgezahlt wurden.
20
1
Relationale Datenbanken eine Einführung
1.1.6 Datensicherheit Der Begriff der Datensicherheit bezieht sich auf den Ausfall von Hardware und/oder Software. Ein Datenbankmanagementsystem sollte in der Lage sein, nach denkbaren Ausfällen die betroffenen Datenbanken automatisch in den letzten konsistenten Stand zu überführen.
1.1.7 Datenschutz Eine Datenbank sollte gegen unerlaubten Zugriff geschützt werden können. Wenn z.B. eine Datenbank die Gehälter der Mitarbeiter einer Firma enthält, ist es dringend notwendig, den Zugriff auf diese Daten nur ausgewählten Benutzern zu ermöglichen. Dasselbe gilt für die sicherheitsrelevanten Daten eines Unternehmens. Entsprechende Möglichkeiten, wie Vergabe und Entzug der Zugriffsrechte sollte jedes Datenbankmanagementsystem unterstützen.
1.2 Relationale Datenbanken Der Begriff der relationalen Datenbanken wurde 1970 von E.F.Codd eingeführt. In dem Artikel »A Relational Model of Data for Large Shared Data Banks« wurde die theoretische Grundlage für relationale Datenbanken festgelegt: das so genannte relationale Datenmodell. Im Unterschied zu anderen Datenbankmanagementsystemen (netzwerkartigen bzw. hierarchischen Systemen) basiert das relationale Modell völlig auf den mathematischen Grundlagen der relationalen Algebra. Eine Erklärung der relationalen Algebra liegt außerhalb der Ziele dieses Buches. Wir werden die wichtigsten Eigenschaften des relationalen Modells mit Hilfe einer Beispieldatenbank erklären. Weiter dient die Beispieldatenbank als Grundlage für alle praktischen Beispiele innerhalb dieses Buches. Das Grundelement einer relationalen Datenbank ist die Tabelle. Aus der Benutzersicht besteht jede relationale Datenbank nur aus Tabellen. Eine Tabelle setzt sich aus Reihen und Spalten zusammen, d.h. sie beinhaltet keine, eine oder mehrere Reihen und eine oder mehrere Spalten. Das Objekt, das genau zu einer Reihe und einer Spalte gehört, heißt Datenwert oder Datum. Die Beispieldatenbank, die in diesem Buch verwendet wird, enthält die Datenwerte einer Firma, die in mehrere Abteilungen unterteilt ist. Jeder Mitarbeiter der Firma gehört einer der existierenden Abteilungen an. Die Eigenschaft unserer Firma ist, dass die Mitarbeiter ihre Tätigkeiten in verschiedenen Projekten ausüben. Jeder Mitarbeiter kann in verschiedenen Projekten arbeiten und dabei unterschiedliche Aufgaben wahrnehmen.
Relationale Datenbanken
21
Die Datenbank besteht aus vier Tabellen: - abteilung, - mitarbeiter, - projekt und - arbeiten. Die Tabelle abteilung stellt alle Abteilungen der Firma dar. Jede Abteilung ist auf folgende Weise beschrieben: abteilung (abt_nr,abt_name,stadt)
abt_nr ist die für jede Abteilung der Firma eindeutige Abteilungsnummer. abt_name steht für den Namen der Abteilung; stadt für die Stadt, in der sich diese Abteilung befindet. Die Tabelle mitarbeiter beinhaltet alle Mitarbeiter der Firma. Jeder Mitarbeiter ist auf folgende Weise beschrieben: mitarbeiter (m_nr,m_name,m_vorname,abt_nr)
m_nr kennzeichnet die für jeden Mitarbeiter eindeutige Personalnummer. m_name und m_vorname kennzeichnen Namen und Vornamen des Mitarbeiters, während abt_nr die Nummer der Abteilung benennt, welcher der Mitarbeiter angehört. Die Tabelle projekt stellt alle Projekte der Firma dar. Jedes Projekt ist dabei auf folgende Weise beschrieben: projekt (pr_nr,pr_name,mittel)
pr_nr bezeichnet die innerhalb der Firma eindeutige Nummer des Projektes. pr_name und mittel kennzeichnen den Namen des Projektes bzw. die Geldmittel, die für das Projekt zur Verfügung stehen. Die Geldmittel sind in DM angegeben. Die Tabelle arbeiten beschreibt die Beziehung zwischen den Mitarbeitern und den Projekten. Diese Tabelle ist auf folgende Weise beschrieben: arbeiten (m_nr,pr_nr,aufgabe,einst_dat)
m_nr zeigt die Personalnummer des Mitarbeiters und pr_nr die Nummer des Projektes, in dem der Mitarbeiter arbeitet, an. Die Kombination aus m_nr und pr_nr ist innerhalb der Firma eindeutig. aufgabe beschreibt die Funktion des Mitarbeiters (mit der Personalnummer m_nr) innerhalb des Projektes (mit der Nummer pr_nr). einst_dat kennzeichnet das Eintrittsdatum des Mitarbeiters in das Projekt. Die relationale Datenbank für das soeben beschriebene Schema ist in den Tabellen 1.1 bis 1.4 dargestellt.
22
1
Relationale Datenbanken eine Einführung
abt_nr
abt_name
stadt
a1 a2 a3
Beratung Diagnose Freigabe
München München Stuttgart
Tabelle 1.1: Tabelle abteilung m_nr
M_name
m_vorname
abt_nr
25348 10102 18316 29346 9031 2581 28559
Keller Huber Müller Probst Meier Kaufmann Mozer
Hans Petra Gabriele Andreas Rainer Brigitte Sibille
a3 a3 a1 a2 a2 a2 a1
Tabelle 1.2: Tabelle mitarbeiter pr_nr
pr_name
mittel
p1 p2 p3
Apollo Gemini Merkur
120000.0 95000.0 186500.0
Tabelle 1.3: Tabelle projekt m_nr
pr_nr
aufgabe
einst_dat
10102 10102 25348 18316 29346 2581 9031 28559 28559 9031 29346
p1 p3 p2 p2 p2 p3 p1 p1 p2 p3 p1
Projektleiter Gruppenleiter Sachbearbeiter
1.Okt.1988 1.Jan.1989 15.Feb.1988 1.Jun.1989 15.Dez.1987 15.Okt.1989 15.Apr.1989 1.Aug.1988 1.Feb.1989 15.Nov.1988 1.Apr.1989
Projektleiter Gruppenleiter Sachbearbeiter Sachbearbeiter Sachbearbeiter
Tabelle 1.4: Tabelle arbeiten
Mit Hilfe unseres Beispiels können wir jetzt einige wichtige Eigenschaften des relationalen Modells erklären: - Die Reihen innerhalb einer Tabelle können eine beliebige Reihenfolge haben. - Die Spalten innerhalb einer Tabelle können eine beliebige Reihenfolge haben.
Datenbankdesign
23
- Alle Datenwerte einer Spalte haben genau denselben Datentyp. - Jede Spalte hat einen eindeutigen Namen innerhalb einer Tabelle. Spalten, die verschiedenen Tabellen angehören, können durchaus denselben Namen haben. (Beispiel: Die Spalte m_nr in der Tabelle arbeiten und die Spalte m_nr in der Tabelle mitarbeiter.) - Jeder einzelne Datenwert innerhalb einer Tabelle ist durch einen einzigen Wert dargestellt. Das heißt: In einer Reihe und innerhalb einer Spalte können sich nie mehrere Werte gleichzeitig befinden. - In jeder Tabelle einer relationalen Datenbank existiert ein (oder mehrere) Bezeichner, der jede Reihe der Tabelle eindeutig definiert. Dieser Bezeichner kann entweder aus einer Spalte oder aus einer Kombination mehrerer Spalten bestehen. Im relationalen Modell heißt dieser Bezeichner Primärschlüssel. Die Spalte abt_nr ist der Primärschlüssel in der Tabelle abteilung; m_nr ist der Primärschlüssel in der Tabelle mitarbeiter; pr_nr ist der Primärschlüssel in der Tabelle projekt und die Kombination der Spalten (m_nr, pr_nr) ist der Primärschlüssel in der Tabelle arbeiten. (Die Primärschlüssel der Beispieldatenbank sind grau dargestellt.) - In einer Tabelle existieren nie zwei identische Reihen. (Diese Eigenschaft wird von allen relationalen Datenbankmanagementsystemen nicht unterstützt.) Hinweis
In der Terminologie relationaler Datenbanken existieren mehrere analoge Begriffe. So entsprechen die mathematischen Begriffe Relation, Tupel und Attribut in der Praxis jeweils den Begriffen Tabelle, Reihe und Spalte. Zusätzlich existieren in der Praxis weitere Begriffe wie Satz oder Record (für Reihe), Feld (für Spalte) usw. In diesem Buch werden nur die Begriffe benutzt, die im SQL-Standard verwendet werden, also Tabelle, Reihe und Spalte.
1.3 Datenbankdesign Das Datenbankdesign ist eine sehr wichtige Phase, die der Erstellung einer Datenbank vorangeht. Falls das Datenbankdesign intuitiv und ohne sorgfältige Analysephase entworfen wird, ist die daraus resultierende Datenbank in den meisten Fällen nicht optimal an die Aufgabe, zu deren Lösung sie aufgebaut wurde, angepasst. Die daraus resultierende Folge kann überflüssige Datenredundanz mit damit verbundenen Nachteilen für Speicherverbrauch und Datenkonsistenz sein. Die Normalisierung der Daten stellt ein Verfahren dar, in dem die Datenredundanz auf der Basis der funktionalen Abhängigkeiten stufenweise reduziert werden kann. Die funktionale Abhängigkeit gilt dann innerhalb einer Tabelle zwischen zwei Spalten (bzw. Spaltengruppen), wenn in jeder Tabellenreihe der Wert einer Spalte (Spalten-
24
1
Relationale Datenbanken eine Einführung
gruppe) eindeutig durch den entsprechenden Wert der anderen Spalte (Spaltengruppe) festgelegt wird. Die funktionale Abhängigkeit wird durch einen Pfeil (AB: »B ist funktional abhängig von A«) gekennzeichnet. Beispiel 1.1 In der Tabelle mitarbeiter der Beispieldatenbank gilt: m_nr m_name
weil jeder Mitarbeitername eindeutig durch die entsprechende Mitarbeiternummer identifiziert werden kann. Eine zweite Art der Abhängigkeit, die auch eine Rolle bei den Normalformen spielt, ist die mehrwertige Abhängigkeit. Die mehrwertige Abhängigkeit besagt, dass auf Grund eines Spaltenwertes immer die entsprechende Menge von Werten anderer Spalten eindeutig festgelegt werden kann. Diese Art von Abhängigkeit zwischen Tabellenspalten wird durch einen Doppelpfeil »» gekennzeichnet. Beispiel 1.2 ISBN Autoren
Das Attribut Autoren ist mehrwertig von dem ISBN-Code jedes Buches abhängig, weil wir auf Grund des Codes alle Autoren jedes Buches eindeutig festlegen können. Insgesamt existieren fünf Normalformen, von welchen wir die ersten drei erläutern werden. Die vierte und fünfte Normalform haben keine bzw. sehr geringe praktische Bedeutung. Jede Normalform ist in der nachfolgenden enthalten.
1.3.1 Allgemeine Hinweise zur Normalisierung Der Prozess der Normalisierung einer Datenbank sollte immer mit der ersten Normalform beginnen. Nachdem die Datenbank die erste Normalform erfüllt, sollten die Tabellen der Datenbank so zerlegt werden, dass sie die zweite Normalform erfüllen usw. Für die meisten Datenbanken genügt die Normalisierung bis einschließlich der dritten Normalform. Die vierte und insbesondere die fünfte Normalform finden in der Praxis selten Anwendung. Die im Zusammenhang mit der Normalisierung entscheidende Frage ist, wie viel Datenredundanz sinnvoll ist. Diese Frage kann nur für jede Datenbank separat beantwortet werden. Das wichtigste Kriterium für diese Entscheidung ist, ob die Datenbank wenigen oder vielen Änderungen unterworfen ist. Die Datenbanken, die wenigen oder keinen Änderungen unterworfen sind, können problemlos mehr Datenredundanz enthalten. Demgegenüber sollten die Datenbanken, die häufig geändert werden, möglichst wenig redundante Daten haben, weil das Ziel, die redundanten Daten konsistent zu halten, im Allgemeinen nur mit hohem Aufwand zu erreichen ist.
Datenbankdesign
25
1.3.2 Erste Normalform Eine Tabelle befindet sich in der ersten Normalform, falls in jeder Reihe und für jede Spalte nur atomare Werte existieren. Wie aus dem vorherigen Abschnitt ersichtlich, beinhaltet das relationale Datenmodell an sich schon diese Eigenschaft. Die erste Normalform werden wir anhand eines Ausschnitts aus der Tabelle arbeiten der Beispieldatenbank darstellen: 10102 10102 ..........
p1 p3 ..........
....... ....... .......
Falls diese zwei Reihen folgendermaßen geschrieben würden: 10102 ..........
(p1,p3) ..........
........ ........
würde die Tabelle arbeiten nicht in der ersten Normalform sein. (Dies ist im relationalen Datenmodell nicht möglich.)
1.3.3 Zweite Normalform Eine Tabelle befindet sich in der zweiten Normalform, wenn sie keine funktionalen Abhängigkeiten von Teilen des Schlüssels enthält. Mit anderen Worten, jede Spalte dieser Tabelle, die den Primärschlüssel nicht bildet, darf nur vom ganzen Primärschlüssel (und nicht nur von einem Teil dieses Schlüssels) funktional abhängig sein. Nehmen wir an, dass die Tabelle arbeiten der Beispieldatenbank eine zusätzliche Spalte mit der Nummer der Abteilung enthält: m_nr
pr_nr
aufgabe
einst_dat
abt_nr
10102 10102 25348 18316 ..... .....
p1 p3 p2 p2 ... ...
Projektleiter Gruppenleiter Sachbearbeiter
1.Okt.1988 1.Jan.1989 15.Feb.1988 1.Jun.1989 .......... ..........
a3 a3 a3 a1 ... ...
.......... ..........
Den Primärschlüssel dieser Tabelle bildet die Kombination der Spalten m_nr und pr_nr. Die Spalte abt_nr ist nicht voll funktional vom kompletten Primärschlüssel, sondern schon von einem Teil (m_nr) abhängig, weil jede Abteilung, zu der ein Mitarbieter gehört, durch seine Personalnummer eindeutig identifiziert werden kann.
26
1
Relationale Datenbanken eine Einführung
Deswegen befindet sich die oben abgebildete Tabelle nicht in der zweiten Normalform. (Die Tabelle arbeiten der Beispieldatenbank befindet sich in der zweiten Normalform.)
1.3.4 Dritte Normalform Die dritte Normalform besagt, dass zwischen den Spalten einer Tabelle, die nicht den Primärschlüssel bilden, keine Abhängigkeiten existieren dürfen. Ausgegangen wird dabei immer von einer Tabelle, die sich bereits in der zweiten Normalform befindet. Nehmen wir an, dass die Tabelle mitarbeiter der Beispieldatenbank eine zusätzliche Spalte mit dem Namen der Abteilung enthält: m_nr
m_name
m_vorname
abt_nr
abt_name
25348 10102 18316 29346 ..... .....
Keller Huber Müller Probst ........ ........
Hans Petra Gabriele Andreas ........ ........
a3 a3 a1 a2 ... ...
Freigabe Freigabe Beratung Diagnose ........ ........
Der Primärschlüssel dieser Tabelle ist die Spalte m_nr. Weil die Spalten abt_nr und abt_name voneinander abhängig sind und keine von beiden Teil des Primärschlüssels ist, befindet sich die oben abgebildete Tabelle nicht in der dritten Normalform. (Die Tabelle mitarbeiter, genauso wie alle anderen Tabellen der Beispieldatenbank, befindet sich in der dritten Normalform.) Datenbanken, die die dritte Normalform erfüllen, enthalten weitgehend nicht redundante Daten.
1.3.5 Das Entity/Relationship-Modell Alle Daten einer relationalen Datenbank können theoretisch mit Hilfe einer einzigen Tabelle dargestellt werden. (Eine solche Tabelle wird universelle Tabelle genannt.) Der Nachteil eines solchen Datenbankentwurfs stellt die große Redundanz von Daten dar. Falls eine Datenbank z.B. alle Daten in Bezug auf Mitarbeiter und Projekte einer Firma beinhaltet (wo z.B. jeder Mitarbeiter in verschiedenen Projekten arbeitet und jedes Projekt mehrere Mitarbeiter beschäftigt), wird die Speicherung dieser Daten in einer Tabelle dazu führen, dass die Tabelle sehr viele Reihen und Spalten hat. Diese Daten werden redundant sein, was im Falle einer Änderung erhebliche Probleme aufwerfen kann. Der Entwurf der relationalen Datenbanken funktioniert so, dass die existierenden Datenredundanzen in den Tabellen entfernt werden, indem jede Tabelle mit redundanten Daten unterteilt wird, bis keine Datenredundanzen mehr existieren.
Datenbankdesign
27
Das Grundkonzept des E/R-Modells ist die Entität, d.h. ein Objekt der realen Welt. Jede Entität hat mehrere Attribute, die spezifische Eigenschaften dieser Entität darstellen. Abhängig von seinem Typ kann ein Attribut: - einwertig (atomar) - mehrwertig - zusammengesetzt sein. Ein Attribut ist einwertig, falls je ein Wert für die Darstellung jeder Instanz einer Entität benutzt wird. Der Familienname einer Person ist ein Beispiel eines einwertigen Attributs. (Die meisten Attribute sind einwertig.) Ein mehrwertiges Attribut kann einen oder mehrere Werte für die Darstellung der Instanzen einer Entität haben. Der Standort einer Firma ist z.B. ein mehrwertiges Attribut, weil jede Firma einen oder mehrere Standorte haben kann. Zusammengestellte Attribute sind Attribute, die aus mehreren (voneinander unabhängigen) Subattributen zusammengestellt sind. Das typische Beispiel eines zusammengesetzten Attributs ist die Adresse (einer Person oder einer Firma). Dieses Attribut ist aus mehreren einwertigen Attributen, wie PLZ, Stadt und Straße zusammengesetzt. Das folgende Beispiel zeigt die Entität PERSON, die mehrere Attribute hat, von welchen das Attribut Adresse zusammengesetzt und das Attribut Vorname mehrwertig ist. (Alle anderen Attribute sind einwertig.) Beispiel 1.3 PERSON(Pers_nr,Familien_name,{Vorname},Alter,Adresse(PLZ,Strasse))
Jede Entität hat ein Attribut (oder eine Kombination mehrerer Attribute), dessen Werte eindeutig für alle Instanzen sind. Ein solches Attribut wird Schlüsselattribut genannt. (In Beispiel 1.3 ist das Attribut Personal_nr das Schlüsselattribut der Entität PERSON.) Das dritte Grundkonzept des E/R-Modells (neben Entität und Attribut) ist die Beziehung (relationship). Eine Beziehung existiert, falls eine Entität den Bezug auf eine oder mehrere Entitäten hat. (Im spezifischen Fall kann eine Entität auch eine Beziehung mit sich selbst haben.) Die Anzahl der an einer Beziehung teilnehmenden Entitäten spezifiziert den Rang der Beziehung. (Die Beziehung arbeitet_ zwischen den Entitäten MITARBEITER und PROJEKT (siehe Abbildung 1.1) hat z.B. den Rang 2, weil sie zwei Entitäten verbindet.) Jede existierende Beziehung zwischen zwei Entitäten kann nur einen der drei folgenden (Beziehungs-)Typen haben: 1:1, 1:N und M:N. Die Entitäten ABTEILUNG und MITARBEITER haben z.B. den Beziehungstyp 1:N, falls in jeder Abteilung ein oder mehrere Mitarbeiter arbeiten und jeder Mitarbeiter nur einer einzigen Abteilung ange-
28
1
Relationale Datenbanken eine Einführung
hört. Eine Beziehung kann, genauso wie jede Entität auch Attribute haben (siehe Abbildung 1.1) Abbildung 1.1 zeigt ein Beispiel eines E/R-Diagramms. (Ein E/R-Diagramm ist die graphische Notation, die für die Beschreibung eines spezifischen Modells verwendet wird.) In der E/R-Notation wird jede Entität als Rechteck dargestellt, wobei der Entitätsname innerhalb des Rechtecks (gewöhnlich in Großbuchstaben) geschrieben wird. Attribute sind durch Ellipsen spezifiziert, und jedes Attribut ist durch eine gerade Linie mit der entsprechenden Entität (oder Beziehung) verbunden. Schließlich werden die Rauten für die Darstellung der Beziehungen verwendet und alle Entitäten, die zu einer Beziehung gehören, werden mit ihr durch gerade Linien verbunden. Der Beziehungstyp jeder Entität wird auf der Verbindungslinie notiert. . Hinweis
Die E/R-Notation ist nicht standardisiert. Deswegen ist die in Abbildung 1.1 verwendete Notation nur eine von vielen existierenden E/R-Notationen
Abbildung 1.1: Beispiel eines E/R-Diagramms
Die Datenbanksprache SQL
29
1.4 Die Datenbanksprache SQL SQL ist die standardisierte Datenbanksprache, die von allen relationalen Datenbanksystemen verwendet wird. Die Anweisungen dieser Sprache können in zwei Gruppen unterteilt werden: - Anweisungen für Datendefinition (DDL – Data Definition Language) - Anweisungen für Datenmanipulation (DML – Data Manipulation Language) Folgende beide Abschnitte beschreiben die Anweisungen dieser beiden Gruppen.
1.4.1 SQL-Anweisungen für Datendefinition Datendefinitionsanweisungen ermöglichen das Erstellen, Ändern und Löschen existierender Datenbankobjekte. Mit Hilfe der generischen Anweisung CREATE objekt_name objekteigenschaften
können u.a. folgende Datenbankobjekte erstellt werden: - Datenbank selbst (wird vom SQL-Standard und manchen Datenbanksystemen nicht unterstützt) - Tabelle - Sicht - Index. Genauso existiert eine generische Anweisung ALTER objekt_name objekteigenschaften
mit der die Struktureigenschaften eines Datenbankobjektes geändert werden können. (Verschiedene Datenbanksysteme unterstützen die Änderung unterschiedlicher Datenbankobjekte, wobei für alle Systeme gemeinsam ist, dass sie die Änderung einer Tabelle mit Hilfe der ALTER TABLE-Anweisung ermöglichen.) Schließlich existiert die generische Anweisung DROP objekt_name
mit der ein existierendes Datenbankobjekt gelöscht wird. Beispiel 1.4 zeigt die Erstellung der Tabelle mitarbeiter der Beispieldatenbank. Beispiel 1.4 CREATE TABLE mitarbeiter (m_nr INTEGER NOT NULL, m_name VARCHAR(25) NOT NULL,
30
1
Relationale Datenbanken eine Einführung
m_vorname VARCHAR(25) NOT NULL, abt_nr CHAR(4) NULL, PRIMARY KEY (m_nr))
Die CREATE TABLE-Anweisung in Beispiel 1.4 enthält den Namen der Tabelle (mitarbeiter) und die Definition aller Spalten, die zu dieser Tabelle gehören. Die Definition einer Spalte enthält u.a. den Datentyp der Spalte und die Information, ob die Spalte die NULL-Werte zulässt oder nicht. Hinweis
Ein NULL-Wert ist ein spezieller Wert, der für eine Spalte zugelassen werden kann. Dieser Wert wird vorwiegend dann verwendet, wenn die Information für einen Datenwert der Spalte fehlt oder nicht bekannt ist. Er kann in einer Tabelle mit Personaldaten vorkommen, z.B. wenn eine Person über einen Telefonanschluss verfügt, ihre Telefonnummer aber (noch) nicht bekannt ist. In diesem Fall ist es sinnvoll, die Spalte telefon_nr mit dem NULL-Wert zu besetzen. Bezüglich der Datentypen von Spalten gilt folgendes: Alle Datenwerte einer Spalte müssen denselben Datentyp haben. Die SQL-Sprache kennt mehrere Datentypen, die in folgende Gruppen unterteilt werden können: - numerische Datentypen - alphanumerische Datentypen - Datentypen für Datums- und/oder Zeitangeben. Zu den numerischen Datentypen gehören u.a.: INTEGER, SMALLINT, DECIMAL und FLOAT. Die alphanumerischen Datentypen sind u.a. CHARACTER und VARCHAR (was ein Kürzel für CHARACTER VARYING ist). Zu den Datumsdatentypen gehören die Datentypen DATETIME und TIMESTAMP. In Beispiel 1.4 erlaubt die Spalte abt_nr den NULL-Wert, was bedeutet, dass es durchaus möglich ist, dass für einige Mitarbeiter (z.B. bei Neueinstellungen) die zugehörige Abteilung unbekannt ist. Beispiel 1.5 zeigt die Verwendung der ALTER TABLE-Anweisung. Beispiel 1.5 ALTER TABLE mitarbeiter (ADD telefon_nr CHAR(12) NULL)
In Beispiel 1.5 wird eine neue Spalte (telefon_nr) der Tabelle mitarbeiter hinzugefügt. Beispiel 1.6 DROP TABLE mitarbeiter
Die Datenbanksprache SQL
31
Mit der DROP TABLE-Anweisung in Beispiel 1.6 wird eine existierende Tabelle (in diesem Fall die Tabelle mitarbeiter) gelöscht.
1.4.2 SQL-Anweisungen für Datenmodifikation Datenmodifikationsanweisungen ermöglichen das Erstellen, Ändern und Löschen existierender Datenbankobjekte. Mit Hilfe der Anweisungen - SELECT - UPDATE - INSERT - DELETE ist es möglich, Datenwerte aus einer Datenbank auszuwählen, zu ändern und einzufügen. Folgende Abschnitte beschreiben alle diese Anweisungen.
Die SELECT-Anweisung Mit der SELECT-Anweisung werden Datenwerte aus einer Datenbank ausgewählt. Sie können aus einer oder aus mehreren, miteinander verbundenen Tabellen ausgewählt werden. Das Ergebnis einer solchen Auswahl ist erneut eine Tabelle, die keine, eine oder mehrere Reihen und eine oder mehrere Spalten hat. Beispiel 1.7 zeigt eine einfache Form der SELECT-Anweisung. Beispiel 1.7 Finde alle Mitarbeiterdaten, die entweder die Personalnummer 29346, oder 228559 oder 25348 haben. select * from mitarbeiter where m_nr in (29346, 28559, 25348)
Das Ergebnis ist: m_nr m_name m_vorname abt_nr --------------------------------------------------------25348 Keller Hans a3 28559 Mozer Sibille a1 29346 Probst Andreas a2
Beispiel 1.8 zeigt die Möglichkeit, Daten aus mehreren Tabellen auszuwählen. Beispiel 1.8 Nennen Sie Namen und Vornamen aller Projektleiter, deren Abteilung den Standort Stuttgart hat.
32
1
Relationale Datenbanken eine Einführung
select m_name, m_vorname from arbeiten join mitarbeiter on arbeiten.m_nr =mitarbeiter.m_nr join abteilung on mitarbeiter.abt_nr = abteilung.abt_nr where aufgabe ='Projektleiter' and stadt = 'Stuttgart'
Das Ergebnis ist: m_name m_vorname ----------------------------------------------Huber Petra
In Beispiel 1.8 verknüpfen wir die Tabellen mitarbeiter, arbeiten und abteilung mit zwei Join-Operationen, weil die Daten aus der Tabelle mitarbeiter ausgewählt werden und die beiden Bedingungen sich auf die Spalten aus zwei anderen Tabelle (abteilung und arbeiten) beziehen.
Die INSERT-Anweisung Mit der INSERT-Anweisung können Reihen oder Teile von Reihen in eine bestehende Tabelle eingefügt werden. Im Unterschied zu der SELECT-Anweisung kann man mit einer INSERT-Anweisung Reihen in eine einzige Tabelle einfügen. Beispiel 1.9 insert into mitarbeiter values(15201, 'Lang', 'Viktor', null)
Mit der INSERT-Anweisung in Beispiel 1.9 wird eine Reihe in die Tabelle mitarbeiter eingefügt. Beispiel 1.10 zeigt die zweite Form der INSERT-Anweisung, mit der mehrere Reihen gleichzeitig in eine Tabelle eingefügt werden können. Beispiel 1.10 create table muench_abt (abt_nr char(4) not null, abt_name char(20) not null) insert into muench_abt(abt_nr, abt_name) select abt_nr, abt_name from abteilung where stadt = 'München'
Mit der CREATE TABLE-Anweisung in Beispiel 1.10 wird zuerst eine neue Tabelle erstellt. Die Tabelle muench_abt hat dieselben Spalten wie die Tabelle abteilung, abgesehen von der Spalte stadt. Mit der SELECT-Anweisung, die sich innerhalb von INSERT befindet, werden alle Reihen der Tabelle abteilung ausgewählt, deren Spalte stadt den Datenwert München hat. Diese Reihen werden anschließend in der Tabelle muench_abt eingefügt.
Die Datenbanksprache SQL
33
Die Reihen der Tabelle muench_abt können mit der Anweisung: select * from muench_abt
ausgewählt werden. Das Ergebnis dieser Anweisung ist: abt_nr abt_name -------------------------a1 Beratung a2 Diagnose
Die UPDATE-Anweisung Die UPDATE-Anweisung ändert die Datenwerte der Reihen einer Tabelle. Ähnlich der INSERT-Anweisung kann eine UPDATE-Anweisung nur Reihen einer einzigen Tabelle modifizieren. Beispiel 1.11 update arbeiten set aufgabe = 'Gruppenleiter' where m_nr = 18316 and pr_nr = 'p2'
Mit der UPDATE-Anweisung in Beispiel 1.11 wird nur eine Reihe der Tabelle arbeiten geändert, weil die Kombination beider Bedingungen mit den Spalten m_nr und pr_nr immer nur eine Reihe als Ergebnis liefert. (Mit anderen Worten, die Kombination der Spalten m_nr und pr_nr ist der Primärschlüssel der Tabelle arbeiten.) Im obigen Beispiel wurde die Aufgabe des Mitarbeiters, die bis dahin unbekannt war, definiert. Obwohl eine UPDATE-Anweisung nur Datenwerte einer einzigen Tabelle ändert, ist es möglich, eine andere Tabelle in der Bedingung der UPDATE-Anweisung zu verwenden. Beispiel 1.12 zeigt das. Beispiel 1.12 Die Aufgaben von Frau Huber sollen, wegen längerer Krankheit, in allen Projekten auf unbekannt gesetzt werden. update arbeiten set aufgabe = null where m_nr IN (select m_nr from mitarbeiter where m_name = 'Huber')
Die UPDATE-Anweisung in Beispiel 1.12 beinhaltet eine WHERE-Klausel, die eine SELECT-Anweisung enthält, d.h. eine Abfrage, die sich auf eine andere Tabelle (in diesem Fall mitarbeiter) bezieht.
34
1
Relationale Datenbanken eine Einführung
Die DELETE-Anweisung Mit der DELETE-Anweisung werden Reihen aus einer Tabelle gelöscht. Im Unterschied zu der SELECT-Anweisung kann eine UPDATE-Anweisung nur Reihen aus einer einzigen Tabelle löschen. Beispiel 1.13 Löschen Sie alle Reihen der Tabelle arbeiten. delete from arbeiten
Die DELETE-Anweisung, genauso wie die UPDATE-Anweisung kann optional die WHERE-Klausel enthalten. Falls die WHERE-Klausel ausgelassen wird, wie in Beispiel 1.13 der Fall, werden alle Reihen der genannten Tabelle gelöscht. Folgendes Beispiel zeigt eine DELETE-Anweisung mit der WHERE-Klausel. Beispiel 1.14 Löschen Sie die Datenwerte der Gruppenleiter aller Projekte. delete from arbeiten where aufgabe = 'Gruppenleiter'
Die WHERE-Klausel liefert die Reihen, die aus einer Tabelle gelöscht werden. Die explizite Angabe von Spalten in einer DELETE-Anweisung ist nicht notwendig, weil alle Datenwerte einer oder mehrerer Reihen gelöscht werden. (Die WHERE-Klausel in der DELETE-Anweisung kann, wie bei der UPDATE-Anweisung auch, eine Unterabfrage enthalten.)
1.4.3 Sichten (Views) Eigenschaft aller Benutzertabellen ist, dass sie physikalisch existieren. Eine Benutzertabelle enthält also Objekte, die alle auf der Platte gespeichert sind. Im Unterschied zu Basistabellen belegen Sichten (views) keinen Speicherplatz. Sichten werden immer aus existierenden Basistabellen abgeleitet, und das System trägt die Eigenschaften jeder Sicht in den Systemkatalog ein. (Für die Beschreibung des Systemkatalogs siehe den nächsten Abschnitt.) Lediglich die Einträge in verschiedenen Systemtabellen existieren physikalisch von einer Sicht. Deswegen heißen Sichten auch virtuelle Tabellen. Jede Sicht wird mit Hilfe der CREATE VIEW-Anweisung erstellt und mit der DROP VIEW-Anweisung gelöscht. (Manche Datenbanksysteme unterstützen auch die Anweisung zur Änderung der Struktur einer Sicht.) Jede CREATE VIEW-Anweisung enthält eine SELECT-Anweisung, mit der die Auswahl der Reihen und Spalten aus der(n) Benutzertabelle(n) durchgeführt wird.
Die Datenbanksprache SQL
35
Beispiel 1.15 Erstellen Sie eine Sicht, die alle Daten der Sachbearbeiter der Firma beinhaltet. create view v_sach_arb as select m_nr, pr_nr, einst_dat from arbeiten where aufgabe = 'Sachbearbeiter'
In Beispiel 1.15 werden mit der SELECT-Anweisung die Reihen der Basistabelle arbeiten ausgewählt, die die Bedingung in der WHERE-Klausel erfüllen. Alle ausgewählten Reihen bilden die neuerstellte Sicht v_sach_arb. Alle Datenmanipulationsanweisungen (SELECT, INSERT, UPDATE und DELETE) können auch auf Sichten angewendet werden. Jeder Anwender, der eine Sicht benutzt, arbeitet mit ihr genau wie mit jeder anderen Basistabelle. Die Abfragen, die eine Sicht betreffen, werden tatsächlich auf der zugrunde liegenden Basistabelle durchgeführt. (Wir erinnern noch einmal daran, dass eine Sicht physikalisch nicht existiert; es existieren nur Einträge in Systemtabellen, die sie definieren.) Beispiel 1.16 zeigt dies. Beispiel 1.16 select m_nr from v_sach_arb where pr_nr = 'p2'
Die Abfrage aus Beispiel 1.16 wird von einem Datenbanksystem auf Grund der Definition der Sicht v_sach_arb (Beispiel 1.15) in die Abfrage auf die zugrunde liegende Basistabelle arbeiten folgendermaßen umgewandelt: select m_nr from arbeiten where pr_nr = 'p2' and aufgabe = 'Sachbearbeiter'
1.4.4 Der Systemkatalog und das Informationsschema Der Systemkatalog beinhaltet interne Informationen, die ein Datenbanksystem für einen reibungslosen Ablauf benötigt. Im Systemkatalog befindet sich die Information über alle Datenbanken und ihre Objekte (Tabellen, Spalten, usw.). Diese Information ist für das einwandfreie Funktionieren des Systems unerlässlich. Auf Grund der gespeicherten Information über ein Datenbankobjekt weiß das System z.B. wer der Eigentümer dieses Objektes ist. Dementsprechend enthalten auch alle relationalen Datenbanksysteme einen Systemkatalog. Die Eigenschaft des Systemkatalogs eines relationalen Datenbanksystems ist, dass Metadaten (d.h. Daten über Daten), genauso wie die Basisdaten, in Tabellen gespeichert sind. Damit ist es möglich, auch Metadaten mit Hilfe der SQL-Anweisungen abzufragen und falls notwendig, zu ändern.
36
1
Relationale Datenbanken eine Einführung
Jedes kommerzielle Datenbanksystem hat eine Sammlung von Systemtabellen, die sich in ihren Namen und ihrer Bedeutung unterscheiden. Zum Beispiel ist schon der Begriff des Systemkatalogs bei den unterschiedlichen Datenbanksystemen verschieden: INFORMIX nennt seinen Systemkatalog system catalog, während ORACLE vom data dictionary spricht. (Genauso verwendet IBM DB2 einen anderen Begriff für den Systemkatalog: catalog tables.) Genauso unterscheiden sich die Namen einzelner Tabellen bei verschiedenen Datenbanksystemen: Während INFORMIX und IBM DB2 eine Systemtabelle namens systables enthalten, die Informationen über Tabellen einer Datenbank speichert, hat ORACLE drei Tabellen mit dem entsprechenden Inhalt: user_tables, all_tables und dba_tables. (Zusätzlich dazu, stellen diese drei Oracle-Tabellen Sichten dar.) Beispiele 1.17 und 1.18 zeigen zwei identische Abfragen auf den Systemkatalog von Informix bzw. Oracle. Obwohl beide Beispiele alle Daten der Tabelle mitarbeiter aus dem Sytemkatalog abfragen, zeigen die beiden SELECT-Anweisungen signifikante Unterschiede. Beispiel 1.17 (Informix) SELECT * FROM systables WHERE tabname = 'mitarbeiter'
Beispiel 1.18 (Oracle) SELECT * FROM user_objects WHERE object_name = 'MITARBEITER'
Das SQL-Standardisierungsgremium hat das Informationsschema konzipiert, damit eine einheitliche Oberfläche existiert, mit der Systemkataloge aller Datenbankhersteller abgefragt werden können. Dieses wird im nächsten Abschnitt beschrieben.
Informationsschema Das Informationsschema wurde ursprünglich im SQL92-Standard eingeführt, mit der Absicht eine einheitliche Schnittstelle einzuführen, die dann den Zugriff von einem Datenbanksystem auf den Systemkatalog eines anderen ermöglicht. Es besteht aus Sichten, die die gesamte Information über Datenbankobjekte (Tabellen, Spalten usw.) liefern. (Diese Sichten können vom Benutzer nicht geändert werden.) Im Unterschied zu den Systemtabellen des Systemkatalogs, die die Metadaten eines Systems enthalten, verwaltet das Informationsschema die Umgebung einer Datenbank. Die folgenden Unterabschnitte beschreiben die beiden wichtigsten Sichten, die zum Informationsschema gehören.
Die Datenbanksprache SQL
37
Information_schema.tables Die Sicht information_schema.tables enthält eine Reihe für jede Tabelle der aktuellen Datenbank, zu der der Benutzer Zugriff hat. Die wichtigsten Spalten dieser Sicht sind: Spalte
Beschreibung
table_catalog
Der Name des Katalogs (der Datenbank), zu dem die Sicht gehört.
table_schema
Der Name des Schemas, zu dem die Sicht gehört.
table_name
Der Tabellenname.
table_type
Der Tabellentyp (kann »BASE TABLE« oder »VIEW« sein).
Information_schema.columns Die Sicht enthält eine Reihe für jede Spalte der aktuellen Datenbank, zu der der Benutzer Zugriff hat. Die wichtigsten Spalten dieser Sicht sind: Spalte
Beschreibung
table_catalog
Der Name des Katalogs (der Datenbank), zu dem die Sicht gehört.
table_schema
Der Name des Schemas, zu dem die Sicht gehört.
table_name
Der Tabellenname.
column_name
Der Spaltenname.
ordinal_position
Die Identifikationsnummer der Spalte.
data_type
Der Datentyp der Spalte.
1.4.5 Gespeicherte Prozeduren Gespeicherte Prozeduren haben Ähnlichkeiten mit den Funktionen, die in einer prozeduralen Programmiersprache programmiert sind. Sie werden ähnlich wie alle anderen Datenbankobjekte mit Hilfe einer Datendefinitionsanweisung erstellt und als Objekte der Datenbank gespeichert. Diese Anweisung heißt CREATE PROCEDURE und sie ermöglicht die Speicherung der Prozedur im Datenbank-Server. (Mit der DROP PROCEDURE-Anweisung wird eine erstellte Prozedur gelöscht.) Jede Prozedur kann sowohl SQL- als auch prozedurale Anweisungen enthalten. Die prozeduralen Anweisungen entsprechen den spezifischen Erweiterungen der SQLSprache beim einzelnen Datenbanksystemen.
Hinweis Obwohl gespeicherte Prozeduren seit 1995 vom SQL-Gremium standardisiert sind, haben alle Datenbankhersteller ihre eigenen prozeduralen Erweiterungen. Deswegen ist die Erstellung der gespeicherten Prozeduren herstellerspezifisch und auch
38
1
Relationale Datenbanken eine Einführung
die einfachsten Prozeduren, die für ein System implementiert sind, müssen für ein anderes System umgeschrieben werden. Jeder gespeicherten Prozedur können Daten als Parameter zugewiesen werden. Die Übergabe der Parameter wird beim Prozeduraufruf durchgeführt. Prozeduren können auch einen Rückgabewert übergeben, der die Information über die erfolgreiche Ausführung der Prozedur bzw. über einen aufgetretenen Fehler liefert. Gespeicherte Prozeduren werden übersetzt, bevor sie in der Datenbank gespeichert werden. Damit wird für jede Prozedur immer ein Ausführungsplan gespeichert, der bei der Ausführung der Prozedur benutzt wird. Diese Vorgehensweise bringt für oft verwendete gespeicherte Prozeduren einen entscheidenden Performance-Vorteil: Die wiederholte Übersetzung der Prozedur kann eliminiert und damit ihre Ausführung wesentlich beschleunigt werden. Das o.g. Verfahren bei der Übersetzung und Ausführung von gespeicherten Prozeduren bringt auch einen anderen Vorteil: Die Anzahl der Zugriffe zwischen den Anwendungsprogrammen einerseits und dem Datenbank-Server andererseits lässt sich wesentlich reduzieren, indem man eine SQL-Anweisungsfolge in der Form einer gespeicherten Prozedur schreibt, anstatt SQL-Anweisungen einzeln zu benutzen. (Diese Eigenschaft der gespeicherten Prozeduren ist besonders vorteilhaft, wenn der Datenbank-Server und die Datenbankanwendung entfernt voneinander sind). Die Verwendung von gespeicherten Prozeduren kann weitere Vorteile mit sich bringen: - gespeicherte Prozeduren bieten die Möglichkeit, den Zugriff auf eine Tabelle einzuschränken, indem alle Änderungen der Tabellendaten mit Hilfe einer einzigen Prozedur durchgeführt werden. - Alle Anwendungsprogramme, die das entsprechende Zugriffsrecht haben, können Prozeduren gemeinsam verwenden, anstatt denselben Code einzeln in jedem Programm zu enthalten. Dadurch werden die Programmierung und die Verwendung der existierenden Ressourcen effizienter. Die Verwendung von gespeicherten Prozeduren kann unter Umständen auch einen Nachteil haben: Prozeduren sind in Systemtabellen, die sich im Allgemeinen auf der Platte befinden, gespeichert. Bevor sie zum ersten Mal in einer Anwendung benutzt werden können, müssen sie in den Arbeitsspeicher geladen werden. Mit der Anweisung EXECUTE PROCEDURE kann eine existierende Prozedur ausgeführt werden. (Um eine gespeicherte Prozedur ausführen zu können, muss der Benutzer ihr Eigentümer sein oder das entsprechende Ausführungsrecht haben.
Datenbankanwendungen programmieren
39
1.5 Datenbankanwendungen programmieren Datenbankanwendungen können auf zwei unterschiedliche Arten implementiert werden. Die erste Schnittstelle wird oft Host Language Interface (HLI) genannt, weil dabei die SQL-Anweisungen in einer Host-Sprache eingebettet werden. Diese Schnittstelle wird oft die eingebettete SQL-Sprache genannt. Bei dieser Schnittstelle wird ein Vorübersetzer zur Verfügung gestellt, der jede SQL-Anweisung in die Hostsprache übersetzt. Damit wird, nach der Vorübersetzerphase, ein Programm erzeugt, das anschließend übersetzt werden kann. Prozedurale Programmiersprachen können auch auf eine andere Weise verwendet werden, um Datenbankanwendungen zu erstellen. Diese zweite Methode heißt Schnittstelle mit Bibliotheksaufrufen (Call Level Interface – CLI) und charakterisiert die Existenz einer oder mehrerer Bibliotheken, deren Funktionen von einem Programm aufgerufen werden. (Das Programm wird immer mit Hilfe einer prozeduralen Programmiersprache implementiert.) Die übersetzten und in der Bibliothek gespeicherten Funktionen stellen damit eine weitere Schnittstelle zum Datenbanksystem dar.
1.6 Zusammenfassung Ein relationales Datenbankmanagementsystem gewährleistet u.a.: - logische und physikalische Datenunabhängigkeit - prozedurale und nichtprozedurale Schnittstellen - effiziente Abarbeitung von Datenbankoperationen - Datenintegrität - konkurrierenden Datenzugriff - Datensicherheit - Datenschutz. Die SQL-Sprache ist die standardisierte Datenbanksprache, mit der auf Daten eines relationalen Datenbanksystems zugegriffen werden kann. Die Anweisungen dieser Sprache können in Datendefinitions- (DDL) und Datenmanipulationsanweisungen (DML) unterteilt werden. Die CREATE TABLE-Anweisung stellt das Beispiel einer DDL-Anweisung dar, während SELECT, INSERT, UPDATE und DELETE vier generische DML-Anweisungen sind. Das nächste Kapitel beschreibt die objektorientierten Erweiterungen der relationalen Datenbanksysteme Oracle, Informix und IBM DB2.
2 Objektorientierte Erweiterungen in relationalen DB-Systemen Relationale Datenbanksysteme haben in all den Jahren, in denen sie benutzt wurden, ihre Tauglichkeit für den produktiven Einsatz unter Beweis gestellt, und sind deswegen mit Abstand die wichtigsten Datenbanksysteme der achtziger und neunziger Jahre gewesen. Trotz dieses Erfolgs haben sie auch einige Schwächen, die teils als Schwächen des relationalen Modells gelten und teils als Schwächen ihres ursprünglichen Entwurfs. Die wichtigste Schwachstelle ist, dass objektorientierte Konzepte von relationalen Datenbanksystemen nicht unterstützt werden. Alle auf dem Markt befindlichen Hersteller der relationalen Datenbanksysteme haben sich in den letzten Jahren bemüht diese Schwächen zu beheben und die meisten objektorientierten Konzepte zu implementieren. Obwohl in der Zwischenzeit auch der SQL-Standard (SQL99) objektorientierte Erweiterungen in die SQL-Sprache übernommen und sie festgelegt hat, gilt für alle Datenbankhersteller, dass sie wenig Gemeinsamkeiten bei der Implementierung dieser Konzepte haben. Aus diesem Grund ist es unmöglich, eine Beschreibung dieser Konzepte für alle existierenden Datenbanksysteme zu machen (wie wir für die SQL-Sprache in Kapitel 1 gemacht haben). Deswegen wird dieses Kapitel in mehrere Abschnitte unterteilt, und jeder Abschnitt wird einem spezifischen Datenbanksystem gewidmet. Der erste Abschnitt dieses Kapitels beschreibt die objektorientierten Erweiterungen bei Oracle, während der zweite Teile dieselben Erweiterungen bei Informix erläutert. Im letzten Teil des Kapitels werden objektorientierte Erweiterungen von IBM DB2 skizziert. (Wir werden uns mit den objektorientierten Erweiterungen dieses Systems nicht ausführlich befassen, weil die Firma IBM diese Funktionalität noch nicht in die JDBCSchnittstelle bzw. SQLJ implementiert hat.)
2.1 Objektorientierte Erweiterungen bei Oracle Oracle hat folgende Erweiterungen, die in Bezug zur Objektorientierung stehen: - der Objekttyp - der Datentyp REF (Objektreferenzen) - Kollektionen. Alle diese Erweiterungen werden in den nachfolgenden Unterabschnitten beschrieben.
42
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
2.1.1 Der Objekttyp Seit Version 8 unterstützt Oracle benutzerdefinierte Datentypen mit Hilfe eines Modells, das die Firma Oracle Typsystem (type system) nennt. Dieses Typsystem gibt dem Benutzer die Möglichkeit, neue Datentypen mit Hilfe der herkömmlichen Standarddatentypen, existierenden benutzerdefinierten Datentypen, Objektreferenzen und Kollektionen zu definieren. (Die einzige Gruppe von Datentypen, die Oracle vor der Einführung der benutzerdefinierten Datentypen unterstützt hat, waren Standarddatentypen wie INTEGER, CHARACTER, FLOAT usw.) Benutzerdefinierte Datentypen werden bei Oracle mit Hilfe des Objekttyps definiert. Ein Objekttyp ist ein einheitlicher Mechanismus, mit dem die Struktur und das Verhalten eines benutzerdefinierten Datentyps definiert werden kann. (Unter der Struktur eines Typs verstehen wir alle Attribute, die diesen Typ beschreiben. Das Verhalten eines Typs umfasst alle Operationen (Methoden), die auf einzelne Instanzen des Typs angewendet werden können.) Ein Objekttyp wird bei Oracle mit Hilfe der CREATE OBJECT TYPE-Anweisung definiert. Die Syntax dieser Anweisung sieht folgendermaßen aus: CREATE TYPE typ_name AS OBJECT (attr_name datentyp [{, attr_name2 datentyp2} ...]
attr_name ist der Name eines Attributes, während datentyp seinen Datentyp kennzeichnet. Beispiel 2.1 zeigt die Erstellung eines einfachen Objekttyps. Beispiel 2.1 CREATE TYPE name_t AS OBJECT (nachname VARCHAR(30), vorname VARCHAR(20));
In Beispiel 2.1 wird ein neuer Datentyp namens name_t erstellt. Dieser Datentyp besteht aus zwei Elementen, die beide mit Hilfe des Standarddatentyps VARCHAR definiert sind. (Ein Typ wie name_t, der mehrere Komponenten verschiedener Datentypen zu einem neuen Datentyp zusammenfasst, wird auch Tupel bzw. Reihentyp genannt.) Die CREATE OBJECT TYPE-Anweisung erstellt im Grunde nur eine Schablone (ähnlich der struct-Anweisung in der Sprache C), aber keine Instanz dieses Typs wurde bis jetzt erstellt. Um dies zu erreichen, muss eine Tabelle erstellt werden, deren Spalten (oder Reihen) Instanzen diesen Typs enthalten. Dies wird mit Hilfe der herkömmlichen CREATE TABLE-Anweisung durchgeführt. Beispiel 2.2 zeigt die Erstellung einer Tabelle, deren Spalte mit Hilfe des oben definierten Typs deklariert ist. Beispiel 2.2 CREATE TABLE dozent (dozent_id INT, name name_t, raum INT);
Objektorientierte Erweiterungen bei Oracle
43
In Beispiel 2.2 wurde eine Tabelle erstellt, deren Spalte name mit Hilfe des benutzerdefinierten Typs name_t (Beispiel 2.1) deklariert ist. Die Tabelle dozent ist leer. Um einzelne Reihen in diese Tabelle einzufügen, wird die INSERT-Anweisung verwendet, die in die Spalte name Instanzen des Typs name_t einfügt. Beispiel 2.3 zeigt dies. Beispiel 2.3 INSERT INTO dozent VALUES (1234, name_t('Kurz' , 'Helga'), 120);
Wie aus Beispiel 2.3 ersichtlich, wird der Wert der zweiten Spalte (name) so eingefügt, dass der Typ name_t zuerst angegeben wird, um zu spezifizieren, dass es sich beim Einfügen um eine Instanz dieses Typs handelt. Die Form name_t(’Kurz’, ’Helga’) die eine Instanz eines Typs aus den einzelnen Attributen bildet, wird generell Objektkonstruktor genannt. Um die Attribute eines Objektkonstruktors anzusprechen, wird die sogenannte Punktnotation (dot notation) verwendet. Beispiel 2.4 zeigt die Abfrage auf die Tabelle dozent, wo u.a. auch der Nachname des Dozenten ermittelt wird. Beispiel 2.4 Finde die Nummer und den Nachnamen des Dozenten, der im Raum 120 beherbergt ist. SELECT d.dozent_id, d.name.nachname FROM dozent d WHERE d.raum =120;
In Beispiel 2.4 wird für den Nachnamen des Dozenten die doppelte Punktnotation verwendet: Der erste Punkt indiziert, dass es sich bei name um eine Spalte der Tabelle dozent handelt (d ist der Aliasname dieser Tabelle). Der zweite Punkt spezifiziert, dass es sich bei nachname um ein Attribut des Objekttyps name_t handelt.
Hinweis Bei der Verwendung der Objekttypen und der Punktnotation soll immer der Aliasname für die entsprechende Tabelle benutzt werden. Oracle verlangt dies (obwohl in manchen Fällen auch die Vermeidung der Aliasnamen in einer Anweisung zu ihrer korrekten Ausführung führt). Ein Objekttyp kann benutzt werden, um einen anderen (komplexeren) Objekttyp zu definieren. Beispiel 2.5 zeigt die Erstellung eines solchen Objekttyps.
44
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
Beispiel 2.5 CREATE TYPE person_t AS OBJECT (person_id INT, name name_t, alter_als_jahre INT);
Der Objekttyp person_t verwendet den Typ name_t für die Definition eines seiner Attribute (name). Die Voraussetzung für die Definition eines Objekttyps mit Hilfe eines einfacheren Typs ist die Existenz des zweiten. Das heißt, dass der Typ person_t in Beispiel 2.5 nur erstellt werden kann, falls der Typ name_t schon existiert. Die Verkettung mehrerer Objekttypen, wo der nächste immer auf der Basis des vorherigen (wie im Beispiel 2.5) definiert ist, hat Konsequenzen beim Löschen eines Typs. Generell wird ein Objekttyp mit Hilfe der DROP TYPE-Anweisung gelöscht. Falls aber eine Kette von Abhängigkeiten zwischen mehreren Objekttypen existiert, muss zuerst der zuletzt definierte (der komplexeste) Objekttyp gelöscht werden und danach, der Reihe nach die anderen. Bezogen auf Beispiel 2.5 heißt das, dass zuerst der Objekttyp person_t gelöscht werden muss, bevor der Typ name_t entfernt werden kann. (Die Information über die Abhängigkeiten zwischen den existierenden Objekttypen wird bei Oracle in entsprechenden Systemtabellen gespeichert.) Wie wir schon erläutert haben, kann ein Objekttyp nicht nur für die Definition einer Tabellenspalte (wie in den Beispielen 2.2 bzw. 2.5 der Fall ist) verwendet werden. Ein solcher Typ kann auch für die Erstellung einer sogenannten Objekttabelle (object table) verwendet werden. Eine Objekttabelle ist eine Tabelle, deren Instanzen dem darunterliegenden Objekttyp entsprechen. Beispiel 2.6 zeigt die Erstellung der Objekttabelle personen. Beispiel 2.6 CREATE TABLE personen OF person_t (PRIMARY KEY(person_id));
Die CREATE TABLE-Anweisung in Beispiel 2.6 erstellt eine Tabelle personen, die auf dem Objekttyp person_t (Beispiel 2.5) basiert. Wie aus Beispiel 2.6 ersichtlich, können Integritätsregeln (wie PRIMARY KEY, CHECK usw.) ausschließlich für eine Objekttabelle definiert werden. Mit anderen Worten können solche Integritätsregeln nicht mit der entsprechenden CREATE TYPE-Anweisung angegeben werden, was auch nahe liegend ist, weil eine CREATE TYPE-Anweisung nur eine Schablone definiert, auf deren Grundlage dann viele unterschiedliche Objekttabellen (falls notwendig) erstellt werden können. Beispiel 2.7 zeigt, wie Reihen der Tabelle personen eingefügt werden können, während Tabelle 2.1 den Inhalt dieser Tabelle nach dem Einfügen widerspiegelt.
Objektorientierte Erweiterungen bei Oracle
45
Beispiel 2.7 INSERT INTO personen VALUES(12345, name_t('Kurz' , 'Helga'), 18); INSERT INTO personen VALUES(23456, name_t('Lang' , 'Otto'), 80); INSERT INTO personen VALUES(34567, name_t('Haub' , 'Anne'), 45); person_id
name
alter_als_jahre
12345
Kurz, Helga
18
23456
Lang, Otto
80
34567
Haub, Anne
45
Tabelle 2.1: Objekttabelle personen mit einigen Reihen
Die Syntax einer UPDATE-Anweisung, die sich in ihrer SET-Klausel auf einen Objekttyp bezieht, sieht analog der Syntax der INSERT-Anweisung (siehe Beispiel 2.3) aus. Beispiel 2.8 zeigt eine solche UPDATE-Anweisung. Beispiel 2.8 UPDATE personen p SET p = person_t(123456789, name_t('Lang', 'Anne'), 32) WHERE person_id = 987654321;
In Beispiel 2.8 wird eine ganze Reihe der Tabelle personen auf Grund der Bedingung in der WHERE-Klausel ersetzt. (Oracle erlaubt sogar den Wert eines Primärschlüssels zu ersetzen, wie in Beispiel 2.8 ersichtlich ist.)
2.1.2 Kollektionen Kollektionen haben gewisse Ähnlichkeiten mit den schon beschrieben Objekttypen. Im Unterschied zu den Objekttypen, die generell mehrere Elemente verschiedener Datentypen haben, enthalten Kollektionen immer Elemente desselben Datentyps. (Objekttypen und Kollektionen werden gemeinsam Typkonstruktoren genannt, weil mit ihrer Hilfe komplexere Datentypen erstellt werden können.) Zu den Kollektionen gehören generell: - Menge (set) - Multimenge (multiset bzw. bag) - Liste (list) - Vektor (array) - geschachtelte Tabellen (nested tables bzw. table type). Ein Mengenkonstruktor erzeugt aus mehreren Elementen eines zugrunde liegenden Typs einen neuen Typ. (Der zugrunde liegende Typ heißt Elementtyp und kann ein
46
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
Standarddatentyp, ein Typkonstruktor oder ein benutzerdefinierter Datentyp sein.) Die Elemente einer Menge haben keine Ordnung. Eine Menge enthält keines der Elemente mehrfach. Ein Multimengenkonstruktor entspricht einem Mengenkonstruktor bis auf eine Ausnahme: die mehrfache Erscheinung einzelner Elemente ist erlaubt. Im Unterschied zu den Kollektionen Menge und Multimenge hat eine Liste geordnete Elemente und kann ein Element mehrfach enthalten. Damit existieren bei einer Liste das erste Element, das zweite usw., während bei Mengen und Multimengen solche Klassifizierung der Elemente nicht möglich ist. Ein Vektor (array) ist bis auf einen Unterschied identisch mit dem Kollektionstyp Liste. Im Unterschied zu Liste hat Vektor eine konstante Anzahl von Elementen. Eine geschachtelte Tabelle ist diejenige Tabelle, die in einer anderen Tabelle anstelle eines Datenwertes erscheint. (Die Unterstützung der geschachtelten Tabellen verletzt die erste Normalform des relationalen Datenmodels. Von allen oben genannten Kollektionen unterstützt Oracle die letzten beiden: Vektoren und geschachtelte Tabellen.
Vektoren Der Kollektionstyp Vektor wird bei Oracle mit Hilfe des Datentyps VARRAY (varyinglength array) definiert. Jeder mit VARRAY definierte Vektor hat einen Namen, den Datentyp der Elemente und die maximale Anzahl von Elementen, die dieser enthalten kann. (Die tatsächliche Anzahl der Elemente entspricht nicht unbedingt der maximalen Angabe, genauso wie das beim Datentyp VARCHAR ist, wo die tatsächliche Anzahl von alphanumerischen Zeichen kleiner oder gleich dem angegebenen Wert ist.) Alle Elementwerte eines Vektors werden bei Oracle in den meisten Fällen physikalisch mit den anderen Spalten der entsprechenden Reihe gespeichert. Die Erstellung eines Vektors wird ähnlich der Erstellung eines Objekttyps durchgeführt. Der einzige Unterschied ist, dass anstelle der CREATE OBJECT TYPE-Anweisung die CREATE VARRAY TYPE-Anweisung verwendet wird. Beispiel 2.9 zeigt die Verwendung der CREATE VARRAY TYPE-Anweisung. Beispiel 2.9 CREATE TYPE char_varray AS VARRAY(6) OF CHAR(30); CREATE TABLE firma (name CHAR(30), orte char_varray );
In Beispiel 2.9 wird zuerst ein Vektor namens char_varray mit Hilfe der CREATE VARRAY TYPE-Anweisung erstellt. Dieser Typ wird dann in der zweiten SQLAnweisung (CREATE TABLE) für die Definition der Spalte orte verwendet.
Objektorientierte Erweiterungen bei Oracle
47
Die Tabelle firma ist leer. Um einzelne Reihen in diese Tabelle einzufügen wird die INSERT-Anweisung mit den Objektkonstruktoren verwendet, die wir schon in Beispiel 2.3 benutzt haben. Beispiel 2.10 zeigt, wie Reihen in die Tabelle firma eingefügt werden können. Beispiel 2.10 INSERT INTO firma VALUES ('BMW', char_varray('München','Ingolstadt','Übersee')); INSERT INTO firma VALUES ('Audi', char_varray('Stuttgart','Dingolfing','Indien')); INSERT INTO firma VALUES ('Mitsubishi', char_varray('Tokio','London','Hamburg'));
Wie aus Beispiel 2.10 ersichtlich, werden die Elemente der VARRAY-Spalte mit Hilfe des Konstruktors char_varray eingefügt. Eine Abfrage auf die Spalte vom Typ VARRAY wird auf übliche Art und Weise durchgeführt. Beispiel 2.11 zeigt die Abfrage auf die Tabelle verzeichnis. Beispiel 2.11 SELECT f.name,f.orte FROM firma f
Ein einzelnes Element eines Vektors kann bei Oracle nicht direkt (d.h. mit Hilfe der interaktiven SQL-Sprache) abgefragt werden. Dies kann nur mit Hilfe der existierenden Programmierschnittstellen durchgeführt werden (siehe Beispiel 5.2). Beispiel 2.12 zeigt die Änderung einer VARRAY-Spalte. Beispiel 2.12 UPDATE firma SET orte = char_varray('Tokio', 'London', 'Hamburg', 'Paris') WHERE name = 'Mitsubishi'
In Beispiel 2.12 wird die Anzahl der Standorte des Autoherstellers Mitsubishi um einen neuen (Paris) erweitert. Die UPDATE-Anweisung verwendet den Objektkonstruktor, um den neuen Standort (zusammen mit der Liste aller existierenden Standorte) anzugeben.
Geschachtelte Tabellen Eine geschachtelte Tabelle stellt genau das dar, was ihr Name sagt, nämlich die Schachtelung einer Tabelle innerhalb einer anderen. Damit ist die geschachtelte Tabelle eine Kollektion, deren Elemente Tabellen sind. Die geschachtelten Tabellen werden mit Hilfe der herkömmlichen CREATE TABLEAnweisung erstellt, wobei diese Anweisung als Erweiterung die NESTED TABLE-
48
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
Klausel enthalten kann. Mit dieser Klausel kann die geschachtelte Tabelle separat, d.h. physikalisch getrennt von der Basistabelle gespeichert werden. Die Syntax der NESTED TABLE-Klausel sieht folgendermaßen aus: NESTED TABLE spalten_name STORE AS tabellen_name
Beispiel 2.13 zeigt die Erstellung einer geschachtelten Tabelle. Beispiel 2.13 CREATE TYPE verwandte_t AS TABLE OF person_t; CREATE TABLE arbeiter (arb_nr INT, personalien person_t, verwandte verwandte_t, PRIMARY KEY(arb_nr)) NESTED TABLE verwandte STORE AS ver_tabelle;
In Beispiel 2.13 wird zuerst eine Objekttabelle namens verwandte_t erstellt. Diese Tabelle wird als Typ der Spalte verwandte in der Tabelle arbeiter verwendet. Damit enthält die Spalte verwandte alle Verwandten eines Arbeiters in der Tabellenform. Die Tabelle arbeiter ist leer. Um die Reihen in diese Tabelle einzufügen, muss die INSERT-Anweisung, die in Beispiel 2.14 angegeben ist, verwendet werden. Beispiel 2.14 INSERT INTO arbeiter VALUES(12345,person_t(123,name_t('Mueller','Hans'),34), verwandte_t(person_t(124, name_t('Mueller','Franz'), 14), person_t(125, name_t('Mueller','Christine'), 29))) INSERT INTO arbeiter VALUES (23451, person_t(231, name_t('Schneider','Georg'), 48), verwandte_t(person_t(232, name_t('Schindler','Manuela'), 56), person_t(233, name_t('Buchauer','Hubert'), 42))) INSERT INTO arbeiter VALUES (34512, person_t(312, name_t('Huber','Helga'), 68), verwandte_t(person_t(313, name_t('Schuhmann','Erik'), 26), person_t(314, name_t('Metzger','Daniela'), 57)))
Um die Daten aller Verwandten eines Arbeiters einzufügen, wird in Beispiel 2.14 der Typkonstruktor verwandte_t in jeder INSERT-Anweisung einmal verwendet. Nach der Angabe dieses Konstruktors wird für jeden einzelnen Verwandten der Typkonstruktor person_t benutzt.
Objektorientierte Erweiterungen bei Oracle
49
Tabelle 2.2 zeigt den Inhalt der Tabelle arbeiter nach der Ausführung des Beispiels 2.14. arb_nr
personalien
verwandte
123, (Mueller,Hans), 34
124, (Muller, Franz), 14
12345 34512
125, (Mueller, Christine), 29 312, (Huber, Helga), 68
313, (Schuhmann, Erik), 26 314, (Metzger, Daniela), 57
Tabelle 2.2: Instanzen der Tabelle arbeiter mit der geschachtelten Tabelle verwandte
Beispiel 2.15 zeigt, wie man mit einer INSERT-Anweisung Reihen der geschachtelten Tabelle einfügen kann. Beispiel 2.15 INSERT INTO TABLE (SELECT a.verwandte FROM arbeiter a WHERE a.arb_nr = '23451') VALUES (person_t(313, name_t('Schuhmann','Erik'), 26))
INSERT in Beispiel 2.15 stellt eine Erweiterung der Grundform dieser Anweisung dar. Anstelle der Tabellennamen erscheint in dem obigen Beispiel eine SELECT-Anweisung, die die geschachtelte Tabelle (d.h. die Verwandten) des Mitarbeiters mit der Personalnummer 23451 wählt. Nach der Festlegung der geschachtelten Tabelle wird dieser eine neue Reihe: (313, name_t('Schuhmann','Erik'), 26)
hinzugefügt. Beispiel 2.16 zeigt eine Abfrage auf die geschachtelte Tabelle. Beispiel 2.16 SELECT verwandte FROM arbeiter WHERE arb_nr = 23451
Diese Abfrage listet eine Zeile für den Mitarbeiter mit der Personalnummer 23451 auf, mit allen seinen Verwandten, die in einer zusätzlichen Spalte als skalarer Wert erscheinen. Beispiel 2.17 zeigt eine weitere Form der Abfrage auf geschachtelten Tabellen.
50
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
Beispiel 2.17 SELECT arb_nr FROM arbeiter a WHERE 3 > (SELECT COUNT(*) FROM TABLE(a.verwandte));
In Beispiel 2.17 erscheint die geschachtelte Tabelle in der WHERE-Klausel der SELECTAnweisung. Aus diesem Grund muss man dem System mitteilen, dass es sich um eine Tabelle handelt. Dies wird bei Oracle mit dem Schlüsselwort TABLE erreicht, das vor dem Namen der geschachtelten Tabelle geschrieben wird. (Ohne diese Umwandlung würde der Wert a.verwandte als skalarer Wert (und nicht als geschachtelte Tabelle) betrachtet.) Das folgende Beispiel zeigt die Änderung der geschachtelten Tabelle. Beispiel 2.18 UPDATE TABLE(SELECT verwandte FROM arbeiter WHERE arb_nr = 12345) v SET v.name.vorname = 'Helga' WHERE v.person_id = 314;
Beispiel 2.18 verwendet die UPDATE-Anweisung, um die Werte in der geschachtelten Tabelle zu ändern. Für diesen Fall wird, genauso wie in Beispiel 2.17, das Schlüsselwort TABLE verwendet, um dem System mitzuteilen, dass die Änderung die geschachtelte und nicht die äußere Tabelle (arbeiter) betrifft.
2.1.3 Der Datentyp REF Instanzen, die in einer Objekttabelle erscheinen, werden Reiheninstanzen (row objects) genannt. Genauso werden Instanzen, die als Werte einer Spalte existieren, Spalteninstanzen (column objects) genannt. Damit ist z.B. die Instanz (123456789, name_t('Lang', 'Anne'), 32)
in der SET-Klausel des Beispiels 2.8 eine Reiheninstanz, während 'Lang', 'Anne'
eine Spalteninstanz der obigen Reiheninstanz darstellt. Jede Reiheninstanz bei Oracle hat einen Identifikator (object identifier), mit dem diese eindeutig identifiziert wird. Ein solcher Objektidentifikator kann verwendet werden, um einer Tabellenspalte eine ganze Instanz einer anderen (oder derselben) Tabelle zuzuweisen. Dies wird mit Hilfe des REF-Datentyps durchgeführt. Damit ist REF ein Zeiger, der auf den Objektidentifikator einer Reiheninstanz zeigt. Der REF-Datentyp findet in der Praxis u.a. Anwendung in folgenden Bereichen: - für die Definition einer (rekursiven) Klasse-Komponentenklasse-Beziehung - um (ineffiziente) Join-Operationen zu vermeiden.
Objektorientierte Erweiterungen bei Oracle
51
Die beiden folgenden Unterabschnitte beschreiben die obigen Anwendungen des REFDatentyps.
Die Definition einer Klasse-Komponentenklasse-Beziehung - Eine Klasse-Komponentenklasse-Beziehung ist eine spezielle Form allgemeiner Beziehungen zwischen zwei Klassen, wo eine Klasse (oder ihre Instanzen) als Datentyp von Spalten einer anderen Klasse erscheinen. Beispiel 2.19 zeigt eine Klasse-Komponentenklasse-Beziehung. Beispiel 2.19 BUCH(Isbn:CHAR(13), Buchname:CHAR(50),Autor:CHAR(30), Verlag:VERLAG)
In Beispiel 2.19 ist eine Klasse namens BUCH dargestellt. Diese Klasse hat mehrere Attribute, von welchen die ersten drei mit Hilfe des Standarddatentyps CHARACTER definiert sind. Das letzte Attribut – Verlag – ist mit Hilfe einer anderen Klasse namens VERLAG definiert. Das heißt, dass eine (oder eventuell mehrere) Instanzen dieser Klasse benutzt werden, um das Attribut Verlag zu definieren. In diesem Fall wird die Klasse VERLAG Komponentenklasse genannt, weil ihre Instanzen als Spaltendefinition einer anderen Klasse dienen. Eine Klasse-Komponentenklasse-Beziehung kann auch rekursiv sein, falls Klasse und ihre Komponentenklasse identisch sind. Beispiel 2.20 zeigt eine rekursive Klasse-Komponentenklasse-Beziehung. Beispiel 2.20 CREATE TYPE kurs_t AS OBJECT (titel CHAR(25), inhalt CHAR(120),voraussetzung REF(kurs_t),dauer INT); CREATE TABLE kurs OF kurs_t;
Die Spalte voraussetzung der Klasse kurs_t in Beispiel 2.20 ist gleichzeitig eine Instanz derselben Klasse. (Man kann sich das so vorstellen, dass z.B. die Voraussetzung für den Kurs »Datenbanken« die Instanz eines Kurses namens »Grundlagen der Informatik« ist.)
Ersetzung der Join-Operation mit Hilfe des REF-Datentyps Die zweite praktische Anwendung des REF-Datentyps – die Ersetzung des Join-Operators mit Hilfe des REF-Datentyps – werden wir mit Hilfe der modifizierten Tabellen der Beispieldatenbank zeigen. Beispiel 2.21 zeigt dies. Beispiel 2.21 CREATE TYPE mitarbeiter_t AS OBJECT (m_nr int, m_name char(20), m_vorname char(20), abt_nr char(20)); CREATE TYPE projekt_t AS OBJECT
52
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
(pr_nr char(4), pr_name char(20), standort char(25)); CREATE TYPE arbeiten_t AS OBJECT (m_nr INT, pr_nr CHAR(4), aufgabe CHAR(25), einst_dat DATE, arb_mit REF mitarbeiter_t, arb_pro REF projekt_t;
Beispiel 2.22 CREATE TABLE mitarbeiter1 OF mitarbeiter_t; CREATE TABLE projekt1 OF projekt_t; CREATE TABLE arbeiten1 OF arbeiten_t;
In Beispiel 2.21 werden zuerst drei neue Typen (mitarbeiter_t, projekt_t und arbeiten_t) mit Hilfe der CREATE OBJECT TYPE-Anweisung erstellt. Auf der Basis dieser Objekttypen werden in Beispiel 2.22 die entsprechenden Tabellen (mitarbeiter1, projekt1, arbeiten1) erzeugt. Dabei haben die Tabellen mitarbeiter1 und projekt1 dieselbe Struktur (Tabellenschema), wie die Tabellen mitarbeiter und projekt in den Tabellen 1.2 bzw. 1.3. Die dritte Tabelle arbeiten1 unterscheidet sich von der in Kapitel 1 definierten Tabelle arbeiten, weil sie zwei zusätzliche Spalten – arb_mit und arb_pro – enthält, die für die Referenzierung je einer Reihe der Tabellen mitarbeiter1 und projekt1 (in dieser Reihenfolge) dienen. Folgendes Beispiel zeigt das Einfügen der Spaltenwerte in die Tabellen mitarbeiter1, projekt1 und arbeiten1. Beispiel 2.23 insert into mitarbeiter1(m_nr, m_name, m_vorname, abt_nr) select * from mitarbeiter; insert into projekt1(pr_nr, pr_name, mittel) select * from projekt; insert into arbeiten1(m_nr,pr_nr,aufgabe,einst_dat,arb_mit, arb_pro) select a.m_nr,a.pr_nr,a.aufgabe, a.einst_dat, ref(m), ref(p) from arbeiten a, projekt1 p, mitarbeiter1 m where a.m_nr = m.m_nr and p.pr_nr = a.pr_nr"
Die ersten beiden INSERT-Anweisungen verwenden die allgemein bekannte INSERTAnweisung mit SELECT (siehe Beispiel 1.10), um Reihen der Tabelle mitarbeiter1 einzufügen. Die dritte INSERT-Anweisung fügt die Werte der Spalten arb_mit und arb_pro mit Hilfe von Referenzen ein, die auf einzelne Reihen der Tabellen projekt1 und mitarbeiter1 zeigen. Durch die Angabe dieser zusätzlichen Spalten in der Tabelle arbeiten1 ist es möglich, eine Verknüpfung der Tabellen mit dem Join-Operator zu ersetzen. Folgendes Beispiel zeigt eine SELECT-Anweisung mit zwei Join-Operationen und die äquivalente Abfrage mit Hilfe des REF-Typs.
Objektorientierte Erweiterungen bei Informix
53
Beispiel 2.24 Finden Sie die Mitarbeiteraufgabe, den Mitarbeiternamen und den Standort des Projektes, in dem Mitarbeiter arbeiten, die nach dem 1.1.1988 in ihrem Projekt eingestellt wurden. SELECT arbeiten.aufgabe, mitarbeiter.m_name, projekt.mittel FROM mitarbeiter, arbeiten, projekt WHERE mitarbeiter.m_nr = arbeiten.m_nr AND arbeiten.pr_nr = projekt.pr_nr AND einst_dat > '1.1.1988';
Beispiel 2.25 ist dem Beispiel 2.24 identisch, verwendet aber die Spalten arb_mit und arb_pro, um die Join-Operationen in Beispiel 2.24 zu ersetzen. Beispiel 2.25 SELECT a.aufgabe, a.arb_mit.m_name, a.arb_pro.mittel FROM arbeiten1 a WHERE einst_dat > '1.1.1988';
Die Abfrage in Beispiel 2.24 verwendet die übliche Join-Operation, um die drei Tabellen (mitarbeiter, arbeiten, projekt) miteinander zu verknüpfen. Die Abfrage in Beispiel 2.25 benutzt die zwei in Beispiel 2.21 definierten Spalten (arb_mit und arb_pro), um die Join-Operationen mit Hilfe des REF-Operators zu ersetzen. Dies ist möglich, weil die Spalte arb_mit der Tabelle arbeiten1 eine Instanz der Tabelle mitarbeiter1 referenziert. Dasselbe gilt für die Spalte arb_pro, die eine Instanz der Tabelle projekt1 referenziert.
2.2 Objektorientierte Erweiterungen bei Informix Objektorientierte Erweiterungen bei Informix haben ihren Ursprung in einem anderen objektrelationalen System namens Illustra. Illustra wurde von der Firma Informix aufgekauft, und die meisten objektorientierten Konzepte, die in Illustra implementiert waren, wurden in den Datenbank-Server von Informix übernommen. In diesem Buch werden wir folgende Informix-Erweiterungen in Bezug auf Objektorientierung beschreiben: - benutzerdefinierte Datentypen - Reihentypen - Kollektionen - Typ- und Tabellenhierarchien. Die folgenden Abschnitte beschreiben diese objektorientierten Konzepte.
54
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
2.2.1 Benutzerdefinierte Datentypen Der Datenbank-Server von Informix unterstützt folgende benutzerdefinierte Datentypen: - OPAQUE - DISTINCT. Im Allgemeinen kann man sagen, dass der OPAQUE- und DISTINCT-Datentyp eine größere Flexibilität bei der Speicherung und Verwaltung der Datentypen ermöglichen.
Der OPAQUE-Datentyp Der OPAQUE-Datentyp entspricht einem abstrakten Datentyp (ADT), d.h. einem benutzerdefinierten Typ beliebiger Komplexität. Mit der CREATE OPAQUE TYPEAnweisung kann ein vom Benutzer definierter Basisdatentyp erstellt werden. Ein solcher Basisdatentyp kann danach genauso wie jeder andere vom Datenbanksystem unterstützte Datentyp verwendet werden. Die Syntax der CREATE OPAQUE TYPE-Anweisung lautet: CREATE OPAQUE TYPE typ_name (INTERNALLENGTH = konst |VARIABLE,[angabe_liste]);
typ_name kennzeichnet den Namen des zu erstellenden Typs. Die INTERNALLENGTH-Angabe legt die Anzahl von Bytes fest, die für die Darstellung eines Wertes des definierten Typs notwendig sind. Die interne Länge kann entweder fest oder variabel sein. Falls es sich um eine feste Länge handelt, wird eine Konstante (konst) zugewiesen, während VARIABLE die unterschiedlichen Längen einzelner Instanzen (Werte) des neuen Datentyps kennzeichnet. angabe_liste ist eine optionale Liste mit diversen unterschiedlichen Optionen. Die Erstellung eines neuen Datentyps mit Hilfe der CREATE OPAQUE TYPE-Anweisung bedeutet nur den ersten Schritt bei seiner Definition. Nach der Erstellung des Datentyps müssen alle notwendigen Umwandlungsfunktionen, die das interne Format des Typs in das Benutzerformat (und umgekehrt) konvertieren, definiert werden. Erst nach der Definition dieser Funktionen kann ein abstrakter Datentyp sinnvoll in der Praxis verwendet werden. Zusätzlich zu der Definition von Funktionen ist es auch notwendig, die Umwandlung des neudefinierten Datentyps in einen anderen existierenden Datentyp zu gewährleisten. Diese Umwandlung wird mit der CREATE CAST-Anweisung durchgeführt. Beispiel 2.26 zeigt die Erstellung eines abstrakten Datentyps mit den dazugehörigen Funktionen.
Objektorientierte Erweiterungen bei Informix
55
Beispiel 2.26 CREATE OPAQUE TYPE rgba (INTERNALLENGTH = 16, ALIGNMENT = 4 ); CREATE FUNCTION rgbaIn (LVARCHAR) RETURNS rgba EXTERNAL NAME $INFORMIXDIR/extend/rgba.1.0/rgba.bld(rgbaInput)" LANGUAGE c; CREATE IMPLICIT CAST (LVARCHAR AS rgba WITH rgbaIn); CREATE FUNCTION rgbaOut (rgba) RETURNS LVARCHAR EXTERNAL NAME $INFORMIXDIR/extend/rgba.1.0/rgba.bld(rgbaOutput)" LANGUAGE c; CREATE CAST (rgba AS LVARCHAR WITH rgbaOut);
Beispiel 2.26 zeigt einen Ausschnitt aus der Skriptdatei, mit der die Erstellung eines kleinen Moduls (rgba) durchgeführt wird. Zuerst wird der neue abstrakte Datentyp rgba mit Hilfe der CREATE OPAQUE TYPE-Anweisung erstellt. Die Funktion rgbaIn ist eine Umwandlungsfunktion (cast-Funktion), die das Benutzerformat in das interne Format des neuen Datentyps umwandelt. Diese Funktion wird in der anschließenden CREATE CAST-Anweisung für die Erstellung einer impliziten Umwandlung zwischen dem abstrakten Datentyp und dem Datentyp LVARCHAR verwendet. Die zweite Funktion rgbaOut ist genauso eine Umwandlungsfunktion, die das interne Format des neuen Datentyps in das Benutzerformat umwandelt.
Der DISTINCT-Datentyp Der DISTINCT-Datentyp erlaubt die Definition eines neuen Typs auf der Basis eines existierenden Datentyps. Mit der CREATE DISTINCT TYPE-Anweisung wird ein neuer DISTINCT-Datentyp erstellt. Diese Anweisung hat folgende Syntax: CREATE DISTINCT TYPE typ_name AS quell_typ
typ_name kennzeichnet den Namen des neuen Datentyps. quell_typ ist der Name eines existierenden Datentyps, der als Quelle für den neuen Datentyp dient. quell_typ kann jeder von Informix unterstützte Datentyp (Standard oder benutzerdefiniert) sein. Beispiel 2.27 zeigt die Erstellung eines DISTINCT-Datentyps. Beispiel 2.27 CREATE DISTINCT TYPE alter AS INTEGER;
56
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
In Beispiel 2.27 wurde ein DISTINCT-Datentyp namens alter auf der Basis des Standarddatentyps INTEGER erstellt. Obwohl ein neuerstellter DISTINCT-Datentyp dieselbe Darstellung wie der darunterliegende Datentyp hat, können die beiden Datentypen nicht direkt miteinander verglichen werden. Die einzige Alternative ist die Verwendung der Umwandlungsfunktion cast. Für den in Beispiel 2.27 erstellten Datentyp alter heißt das, dass der Vergleich mit den Ganzzahlen direkt nicht möglich ist. Jedesmal, wenn ein DISTINCT-Datentyp erstellt wird, werden implizit vom InformixSystem zwei Umwandlungsfunktionen erstellt: Eine vom DISTINCT- zum Quelldatentyp und umgekehrt. Alle für den Quelldatentyp definierten Funktionen und Umwandlungsfunktionen können auf den entsprechenden DISTINCT-Datentyp angewendet werden. Im Gegensatz dazu gelten alle neudefinierten Funktionen für den DISTINCTDatentyp nicht für den entsprechenden Quelldatentyp.
2.2.2 Der Reihentyp Eine der wichtigsten Eigenschaften des relationalen Datenmodells ist, dass jeder einzelne Datenwert innerhalb einer Tabelle durch einen atomaren Wert dargestellt ist (siehe Eigenschaften des relationalen Datenmodels in Kapitel 1). Diese Eigenschaft gilt nicht für objektrelationale Datenbanksysteme wie Oracle und Informix, weil diese innerhalb einer Spalte und einer Reihe mehrwertige Attribute bzw. ganze Tabellen speichern können. Dementsprechend ist eine Tabelle bei Informix eine Sammlung von Spalten, die entweder einen Standard- oder einen benutzerdefinierten Datentyp haben. Ein Reihentyp bei Informix fasst mehrere Komponenten (Elemente) unterschiedlicher Datentypen zusammen. Ein Reihentyp kann sowohl einer Tabelle als auch einer einzigen Spalte zugewiesen werden. Falls ein Reihentyp einer Tabelle zugewiesen wird, definiert dieser die Schablone, nach der die Tabelle erstellt wird. Im Falle einer Spalte bedeutet eine solche Zuweisung, dass die Spalte aus mehreren unterschiedlichen Teilen zusammengesetzt (nicht atomar) ist. Hinweis
Der Reihentyp bei Informix entspricht dem Objekttyp bei Oracle. Jeder Reihentyp kann: - benannt (named) - namenlos (unnamed) sein. Die Erstellung eines Reihentyps hängt davon ab, ob es sich um einen benannten oder einen namenlosen Datentyp handelt.
Objektorientierte Erweiterungen bei Informix
57
Mit der CREATE ROW TYPE-Anweisung wird ein benannter Reihentyp erstellt. Ein benannter Reihentyp hat immer einen innerhalb der aktuellen Datenbank eindeutigen Namen. Bei der Erstellung eines benannten Reihentyps werden immer sein Name und die Namen und Datentypen der zu ihm gehörenden Spalten festgelegt. Die CREATE ROW-Anweisung hat folgende Syntax: CREATE ROW TYPE typ_name (spalte1 typ1 [NOT NULL] [{, spalte2 typ2 [NOT NULL]}...]) [UNDER supertyp];
typ_name ist der Name des neuerstellten benannten Datentyps. Der benannte Datentyp hat eine oder mehrere Spalten. Jede Spalte wird mit Hilfe eines Standard- oder eines benutzerdefinierten Datentyps definiert. Die optionale Angabe UNDER ermöglicht die Definition des neuen Datentyps innerhalb einer Typhierarchie, wobei supertyp der Name eines existierenden Typs ist, der sich in der Hierarchie direkt über dem neu definierten Reihentyp befindet. (Zur Definition der Typhierarchie und der Vererbung siehe Abschnitt 2.2.4.) In Bezug auf eine Spalte kann ein benannter Reihentyp als Datentyp verwendet werden, falls die Spalte ein zusammengesetztes Attribut ist. Mit Hilfe des definierten benannten Reihentyps kann das System sowohl auf die ganze Spalte als auch auf ihre einzelnen Komponenten zugreifen. Beispiel 2.28 zeigt die Erstellung eines benannten Reihentyps. Beispiel 2.28 CREATE ROW TYPE name_t (nachname CHAR(20) NOT NULL, vorname CHAR(20));
Beispiel 2.29 zeigt die Erstellung eines weiteren Reihentyps, der später in diesem Kapitel verwendet wird. Beispiel 2.29 CREATE ROW TYPE adresse_t (stadt CHAR(25) NOT NULL, strasse CHAR(20) NOT NULL, hausnr INTEGER NOT NULL);
In den Beispielen 2.28 und 2.29 sind zwei benannte Reihentypen – name_t und adresse_t – erstellt worden. Diese beiden Reihentypen können jetzt wie jeder andere existierende Datentyp einer Spalte zugewiesen werden (siehe nächstes Beispiel).
58
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
Zwei benannte Reihentypen können nicht identisch sein, sogar wenn sie identische Strukturen (Typschemata) aufweisen. Dies liegt daran, dass sie unterschiedliche Namen haben. Im Unterschied dazu wird ein namenloser Reihentyp durch seine Struktur definiert. Damit sind zwei namenlose Reihentypen immer identisch, falls sie dieselbe Struktur aufweisen. Mit Hilfe des CAST-Operators ist es trotzdem möglich, einen benannten Reihentyp in einen anderen benannten Reihentyp umzuwandeln. Eine Tabelle kann typisiert oder nicht typisiert sein. Jede typisierte Tabelle wird mit Hilfe eines benannten Reihentyps definiert. Eine nicht typisierte Tabelle ist diejenige Tabelle, die nicht auf einem entsprechenden Typ basiert. Die CREATE TABLE-Anweisung, mit der eine typisierte Tabelle erstellt wird, enthält die Angabe des benannten Reihentyps, der als Schablone für ihre Erzeugung verwendet wird. Eine Spalte eines benannten Reihentyps darf bei Informix nur die NULL bzw. NOT NULL-Angabe als Integritätsregel enthalten. Alle anderen Integritätsregeln dürfen ausschließlich für die entsprechende Tabelle angegeben werden. Beispiel 2.30 CREATE ROW TYPE firma_t (firmen_nr INTEGER NOT NULL, firmen_name CHAR(25) NOT NULL, adresse adresse_t); CREATE TABLE firma OF TYPE firma_t (PRIMARY KEY(firmen_nr));
In Beispiel 2.30 wird zuerst ein benannter Reihentyp (firma_t) definiert. firma_t hat als Komponente einen anderen Reihentyp: adresse_t (Beispiel 2.29), der für die Definition der Spalte adresse verwendet wird. Anschließend wird eine entsprechende Tabelle (firma) erstellt. Diese typisierte Tabelle hat dasselbe Schema wie der entsprechende Reihentyp. Ein namenloser Reihentyp kennzeichnet eine Gruppe von Feldern, die unterschiedliche Datentypen haben können. Im Unterschied zum benannten Reihentyp, der als Datentyp einer Spalte oder als Schablone einer Tabelle verwendet wird, kann der namenlose Reihentyp ausschließlich für die Definition einer Spalte verwendet werden. Ein namenloser Reihentyp kann mit Hilfe des ROW-Konstruktors erstellt werden. Beispiel 2.31 zeigt die Verwendung eines namenlosen Reihentyps. Beispiel 2.31 CREATE TABLE student (matrikel_nr INTEGER NOT NULL PRIMARY KEY, name ROW(nachname CHAR(20), vorname CHAR(20)));
In Beispiel 2.31 enthält die Tabelle student u.a. eine Spalte name, die als namenloser Reihentyp (mit den Feldern nachname und vorname) definiert ist.
Objektorientierte Erweiterungen bei Informix
59
Beim Einfügen der Werte eines Reihentyps kann sowohl ein benannter als auch ein namenloser Reihentyp verwendet werden. Der Bezug auf den darunterliegenden Reihentyp kann durch die Angabe des CAST-Operators »::« gemacht werden, dem dann der Reihentyp folgt. Beispiel 2.31 zeigt eine INSERT-Anweisung, die einen Reihentyp einfügt. Beispiel 2.32 INSERT INTO firma VALUES (351886, 'BMW', ROW ('München', 'Karlstr. ', 4) ::adresse_t);
Die INSERT-Anweisung in Beispiel 2.32 verwendet den CAST-Operator »::«, um den Bezug zum Reihentyp adresse_t herzustellen. Wie wir schon erläutert haben, können Spalten einer Tabelle mit Hilfe eines benannten oder namenlosen Reihentyps definiert werden. Die Abfragen auf eine so definierte Spalte entsprechen der Abfrage auf eine Spalte, die mit Hilfe eines Standarddatentyps definiert ist. Beispiel 2.33 zeigt eine solche Abfrage. Beispiel 2.33 SELECT * FROM firma;
Das Ergebnis ist: firmen_nr 351886 --------------------------------------------------------firmen_name BMW adresse ROW('München','Karlstr.',4)
Die Tabelle firma (Beispiel 2.32) enthält eine Reihe, die im Ergebnis dargestellt ist. Mit der SELECT-Anweisung ist es auch möglich, einzelne Felder einer zusammengesetzten Spalte abzufragen. In diesem Fall muss (wie bei Oracle) die Punktnotation (spalten_name.feld_name) verwendet werden. Beispiel 2.34 zeigt die Verwendung der Punktnotation. Beispiel 2.34 SELECT adresse.stadt, adresse.strasse FROM firma WHERE firmen_nr = 351886;
Das Ergebnis ist: stadt strasse -----------------------München Karlstr.
60
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
Die Punktnotation kann auch auf eine Spalte, die mit Hilfe des namenlosen Reihentyps definiert ist, angewendet werden. Beispiel 2.35 zeigt dies. Beispiel 2.35 SELECT student.matrikel_nr, student.name.nachname FROM student WHERE matrikel_nr = 12345;
Die SELECT-Anweisung in Beispiel 2.35 wählt eine einzige Reihe aus der Tabelle student (Beispiel 2.31) aus. Das Ergebnis könnte sein: matrikel_nr nachname ---------------------------12345 Grimm
Bei der Änderung einer Spalte, die als namenloser bzw. benannter Reihentyp definiert ist, müssen alle Werte explizit angegeben werden. Beispiel 2.36 UPDATE firma SET adresse = "ROW('München', 'Ottostr. ', 10)" WHERE firmen_nr = 351886;
Beispiel 2.36 zeigt die Änderung der Spalte adresse der Tabelle firma. Diese Spalte ist mit Hilfe eines benannten Reihentyps definiert. Um diese Spalte zu ändern, muss das Schlüsselwort ROW vor der Werteliste angegeben werden. Hinweis
Die Änderung einer Reihentypspalte kann mit der interaktiven front-end-Komponente von Informix nur so durchgeführt werden, dass die Werte aller Felder des Reihentyps geändert werden. Falls einzelne Werte modifiziert (eingefügt, geändert bzw. gelöscht) werden sollen, muss eine Programmierschnittstelle (SPL bzw. ESQL) verwendet werden.
2.2.3 Kollektionen Ein Kollektionsdatentyp enthält mehrere Elemente, die alle denselben Datentyp haben. Als solcher kann er als Datentyp einer Spalte zugewiesen werden. Jeder Kollektionsdatentyp hat zwei Merkmale: den Typkonstruktor und den Datentyp der Elemente.
Objektorientierte Erweiterungen bei Informix
61
Hinweis
Kollektionen haben gewisse Ähnlichkeiten mit den schon beschrieben Reihentypen. Im Unterschied zu den Reihentypen, die generell mehrere Elemente verschiedener Datentypen haben, enthalten Kollektionen immer Elemente desselben Datentyps. Zu den Kollektionen gehören generell: - Menge (set) - Multimenge (multiset bzw. bag) - Liste (list) - Vektor (array) - geschachtelte Tabellen (nested tables). Ein Mengenkonstruktor erzeugt aus mehreren Elementen eines zugrunde liegenden Typs einen neuen Typ. Die Elemente einer Menge haben keine Ordnung. Genauso enthält eine Menge keines der Elemente mehrfach. Ein Multimengenkonstruktor entspricht einem Mengenkonstruktor bis auf eine Ausnahme: die mehrfache Erscheinung einzelner Elemente ist erlaubt. Im Unterschied zu den Kollektionen Menge und Multimenge hat eine Liste geordnete Elemente und kann ein Element mehrfach enthalten. Damit existiert bei einer Liste das erste Element, das zweite usw., während bei Mengen und Multimengen solche Klassifizierung der Elemente nicht möglich ist. Ein Vektor (array) ist, bis auf einen Unterschied, identisch mit dem Kollektionstyp Liste. Im Unterschied zu Liste hat Vektor eine konstante Anzahl von Elementen. Eine geschachtelte Tabelle ist diejenige Tabelle, die in einer anderen Tabelle anstelle eines Datenwertes erscheint. (Die Unterstützung der geschachtelten Tabellen verletzt die erste Normalform des relationalen Datenmodells.) Der Typkonstruktor eines Kollektionsdatentyps bei Informix kann: - Menge (SET) - Multimenge (MULTISET) - Liste (LIST) sein. Eine Menge wird bei Informix mit Hilfe des Schlüsselwortes SET erstellt. Beispiel 2.37 zeigt zuerst die Erstellung eines Typs und danach die Erstellung einer Tabelle mit einer Spalte, die vom Typ SET ist.
62
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
Beispiel 2.37 CREATE ROW TYPE angestellter_t (ang_nr INTEGER NOT NULL, ang_name CHAR(20) NOT NULL, ang_vorname SET(CHAR(20) NOT NULL)); CREATE TABLE angestellter OF TYPE angestellter_t (PRIMARY KEY(ang_nr));
In Beispiel 2.37 ist ein benannter Reihentyp angestellter_t erstellt worden, dessen Spalte ang_vorname mit Hilfe des SET-Typs definiert ist. Das ist auch nahe liegend, weil alle Vornamen eines Mitarbeiters verschieden sein sollen. Der Typkonstruktor SET kann, genauso wie alle anderen Typkonstruktoren, für die Definition einer nicht typisierten Tabelle verwendet werden. Beispiel 2.38 CREATE TABLE konzern (konzern_name CHAR(30) NOT NULL PRIMARY KEY, standorte SET(CHAR(20) NOT NULL));
Eine Multimenge ist eine nichtgeordnete Sammlung von Elementen desselben Datentyps. (Jedes Element einer Multimenge kann mehrfach vorkommen.) Sie wird mit Hilfe des Schlüsselwortes MULTISET definiert. Beispiel 2.39 zeigt die Verwendung von MULTISET. Beispiel 2.39 CREATE ROW TYPE lehrer_t (lehrer_nr INTEGER NOT NULL, name name_t, adresse adresse_t, titel MULTISET(CHAR(8) NOT NULL)); CREATE TABLE lehrer OF TYPE lehrer_t (PRIMARY KEY(lehrer_nr));
In Beispiel 2.39 wurde der Typ lehrer_t erstellt, dessen erste beide Spalten mit Hilfe der schon existierenden benannten Reihentypen name_t und adresse_t (Beispiele 2.28 und 2.29) und dessen letzte Spalte mit Hilfe des Schlüsselwortes MULTISET definiert sind. Die Spalte titel ist als eine Multimenge definiert, weil ein Lehrer mehrere, eventuell sich wiederholende Titel (Prof. Dr. usw.) haben kann. Eine Liste ist eine geordnete Sammlung von Elementen desselben Datentyps, die auch mehrfach vorkommen können. Sie wird mit Hilfe des Schlüsselwortes LIST erstellt. Beispiel 2.40 zeigt die Verwendung dieses Kollektionstyps.
Objektorientierte Erweiterungen bei Informix
63
Beispiel 2.40 CREATE ROW TYPE vb_t (nummer INTEGER NOT NULL, name CHAR(20) NOT NULL, umsatz_pro_monat LIST(MONEY NOT NULL)); CREATE TABLE vb OF TYPE vb_t (PRIMARY KEY (nummer));
In Beispiel 2.40 wird ein benannter Reihentyp erstellt, der u.a. die Spalte umsatz_pro_monat enthält. Diese Spalte beinhaltet Umsätze jedes einzelnen Monats (Januar, Februar usw.) und muss deswegen geordnet, d.h. als Liste definiert werden. Komplexe Datentypen können geschachtelt sein. Es ist z.B. möglich, einen Kollektionsdatentyp zu definieren, dessen Werte einen Reihentyp oder wieder einen Kollektionsdatentyp enthalten. Genauso ist es möglich, dass ein Reihentyp einen oder mehrere Werte vom Typ SET, MULTISET oder LIST enthält. Beispiel 2.41 zeigt die Schachtelung von Typkonstruktoren. Beispiel 2.41 CREATE ROW TYPE diplomand_t (matrikel_nr INTEGER NOT NULL, name name_t, noten_liste LIST(ROW(fach CHAR(20), note INTEGER) NOT NULL)); CREATE TABLE diplomand OF TYPE diplomand_t (PRIMARY KEY(matrikel_nr));
Der Typ diplomand_t in Beispiel 2.41 beinhaltet u.a. die Spalte noten_liste, die als Liste aller Noten in der folgenden Form gespeichert wird: Mathematik Statistik Programmierung
1 3 2
Das folgende Beispiel zeigt, genauso wie das vorherige Beispiel, die Schachtelung von Typkonstruktoren. Beispiel 2.42 CREATE ROW TYPE buch_t (isbn CHAR(13) NOT NULL, autoren_liste LIST (ROW(name CHAR(20), vornamen SET(CHAR(20) NOT NULL)) NOT NULL), buch_titelCHAR(50)); CREATE TABLE buch OF TYPE buch_t (PRIMARY KEY (isbn));
Beispiel 2.42 beinhaltet u.a. die Spalte autoren_liste, die als Liste aller Autoren dargestellt ist. Jeder Autor ist mit Hilfe eines namenlosen Reihentyps definiert, der auch das Feld vornamen vom Typ SET enthält.
64
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
Beim Einfügen der Werte einer Spalte vom Typ SET, MULTISET und LIST müssen folgende syntaktische Regeln beachtet werden: - vor der Werteliste muss der entsprechende Konstruktor (SET, MULTISET oder LIST) stehen. - das Schlüsselwort SET (MULTISET bzw. LIST) muss, gemeinsam mit der Werteliste, innerhalb von Anführungszeichen geschrieben werden. Beispiel 2.43 zeigt das Einfügen einer Reihe in die Tabelle mit der MULTISET-Spalte. Beispiel 2.43 INSERT INTO lehrer VALUES (12345, ROW('Peters', 'Anna') ::name_t, ROW('Ulm', 'Karlstr. ', 14) ::adresse_t, "MULTISET{'Prof. ', 'Dr. ', 'Dr. '}");
In Beispiel 2.43 wird die typisierte Tabelle lehrer (Beispiel 2.39) verwendet. Die INSERT-Anweisung verwendet das Schlüsselwort MULTISET, um der Spalte titel Werte zuzuweisen. Beim Einfügen der Werte ist es auch möglich, eine leere Menge bzw. Multimenge anzugeben. Eine leere Menge enthält keine Elemente. Folgendes Beispiel zeigt das Einfügen einer leeren Multimenge. Beispiel 2.44 INSERT INTO lehrer VALUES (23456,ROW('Lotter', 'Wolfgang') ::name_t, ROW('München', 'Windeckstr', 19) ::adresse_t,"MULTISET{}");
Abfragen auf eine Spalte, die mit Hilfe eines Kollektionsdatentyps definiert ist, liefern alle Elemente einer Menge, Multimenge oder Liste. Beispiel 2.45 zeigt eine Abfrage auf eine Spalte, die vom Typ SET ist. Beispiel 2.45 SELECT ang_nr, ang_vorname FROM angestellter;
Das Ergebnis könnte folgendermaßen aussehen: ang_nr ang_vorname ang_nr ang_vorname
888 SET{'Anna', 'Maria', 'Petra'} 999 SET{'Peter', 'Klaus'}
Die Tabelle angestellter (Beispiel 2.37) enthält eine Spalte ang_vorname, die vom Typ SET ist. Wie aus dem Ergebnis des Beispiels 2.45 ersichtlich, enthält die Ausgabe einer
Objektorientierte Erweiterungen bei Informix
65
Spalte, die mit Hilfe eines Kollektionsdatentyps definiert ist, immer den Namen des Konstruktors (SET, MULTISET bzw. LIST). Die IN-Angabe kann ebenfalls verwendet werden, um festzustellen, ob eine Spalte vom Typ SET, MULTISET oder LIST einen gewissen Wert enthält. Beispiel 2.46 SELECT ang_nr, ang_name, ang_vorname FROM angestellter WHERE 'Petra' IN ang_vorname;
Das Ergebnis könnte folgendermaßen aussehen: ang_nr ang_vorname
888 SET{'Anna', 'Maria', 'Petra'}
Mit der SELECT-Anweisung in Beispiel 2.46 werden diejenigen Reihen der Tabelle angestellter ausgesucht, die den Vornamen Petra in der SET-Spalte ang_vorname enthalten. Obwohl die IN-Angabe die Einschränkung der Suche auf einen einzigen Wert der Spalte vom Typ SET, MULTISET oder LIST ermöglicht, enthält die Ausgabe immer alle Werte des entsprechenden Kollektionstyps. Der Datenbank-Server von Informix unterstützt die Funktion cardinality, mit der die Anzahl der Elemente eines Kollektionsdatentyps berechnet werden kann. Beispiel 2.47 zeigt die Anwendung der cardinality-Funktion. Beispiel 2.47 SELECT ang_nr, CARDINALITY(ang_vorname) anzahl_vornamen FROM angestellter;
Die SELECT-Anweisung in Beispiel 2.47 gibt die Personalnummer jedes Angestellten und die Anzahl seiner Vornamen. Bei der Änderung einer Spalte, die mit Hilfe eines Kollektionsdatentyps definiert wurde, gelten folgende Regeln: - alle Elemente müssen explizit angegeben werden, - alle Elemente müssen, gemeinsam mit dem Schlüsselwort SET (MULTISET bzw. LIST), innerhalb von Anführungszeichen geschrieben werden. Beispiel 2.48 UPDATE lehrer SET titel = "MULTISET{'Prof. ', 'Prof', 'Dr.', 'Dr.'}" WHERE lehrer_nr = 23456;
66
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
In Beispiel 2.48 werden alle Titel (alte und neue) des Lehrers mit der Nummer 23456 innerhalb der SET-Klausel angegeben. Dies ist unbedingt notwendig, weil das Einfügen einzelner Elemente mit den front-end-Komponenten von Informix nicht möglich ist.
2.2.4 Typ- und Tabellenhierarchien Der Datenbank-Server von Informix unterstützt noch ein weiteres objektorientiertes Konzept, nämlich Typ- bzw. Tabellenhierarchien. Im Allgemeinen kennzeichnet eine Objekthierarchie eine Sammlung von Objekten, in der ein Objekt entweder eine Wurzel, einen Knoten oder ein Blatt eines Baums darstellt. Dementsprechend kennzeichnet die Typhierarchie eine Sammlung von Datentypen, wobei jeder Datentyp in der existierenden Hierarchie entweder eine Wurzel oder ein Knoten oder ein Blatt ist. Bei einer Typhierarchie wird ein Typ T1 der Supertyp von T2 genannt, falls T2 in der Hierarchie direkt oder indirekt unter dem Typ T1 liegt. Genauso wird T2 der Subtyp von T1 genannt.
Vererbung Alle Hierarchien (Objekt-, Typ- und Tabellenhierarchien) unterstützen die Vererbung. Dieser Begriff bedeutet, dass alle Eigenschaften eines Objektes (Typs) wie Attribute bzw. dazugehörende Funktionen von allen seinen Unterobjekten (Subtypen) automatisch geerbt werden. Die Vererbung kann einfach oder mehrfach sein. Eine einfache Vererbung kennzeichnet die Eigenschaft einer Hierarchie, wo jeder Typ maximal einen Supertyp hat. Dementsprechend hat bei der mehrfachen Vererbung mindestens ein Typ der Hierarchie zwei oder mehrere Supertypen. (Informix unterstützt in der aktuellen Version die einfache Vererbung.)
Typhierarchien Typhierarchien können nur mit benannten Reihentypen erstellt werden. Beispiel 2.49 zeigt die Erstellung einer Typhierarchie für die Typen person_t, kurs_teiln_t, dozent_t und prof_t (siehe Abbildung 2.1). Beispiel 2.49 CREATE ROW TYPE person_t (name name_t, adresse adresse_t, telefon_nr INTEGER); CREATE ROW TYPE kurs_teiln_t (firma firma_t, funktion CHAR(20)) UNDER person_t; CREATE ROW TYPE dozent_t
Objektorientierte Erweiterungen bei Informix
67
(anzahl_stunden INTEGER) UNDER person_t; CREATE ROW TYPE prof_t (titel MULTISET(CHAR(8) NOT NULL)) UNDER dozent_t;
Wie aus Beispiel 2.49 ersichtlich, muss die Definition jedes Subtyps die Angabe UNDER enthalten. Die Definition eines Supertyps muss immer vor der Definition seiner Subtypen erfolgen. Abbildung 2.1 zeigt die in Beispiel 2.49 definierte Typhierarchie.
Abbildung 2.1: Typhierarchie des Beispiels 2.49
Durch die Vererbung enthält jeder Subtyp zusätzlich zu seinen eigenen Eigenschaften (Spalten und Routinen) auch die Eigenschaften aller seiner Supertypen. Damit enthält z.B. der Typ prof_t insgesamt fünf Attribute (Spalten): name, adresse, telefon_nr, anzahl_stunden und titel.
Tabellenhierarchien Eine Tabellenhierarchie kann nur erstellt werden, falls die entsprechende Typhierarchie existiert. Weil auf der anderen Seite eine Typhierarchie für sich wenig Sinn macht, empfiehlt es sich, die Typ- und die entsprechende Tabellenhierarchie gleich nacheinander zu erstellen. Jede Subtabelle muss genauso wie der entsprechende Subtyp die Angabe UNDER enthalten. Beispiel 2.50 erstellt die entsprechende Tabellenhierarchie für die in Beispiel 2.49 definierte Typhierarchie. Beispiel 2.50 CREATE CREATE CREATE CREATE
TABLE TABLE TABLE TABLE
person OF TYPE person_t; kurs_teiln OF TYPE kurs_teiln_t UNDER person; dozent OF TYPE dozent_t UNDER person; prof OF TYPE prof_t UNDER dozent;
68
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
In einer Tabellenhierarchie bei Informix erbt eine Subtabelle folgende Eigenschaften aller ihrer Supertabellen: - alle Spalten - alle definierten Integritätsregeln - alle definierten Zugriffsmethoden - Speicherangaben - Trigger - Indizes. Hinweis
Die erste Eigenschaft (Spalten) stellt eine Struktureigenschaft dar, während alle anderen in Bezug auf das Verhalten der Tabelle stehen. Beim Einfügen einer Reihe in eine Tabelle, die zu einer Tabellenhierarchie gehört, gibt es keine Besonderheiten. Die Reihe wird in jene Tabelle eingefügt, die in der INSERTAnweisung angegeben ist. Beispiel 2.51 zeigt das Einfügen der Reihen in der in Beispiel 2.50 definierten Tabellenhierarchie. Beispiel 2.51 INSERT INTO person (name, adresse, telefon_nr) VALUES ("ROW('Kurz', 'Ludwig')", "ROW('Rosenheim', 'Ottostr.', 14)", 50832); INSERT INTO kurs_teiln (name, adresse, telefon_nr, firma, funktion) VALUES ("ROW('Lang', 'Peter')", "ROW('Rosenheim', 'Ottostr.', 14),12345, ROW(12345, 'Kettner', ROW('Rosenheim', 'Kettnerstr.',7):: adresse_t )::firma_t,'Projektleiter'); INSERT INTO dozent(name, adresse, telefon_nr,anzahl_stunden) VALUES ("ROW('Müller', 'Ella')", "ROW('Rosenheim', 'Ottostr.', 14)", 53558, 35); INSERT INTO prof (name, adresse, telefon_nr, anzahl_stunden, titel) VALUES ("ROW('Miller', 'Anna')", "ROW('Rosenheim', 'Ottostr.', 14)", 55321, 18, "MULTISET{'Prof.','Dr.'}");
Eine Abfrage auf eine Supertabelle kann entweder auf die Supertabelle und alle ihre Subtabellen angewendet oder nur auf die Supertabelle eingeschränkt werden. Folgende Beispiele zeigen die beiden Alternativen.
Objektorientierte Erweiterungen bei DB2
69
Beispiel 2.52 SELECT name, telefon_nr FROM person;
Die SELECT-Anweisung in Beispiel 2.52 wählt alle Reihen aus der Tabelle person (Beispiel 2.50) und aus allen ihren Subtabellen aus. Die Abfrage liefert keine Spalten, die spezifisch für einzelne Subtabellen sind. Das Ergebnis einer Abfrage kann mit Hilfe der ONLY-Angabe auf Reihen der Supertabelle eingeschränkt werden. Beispiel 2.53 SELECT name, telefon_nr FROM ONLY(person);
Die Änderung einer Spalte einer Tabelle, die sich innerhalb einer Tabellenhierarchie befindet, beeinflusst Reihen dieser Tabelle und Reihen aller ihrer Subtabellen. Beispiel 2.54 zeigt das. Beispiel 2.54 UPDATE person SET adresse = "ROW('Ulm', 'Landstr. ', 1)" WHERE adresse = "ROW('Rosenheim', 'Ottostr. ', 14)";
In Beispiel 2.54 werden Reihen der Tabelle person und aller ihrer Subtabellen, in denen die Bedingung erfüllt ist, geändert. Die SET-Klausel der UPDATE-Angabe darf ausschließlich die Spalten, die sich in der genannten Supertabelle befinden, enthalten. Die Änderung einer Spalte kann mit Hilfe der ONLY-Angabe nur auf die Reihen der genannten Supertabelle eingeschränkt werden. Beispiel 2.55 UPDATE ONLY(person) SET adresse = ROW('Ulm', 'Landstr. ', 1)::adresse_t WHERE adresse = ROW('Rosenheim', 'Ottostr. ', 14)::adresse_t;
Mit der UPDATE-Anweisung in Beispiel 2.55 werden nur Reihen der Tabelle person, die die Bedingung erfüllen, geändert.
2.3 Objektorientierte Erweiterungen bei DB2 Die Firma IBM hat im Vergleich zu Informix und Oracle relativ spät angefangen, die objektorientierten Erweiterungen in ihren Datenbank-Server zu implementieren. Erst im Jahre 2000 sind in Version 7.1 einige Erweiterungen implementiert worden. Zu diesen gehören:
70
2
Objektorientierte Erweiterungen in relationalen DB-Systemen
- benutzerdefinierte Datentypen - der REF-Operator - Typ- und Tabellenhierarchien. DB2 unterstützt zwei benutzerdefinierte Datentypen: - DISTINCT-Datentyp - Objekttyp. Der DISTINCT-Datentyp entspricht genau dem gleichnamigen Datentyp bei Informix (siehe die Beschreibung des DISTINCT-Datentyps in Abschnitt 2.2.). Der Objekttyp entspricht dem Objekttyp bei Oracle, hat aber eine etwas andere Syntax. Beispiel 2.56 zeigt die Erstellung des Objekttyps name_t (Beispiel 2.1) mit DB2. Beispiel 2.56 CREATE TYPE Name_t AS (nachname VARCHAR(30), vorname VARCHAR(20) MODE DB2 SQL
Der REF-Datentyp hat dieselbe Syntax und Bedeutung wie der gleichnamige Datentyp bei Oracle, während die Tabellenhierarchien den Tabellenhierarchien bei Informix entsprechen mit wenig geänderter Syntax. Beispiel 2.57 zeigt die Erstellung der Typhierarchie entsprechend dem Beispiel 2.49, während Beispiel 2.58 die Erstellung der Tabellenhierarchie mit DB2 illustriert. Beispiel 2.57 CREATE TYPE person_t AS (name name_t, adresse adresse_t, telefon_nr INTEGER); MODE DB2 SQL CREATE TYPE kurs_teiln_t UNDER person AS (firma firma_t, funktion CHAR(20)) CREATE TYPE dozent_t UNDER person AS (anzahl_stunden INTEGER) CREATE TYPE prof_t UNDER person AS (titel MULTISET(CHAR(8) NOT NULL))
Beispiel 2.58 CREATE CREATE CREATE CREATE
TABLE TABLE TABLE TABLE
person OF person_t kurs_teiln OF kurs_teiln_t UNDER person dozent OF dozent_t UNDER person prof OF prof_t UNDER dozent
Zusammenfassung
71
Die ausführliche Erläuterung der oben genannten objektorientierten Erweiterungen (sowie manche andere Erweiterungen, die nicht aufgelistet sind), liegt außerhalb der Themen dieses Buches, weil zum Zeitpunkt des Schreibens die JDBC 2.0-Erweiterungen in DB2 noch nicht implementiert sind. (Die Firma IBM arbeitet gerade daran.)
2.4 Zusammenfassung In diesem Kapitel haben wir objektorientierte Erweiterungen dreier Datenbankhersteller: Oracle, Informix und IBM DB2 beschrieben. Zusammenfassend kann man sagen, dass jede Firma z. T. verschiedene Erweiterungen implementiert hat und, falls ein Konzept in mehreren Datenbank-Servern existiert, seine Syntax bei unterschiedlichen Herstellern verschieden ist. Von allen drei Herstellern hat Informix die meisten objektorientierten Erweiterungen implementiert, während die beiden anderen Hersteller (Oracle und IBM) standardisierte Erweiterungen (d.h. diejenigen Erweiterungen, die im SQL99-Standard beschrieben sind), enthalten. Das nächste Kapitel ist das letzte Kapitel des Einführungsteils. Es beschreibt die wichtigsten Teile der Java-Sprache, die wir im weiteren Verlauf des Buches benötigen werden.
3 Die Sprache Java eine Einführung Java ist eine objektorientierte Sprache, die von der Firma Sun Microsystems entworfen und implementiert wurde. Diese Sprache hat in den letzten Jahren an Bedeutung gewonnen, weil sie besonders für Internet-Anwendungen geeignet ist. Aus diesem Grund ist Java für die meisten Informatiker (und Nichtinformatiker) bekannt als Sprache, mit der Applets implementiert werden können. (Applets sind Mini-Anwendungen, die innerhalb einer Web-Seite ablaufen. Sie ermöglichen die Ausführung von Aufgaben auf einem Web-Browser und die Kommunikation mit den Benutzern, ohne dass die Ressourcen des Web-Servers nach dem Runterladen der notwendigen Information noch einmal in Anspruch genommen werden müssen.) Die wichtigste Eigenschaft von Java ist ihre Rechnerunabhängigkeit, was die Ausführung von Code auf entfernten Rechnern ermöglicht. Diese Eigenschaft ist die Grundlage für die Verwendung dieser Sprache im Internet bzw. in jeder verteilten Netzwerkumgebung. Obwohl Java meistens für verteilte Umgebungen verwendet wird, ist sie eine Programmiersprache, die für die Implementierung aller möglichen Anwendungen benutzt werden kann. Bei Java handelt sich um eine objektorientierte Sprache, die als Nachfolgesprache von C und C++ bekannt ist, gleichzeitig aber gewisse Mankos dieser Sprachen behebt.
3.1 Einleitung Java-Programme werden aus einer oder mehreren Klassen gebildet. Jede definierte Klasse ermöglicht die Erstellung einzelner Instanzen dieser Klasse. (Man kann sich eine Java-Klasse als eine Fabrik vorstellen, die als Produkte Instanzen dieser Klasse erzeugt.) Wie jede objektorientierte Sprache enthält eine Java-Klasse zwei Arten von Elementen: Attribute, die bei Java Felder (fields) genannt werden und Methoden (methods). Mit Hilfe eines kleinen Programms werden wir zeigen, wie eine Klasse in Java definiert wird.
74
3
Die Sprache Java eine Einführung
Beispiel 3.1 class Hallo { public static void main (String args[]) { System.out.println ("Hallo"); } }
In Beispiel 3.1 wurde eine Klasse namens Hallo definiert. (Java unterscheidet zwischen Groß- und Kleinbuchstaben. Das ist ein wichtiger Unterschied zu der SQL-Sprache, die solche Unterscheidung nicht macht.) Alle Elemente einer Klasse werden innerhalb eines geschweiften Klammernpaares angegeben. Die Klasse Hallo enthält eine Methode namens main() und keine Felder. Diese Methode hat einen Vektor namens args als einzigen Parameter, der vom Typ String ist. (Der Parameter der main()-Methode bei Java hat dieselbe syntaktische Form und Bedeutung wie der Parameter des main-Programms bei der Sprache C. Dementsprechend wird auch die main-Methode mit Hilfe des Datentyps void deklariert, was bedeutet, dass kein Rückgabewert geliefert wird.) Hinweis
Ein Java-Programm, das die main()-Methode enthält, läuft unabhängig von jedem Browser. Zur Ablaufzeit ruft die JVM (Java Virtual Machine) die main()-Methode auf, und gibt einen Fehler aus, falls sie nicht vorhanden ist. Die Methode main enthält eine einzige Anweisung: System.out.println (Hallo);
die die Methode println() aufruft. System ist eine (vordefinierte) Java-Klasse, die das Feld out beinhaltet. Das Feld out ist eine Instanz der Klasse Printstream, und dieser Typ enthält unter anderen Mehoden auch die Methode println(). (Wie aus der Definition der obigen Anweisung ersichtlich, wird eine Methode mit Hilfe der Punktnotation aufgerufen.) Die Methode println enthält eine alphanumerische Zeichenkette als Parameter, die am Bildschirm ausgegeben wird. Nachdem wir ein einfaches Java-Beispiel gezeigt und erläutert haben, werden wir zuerst nichtobjektorientierte Sprachelemente wie Variablen und Schleifenanweisungen der Java- Sprache erörtern.
3.2 Variablen, Konstanten und Datentypen Java unterscheidet zwischen den Standarddatentypen, die auch primitive Datentypen genannt werden und Instanzen einer Klasse. Der Unterschied zwischen den primitiven Typen und Instanzen einer Klasse ist, dass die primitiven Typen in der Sprache eingebaut sind, während Instanzen von Programmierern definiert sind.
Variablen, Konstanten und Datentypen
75
Die primitiven Datentypen sind: - boolean (entweder richtig oder falsch) - char (Unicode-Zeichen der Länge 16 Bit) - byte (Ganzzahlen der Länge 8 Bit) - short (Ganzzahlen der Länge16 Bit) - int (Ganzzahlen der Länge 32 Bit) - float (Gleitkommazahlen der Länge 32 Bit) - double (Gleitkommazahlen der Länge 64 Bit). Obwohl die primitiven Datentypen bei Java große Ähnlichkeiten mit den Datentypen bei C und C++ aufweisen, haben sie einen wichtigen Unterschied: Alle primitiven Typen haben eine invariante (feste) Länge, die nicht von den Eigenschaften der Hardware, auf dem das Programm abläuft, abhängig sind. Dies ist auch notwendig, damit Programme (von einem Rechner zu einem anderen) portierbar sind. Beispiel 3.2 zeigt einige Beispiele von Java-Variablen und Konstanten. Beispiel 3.2 int var1 = 1; /* Deklaration und Wertzuweisung einer Variablen*/ var1 ++; /* var1 = var1 + 1 */ var1 += 1; // var1 = var1 + 1 static final double = 3.1416; static final double a = 1;
Die erste Zeile des Beispiels 3.2 zeigt die Deklaration und Wertzuweisung einer JavaVariablen. Die zweite und dritte Zeile zeigen zusätzlich die verkürzten Schreibweisen, die verwendet werden können, um einer Variablen einen Wert dazu zu addieren. Beispiel 3.2 zeigt zusätzlich, welche Kommentare die Sprache Java unterstützt. Bei Java gibt es drei verschiedene Arten von Kommentaren. Neben den Zeichenpaaren »/*« und »*/«, die in Beispiel 3.2 verwendet wurden und den Kommentaren bei der Sprache C entsprechen, unterstützt Java auch das Zeichenpaar »//«: Jeder Text, der dem Kommentar »//« folgt, wird bis zum Zeilenende vom Übersetzer außer Acht gelassen. Der dritte Kommentartyp stellt den Dokumentationskommentar (doc comment) dar. Dokumentationskommentare erscheinen zwischen den Zeichenfolgen »/**« und »*/«. Sie werden meistens für die Beschreibung der Deklarationen verwendet. Das JavaWerkzeug namens javadoc extrahiert den Text zwischen den beiden Zeichenfolgen und generiert daraus ein HTML-Dokument.
76
3
Die Sprache Java eine Einführung
Konstanten in der Java-Sprache sind immer benannt. Benannte Konstanten haben zwei Vorteile im Vergleich zu den unbenannten: 1. durch die Benennung einer Konstante wird diese (durch ihren Namen) dokumentiert. 2. falls eine benannte Konstante geändert werden muss, erfolgt diese Änderung an einer einzigen Stelle (im Unterschied zu den unbenannten, die überall geändert werden müssen, wo sie vorkommen). Benannte Konstanten werden in Java als Variablen mit den Eigenschaften static und final deklariert. Die beiden letzten Reihen des Beispiels 3.2 zeigen zusätzlich zu der Definition zweier benannter Konstanten auch eine weitere Eigenschaft von Java, nämlich die Unterstützung von Unicode-Zeichen, wie z.B. (Pi). Unicode ist ein 16-Bit Zeichensatz, der vom Unicode-Konsortium entwickelt und vertrieben wird. Unicode umfasst fast alle Zeichen, die heutzutage weltweit für Rechner verwendet werden. Der Grund liegt darin, dass ein 16-Bit-Zeichensatz mehr als 64.000 Zeichen aufnehmen kann. Im Gegensatz zu den ersten Zeichensätzen, die nur eine begrenzte Anzahl von Zeichen aufnehmen konnten, stellt Unicode eine einheitliche Methode für die Aufnahme unterschiedlichster Zeichen dar. Das bedeutet, dass mit Unicode die Darstellung von Zeichen vieler Alphabete gleichzeitig in einem Dokument möglich ist.
3.2.1 Vektoren (Arrays) und Strings Ein Vektor (array) ist eine Kollektion mehrerer Elemente, die alle denselben Datentyp haben. Der Datentyp des Vektorelementes kann ein primitiver oder komplexer Typ sein. (Für die ausführliche Beschreibung von Vektoren siehe den Abschnitt »Kollektionen« in Kapitel 2.) Um einen Vektor zu definieren, wird der sogenannte Indexoperator »[]« verwendet. Beispiel 3.3 zeigt die Definition und die Initialisierung einiger Vektoren. Beispiel 3.3 int[] vektor1; int[] vektor2 = {1,2,3,4,5}; int[] v_neu; v_neu = vektor2; int[][] matrix = { // Initialisierung eines primitiven Typs {1, 2, 3,}, {4, 5, 6}, }; int [][][] dreidim = new int[2] [3] [4] //Initialisierung mit new
Schleifenanweisungen
77
In Beispiel 3.3 wird zuerst der Vektor vektor1 definiert. Bei der Definition eines Vektors erlaubt die Java-Sprache auch die folgende Notation: int vektor[];
Der Vektor vektor2 wird definiert und gleichzeitig initialisiert. Bei der Initialisierung der Vektors v_neu wird der Zeiger, der auf den Vektor vektor2 zeigt, kopiert. Die Anzahl der Elemente eines Vektors ist systemabhängig, d.h. der Anwender kann die Anzahl der Elemente eines Vektors nicht definieren. Aus diesem Grund haben alle Vektoren einen impliziten Wert – length –, der die Anzahl der Elemente enthält. Damit ist es für den Anwender möglich, die tatsächliche Anzahl der Elemente eines Vektors zu ermitteln. Die Sprache Java unterstützt sowohl eindimensionale als auch mehrdimensionale Vektoren. Mehrdimensionale Vektoren werden genauso wie die eindimensionalen definiert und initialisiert. In Beispiel 3.3 wird auch ein zweidimensionaler Vektor namens matrix definiert und initialisiert. Die letzte Zeile des Beispiels 3.3 zeigt die Initialisierung eines dreidimensionalen Vektors namens dreidim. Alle alphanumerischen Datentypen der SQL-Sprache werden auf den Java-Datentyp String abgebildet.
3.3 Schleifenanweisungen Java unterstützt folgende Schleifenanweisungen: - if/else - for - while bzw. do/while - switch Diese Anweisungen haben eine ähnliche Syntax und Bedeutung wie die gleichnamigen Anweisungen bei den Sprachen C bzw C++. Mit Hilfe eines Beispiels werden wir die beiden ersten Schleifenanweisungen (if und for) zeigen. Beispiel 3.4 class Fakultaeten //Klassennamen beginnen mit einem Großbuchstaben { // Berechnung der ersten Fakultäten // Das Ergebniswert wird markiert, falls gerade static final int MAX_WERT = 5; static final int null_faktor = 1; public static void main (String[] args) { int wert = 1; String kennzeichen;
78
3
Die Sprache Java eine Einführung
System.out.println ("0: " + null_faktor); //System Klasse im Paket java.lang //out-Feld der Klasse System und Instanz vom Typ: static Printstream //Typ Printstream enthält die Methode println for (int i = 1; i