Lade Inhalt...

Programmierung mit Servlets und Applets in Java

Am Beispiel der grafischen Darstellung von Prozessdaten

Diplomarbeit 2001 144 Seiten

Informatik - Software

Leseprobe

Inhaltsverzeichnis

1 Ziel der Studienarbeit

2 Java – Die Programmiersprache
2.1 Geschichte von Java
2.2 Merkmale der Programmiersprache
2.2.1 Portabilität
2.2.2 Objektorientierung
2.2.3 Multithreading
2.2.4 Verteilte Programmierung
2.2.5 Robustheit
2.2.6 Sicherheit
2.2.7 Garbage Collector
2.2.8 Unterschiede zu C++
2.3 Das objektorientierte Konzept
2.3.1 Einführung
2.3.2 Das Konzept
2.3.3 Abstraktion
2.3.4 Botschaften
2.3.5 Kapselung
2.3.6 Klassen und Exemplare
2.3.7 Vererbung und Taxonomie
2.3.8 Polymorphie
2.3.9 Überladung
2.4 Objektorientierte Programmierung mit Java
2.4.1 Die Java Virtual Machine (JVM)
2.4.2 Der Unicode-Zeichensatz
2.4.3 Klassen
2.4.3.1 Definition
2.4.3.2 Zugriffsrechte
2.4.3.3 Modifier
2.4.4 Exemplare
2.4.4.1 Instanzierung
2.4.4.2 Konstruktoren
2.4.4.3 Vererbung
2.4.5 Interfaces
2.4.5.1 Allgemeines
2.4.5.2 Beschreibung
2.4.5.3 Modifier
2.4.6 Datenelemente
2.4.6.1 Beschreibung
2.4.6.2 Zugriffsrechte
2.4.6.3 Modifier
2.4.7 Methoden
2.4.7.1 Methodenkopf und -rumpf
2.4.7.2 Lokale Variablen
2.4.7.3 Methodenaufruf
2.4.7.4 Methodenüberladung
2.4.7.5 Zugriffsrechte
2.4.7.6 Modifier
2.4.8 Pakete
2.4.8.1 Allgemeines
2.4.8.2 Importieren von Paketen und Klassen
2.4.9 Archive
2.4.9.1 Beschreibung
2.4.9.2 Archivarten

3 Java Database Connectivity (JDBC)
3.1 JDBC – Allgemeines
3.2 Die Struktur von JDBC
3.3 Treiber
3.3.1 Der Treibermanager
3.3.2 Treiberarten
3.3.3 Aufgaben des Treibers
3.3.4 Laden eines Treibers
3.4 Die Datenbankverbindung
3.4.1 Das Connection-Objekt
3.4.2 Der JDBC-URL
3.4.3 Schließen einer Verbindung
3.4.4 Die Schnittstelle PooledConnection
3.5 Escape-Klauseln
3.6 SQL-Anweisungen
3.6.1 Das Statement-Objekt
3.6.2 Die execute-Methoden
3.6.3 Ergebnisse von Anfragen
3.6.3.1 Die Schnittstelle Statement
3.6.3.2 Die Schnittstelle PreparedStatement
3.6.3.3 Die Schnittstelle CallableStatement
3.6.4 Abfragen mit den Methoden der Schnittstelle ResulSet
3.6.5 Abbildung von SQL-Typen
3.6.5.1 Typabbildungen
3.6.5.2 getXXX-Methoden
3.6.5.3 setXXX()-Methoden
3.7 Transaktionen in JDBC
3.8 Unterschiede der JDBC-Versionen

4 Applets und Applikationen
4.1 Beispiel einer Applikation: ApplicationTest.class
4.2 Java-Applets erstellen
4.3 Sicherheit bei Applets
4.4 Der Lebenszyklus eines Applets
4.4.1 Init()
4.4.2 Start()
4.4.3 Stop()
4.4.4 Destroy()
4.5 Das Vererbungsdiagramm der Klasse Applet
4.6 Aufruf eines Applets aus der HTML-Seite
4.6.1 Informationen an Applets übergeben
4.6.2 Weitere Applet-Attribute
4.7 Grafikprogrammierung
4.7.1 Striche zeichnen
4.7.2 Figuren
4.7.3 F-llfiguren
4.7.4 3D-Figuren
4.7.5 Text zeichnen (Zeichensätze definieren)
4.8 Benutzeroberfläche des Applets
4.8.1 Die gebräuchlichsten AWT-Komponenten
4.8.2 Swing-Komponenten (Jcomponent)
4.8.3 Container
4.8.4 Layout-Manager
4.9 Multimedia mit Applets
4.10 Der Applet-Kontext
4.11 Kommunikation zwischen verschiedenen Applets
4.12 Objekte im Browser anzeigen
4.13 Zugriff auf JavaScript
4.14 Auftretende Probleme bei der Entwicklung und Ausführung von Applets
4.15 Doppelnutzung als Applet und Applikation
4.16 Signierte Applets / JAR-Dateien signieren
4.16.1 Signierte Intranet-Applets
4.16.2 Signierte Internet-Applets
4.17 Kommunikation über einen URL (Netzwerkverbindungen)
4.17.1 Grundbegriffe der Netzwerkprogrammierung
4.17.1.1 IP-Adresse
4.17.1.2 Hostname
4.17.1.3 Client
4.17.1.4 Server
4.17.1.5 Domain
4.18 Die Streams
4.19 Die URLConnection

5 Servlets in Java
5.1 CGI – Skripte und Servlets
5.2 Aufgaben von Servlets
5.3 Vorteile von Servlets gegenüber den herkömmlichen CGI-Skripten
5.4 Der Kommunikationsprozess im WWW
5.5 Die Servlet – Engine
5.6 Der Lebenszyklus von Servlets
5.6.1 Die init()-Methode
5.6.2 Die service()-Methode
5.6.3 Das SingleThreadModel
5.6.4 Die destroy()-Methode
5.7 Datenfluss bei Applet – Server – Servlet – Kommunikation
5.8 Interaktion zwischen dem Server und dem Client
5.8.1 Aus der Sicht des Clients:
5.8.2 Aus der Sicht des Servers:
5.9 Grundstruktur der Servlets
5.9.1 Der Aufruf aus der HTML-Datei
5.9.2 Client-Anfragen bearbeiten – Formulardaten
5.9.3 Formulardaten aus einem Servlet lesen
5.9.4 Server Antwort generieren:
5.10 Cookies
5.10.1 Vorteile von Cookies
5.10.2 Probleme mit Cookies
5.10.3 Cookies vom Client lesen
5.10.4 Cookies mit einem angegebenen Namen finden
5.11 Sessions
5.11.1 HttpSession-Objekt zur akt. Anfrage nachschauen
5.11.2 Informationen zu einer Sitzung nachschauen
5.12 Applets als Frontend für Servlets
5.12.1 Daten mit GET senden und Ergebnisse direkt verarbeiten (HTTP-Tunneling)
5.12.2 Serialisierte Datenstrukturen lesen
5.12.3 Daten mit POST senden und Ergebnisse direkt verarbeiten
5.13 JDBC (Java Database Connectivity)
5.13.1 Datenbanktreiber laden
5.13.2 Die Verbindungs-URL definieren
5.13.3 Die Verbindung herstellen mit getConnection()
5.13.4 Anweisungen erzeugen
5.13.5 Abfrage ausführen
5.13.6 Ergebnisse verarbeiten
5.13.7 Verbindung schliessen
5.14 Threads
5.14.1 Was sind Threads
5.14.2 Zustände von Threads
5.14.3 Ausführen und starten des Threads
5.14.4 Verwendung von Threads in Applets
5.14.5 Synchronisierung von Threads
5.14.6 Deadlocks

6 Projektbeschreibung
6.1 Randbedingungen für die einzutragenden Werte:
6.2 Das ER-Model
6.3 Darstellung mit Hilfe des Applet auf der Client-Seite
6.3.1 Daten eingeben und absenden
6.3.2 Ergebnisse in graphischer Form darstellen
6.3.3 Datenfluss der Applet – Server – Servlet-Kommunikation des Projektbeispiels
6.3.3.1 Aufruf der Datei AppletCon.class aus der HTML-Datei
6.3.4 Realisierung des Applet
6.3.4.1 Eingabedaten des Clients übernehmen und URL-Verbindung aufbauen
6.3.4.2 Ergebnisdaten einlesen
6.3.4.3 Maximalen Wasserstand ermitteln
6.3.4.4 Zeichnen der X-Achse
6.3.4.5 Zeichnen der Y-Achse
6.3.4.6 Zeichnung der XY-Werte bzw. Zeichnung des Graphen
6.3.4.7 Ablaufplan des Exchange-Sort-Verfahren ermitteln
6.4 Beschreibung der Serverseite bzw. der Servletverarbeitung
6.4.1 Datenbanktreiber laden und Datenbankverbindung herstellen
6.4.2 Parameterwerte aus der Umgebungsvariablen einlesen
6.4.3 Datenbankabfrage ausführen
6.4.4 Abfrageergebnis einlesen und Daten an das Applet senden

7 Realisierung des Servlets (Zufallswerte erzeugen)
7.1 Aufgabendefinition
7.2 Programmablauf
7.3 Klassenbeschreibung
7.3.1 Allgemeines
7.3.2 servlet WerteErzeugen
7.3.3 Klasse Verarbeitung
7.3.4 Klasse Wasserstand
7.3.5 Klasse Zufallszahlen
7.3.6 Klasse DatumZeit

8 Installation und Inbetriebnahme

9 Glossar

10 Literaturhinweise:

Abbildungsverzeichnis

1 Abb. Generationen der Programmiersprachen

2 Abb. Prozedurale und objektorientierte Programmierung

3 Abb. Java Virtual Machine

4 Abb. Datenbankanfrage in JDBC

5 Abb. Treiberarten

6 Abb. Connection-Pool

7 Abb. Applet und Applikation

8 Abb. Lebenszyklus eines Applets

9 Abb. Vererbungsdiagramm

10 Abb. AppletBeispiel1

11 Abb. Lebenszyklus eines Servlets

12 Abb. Datenfluss bei Client – Server Kommunikation

13 Abb. Zustände von Threads

14 Abb. Nicht synchronisierte und synchronisierte Threads

15 Abb. Entity - Relationship - Model

16 Abb. Darstellung des Applets auf der Clientseite

17 Abb. Daten über Applet eingeben und Senden

18 Abb. Ausgabe (a) Applet

19 Abb. Auswertung der Daten in graphischer Form (a)

20 Abb. Auswertung der Daten in graphischer Form (b)

21 Abb. Datenfluss des Projektbeispiels

22 Abb. Exchange-Sort-Verfahren

23 Abb. Servletaufruf über HTML-Seite

24 Abb. Datenfluss des Servlets WerteErzeugen

1 Ziel der Studienarbeit

Ziel dieser Studienarbeit war es, Erfahrungen im Umgang mit der Programmiersprache im Umfeld des Internets zu sammeln. Im Besonderen wollten wir dabei auf die Bereiche Applets, Servlets und Datenbankanbindungen mit der JDBC-API eingehen. Dabei wurden folgende Themenbereiche intensiv bearbeitet:

- Darstellung von grafischen Elementen in Applets
- Kommunikation zwischen Applets und Servlets
- Aufruf von Servlets über einen Webserver
- Funktionen einen Webservers
- Aufbau von Datenbankverbindungen
- Auslesen von Daten aus einer Datenbank
- Schreiben von Daten in eine Datenbank

Außerdem konnten wir Erfahrung in der Projektplanung sammeln, was für zuk-nftige Arbeit an der Hochschule und auch im Praxissemester sehr wichtig ist. Es war das erste Mal während des Studiums, dass wir ein Projekt dieser Größenordnung bearbeiten konnten. Deswegen gestaltete sich die zeitliche Planung und die Abschätzung des Aufwands etwas schwierig. Gäbe es die Möglichkeit, bereits in früheren Semestern Softwareprojekte durchzuführen, wären uns einige Schwierigkeiten erspart geblieben. Allerdings muss sich unsere Studienarbeit am Ergebnis messen lassen und das ist für ein Einsteigerprojekt meiner Meinung nach mehr als ausreichend.

2 Java – Die Programmiersprache

2.1 Geschichte von Java

Die Geschichte der Programmiersprache Java begann bereits im Jahre 1990, als ein Entwicklungsteam um James Gosling (er entwickelte den Texteditor Emacs) bei Sun Microsystems mit der Planung für eine neue Programmiersprache mit Namen Oak beauftragt wurde. Diese Programmiersprache wurde eigentlich nicht für den Einsatz im Internet, sondern zur Steuerung von Consumer-Electronic-Geräten, wie Fernseher und Stereo-Anlagen, aber auch Toaster oder Waschmaschinen, konzipiert. Leider setzte sich diese Sprache nie durch. Die Eigenschaften der neuen Programmiersprache (Plattformunabhängigkeit, geringer Umfang des Quellcodes) waren aber für einen anderen Einsatzzweck geradezu prädestiniert: Software für das World Wide Web.

Nachdem man die Sprache weiter verbessert und in Java umbenannt hatte, stellte Sun die Technologie im Jahr 1995 der Öffentlichkeit vor und entwickelte gleichzeitig einen Web-Browser (HotJava), der die Java-Virtual-Machine, die den Java-Bytecode interpretiert, implementiert hatte. Jetzt war es möglich, kleine Java-Programme, die sogenannten Applets im Browser auszuführen. Diese Mini-Applikationen ermöglichten es, die einstmals starren Web-Seiten auf der Basis von HTML lebendiger zu machen. Als dann Netscape mit dem Navigator 2.0 Ende 1995 nachzog und die JVM ebenfalls integrierte, hatte Java endgültig seinen Durchbruch.

Heute wird Java nicht nur für client-seitige Applets eingesetzt, sondern ebenso zur Datenbankanbindung und zur Programmierung in verteilten Systemen. Java ist eine vollwertige Programmiersprache, mit der sich alle Arten von Anwendungen realisieren lassen. Weil es aber sein Vorteile in Netzwerken voll ausspielt, wird es hauptsächlich dort eingesetzt. Welche Vorteile das sind und welche Merkmale Java des weiteren besitzt, darauf soll in den nächsten Abschnitten eingegangen werden.

2.2 Merkmale der Programmiersprache

2.2.1 Portabilität

Java ist plattformunabhängig. D. h. es ist möglich, Java-Code der nach den Konventionen der Programmiersprache implementiert wurde, auf beliebigen Betriebssystemen auszuführen. Dies ist eine der wichtigsten Forderungen für den Einsatz im Internet.

Dies erreicht Java auf zweierlei Arten. Zum einen wird der Java-Code auf jeder Plattform mit Hilfe der Java-Virtual-Machine (JVM) interpretiert. Ein Java-Programm wird dafür zuerst in sogenannten Bytecode compiliert und bei Ausführung von der JVM übersetzt. Zum anderen sind Datentypen in Java ebenso unabhängig von der jeweiligen Implementierung.

2.2.2 Objektorientierung

Java ist vollständig objektorientiert aufgebaut (im Gegensatz zu C++). So deckt es die Forderungen an objektorientierte Programmiersprachen, wie die Kapselung, die Vererbung, die Polymorphie und die Operatorüberladung weitestgehend ab.

2.2.3 Multithreading

Java unterstützt die gleichzeitige Verarbeitung von Prozessen. Oft ist es notwendig, verschiedene Aufgaben in einem Programm gleichzeitig auszuführen. Ein Beispiel hierfür sind Applets, die Eingaben des Benutzers am Client entgegenzunehmen, während sie auf eintreffende Nachrichten vom Server warten. Früher wurden solche Probleme mit Hilfe von Schleifen gelöst, was aber Schwierigkeiten macht, weil die Tasks nicht mehr sauber voneinander getrennt werden können. Mit sogenannten Threads of Control (Threads = Fäden) können diese Aufgaben effizienter und einfacher gehandhabt werden. Praktisch läuft folgendes ab: in einem Prozess können beliebige Threads gestartet werden, die keine eigenen Prozesse mehr sind, sondern Teilprozesse, die im selben Speicher arbeiten.

2.2.4 Verteilte Programmierung

Java ist für die Programmierung in verteilten Systemen entwickelt worden. Es nutzt TCP/IP als Protokoll, wobei die Kommunikation entweder über Sockets, CGI-Programme (Servlets) oder Remote Method Invocation (RMI) erfolgt.

2.2.5 Robustheit

Java ist eine sehr robuste Programmiersprache, weil bewusst auf unsichere Elemente verzichtet wurde. Im Gegensatz zu C++ verzichtet Java ganz auf den Einsatz von Pointern. Die strenge Einhaltung der Objektorientierung und die Ausklammerung von Operatorpüberladung, multipler Vererbung und automatischer Typumwandlung, tragen einen Großteil dazu bei.

2.2.6 Sicherheit

Ein mindestens ebenso wichtiger Punkt, wie die Portierbarkeit, ist die Forderung nach der Sicherheit von Java-Code. Dies gilt vor allem für Applets, die auf dem Client-Rechner ausgeführt werden. In Java wurde deshalb ein weitestgehender Schutz vor Viren und Zugriffen auf die Festplatte des Clients implementiert. Dies wird hauptsächlich durch den Einsatz der JVM erreicht. Sie unterbindet den Zugriff auf sicherheitskritische Ressourcen und führt vor dem Ablauf des Programms eine Bytecode-Verifizierung durch.

2.2.7 Garbage Collector

In Java gibt es nur sehr vereinzelt eine explizite Speicherfreigabe, weil sie sehr fehleranfällig ist. Java verwendet den Garbage Collector, der in die JVM integriert ist und sorgt während der Laufzeit dafür, dass nicht mehr benötigte Objekte aus dem Speicher gelöscht werden.

2.2.8 Unterschiede zu C++

Java ist mit Syntax und einigen Konventionen an C/C++ angelehnt. Allerdings gibt es einige wichtige Unterschiede. Java wurde auf Sicherheit und für den Einsatz im Internet konzipiert. In Java enthalten sind deshalb nicht:

- Typedefs, Defines und der Präprozessor
- Strukturen und Unions
- Funktionen außerhalb von Klassen
- Multiple Vererbung
- Goto-Befehle
- Automatische Typumwandlung
- Pointer
- Manuelle Speicherverwaltung

2.3 Das objektorientierte Konzept

2.3.1 Einführung

Das objektorientierte Programmierparadigma wurde in den späten Sechziger Jahren bzw. Anfang der Siebziger Jahre mit den Sprachen Simula und Smalltalk entwickelt. Man versprach sich damals eine einfachere Modellierung von Problemen der realen Welt in Programmen. Mittlerweile hat sich das objektorientierte Konzept durchgesetzt. Welche Gründe gab es für den enormen Aufschwung der objektorientierten Programmierung in den Neunziger Jahren?

Zu Beginn des Computerzeitalters waren die Sprachen maschinenorientiert, d.h. ganz nah an der Hardware ausgerichtet. Damit konnten die Eigenschaften der Bauteile sehr effizient genutzt werden, aber aufgrund dieser Nähe konnten keine komplexeren Programme entwickelt werden.

Anwendungsorientierte Sprachen wie Algol, Cobol oder Fortran sollten die Programmierung vereinfachen und schnellere Software-Entwicklung ermöglichen. Doch diese frühen Programme waren unstrukturiert und wenig effizient.

Bessere Ergebnisse versprach man sich von der Einführung von strukturierten Programmiersprachen wie Pascal oder C. Diese Art der Programmierung brachte echte Vorteile denn sie brachte übersichtlichere Programme und schuf die Möglichkeit, komplexere Sachverhalte zu implementieren. Leider kam man durch den prozeduralen Programmierstil dem eigentlichen Problem (dem Was) nicht näher, sondern entfernte sich immer weiter von der realen Problemlösung. Es stand das Konzept (das Wie) im Vordergrund, nicht aber die Modellierung eines Weltausschnitts.

Die objektorientierte Programmierung geht ein Stück weiter in die Richtung, die semantische L-cke zwischen Wirklichkeit und Code zu schließen. Es ist möglich ein reales Problem mit Objekten zu implementieren, die Eigenschaften und Fähigkeiten besitzen, untereinander kommunizieren und ihre Eigenheiten weitervererben können. Damit können sehr komplexe Sachverhalte abgebildet werden und die Flexibilität ist hoch genug, um mit dem stetigen Wandel der realen Welt schritt zu halten, weil solche Objektsysteme relativ einfach zu warten sind.

Abbildung in dieser Leseprobe nicht enthalten

Abb. Generationen der Programmiersprachen

2.3.2 Das Konzept

Die objektorientierte Programmierung unterscheidet sich grundsätzlich vom Ansatz der prozeduralen Programmierung. Diese besteht aus Daten und Prozeduren, die strikt getrennt voneinander sind. Mit Hilfe der Prozeduren kann man Daten manipulieren. Das „Wie“ steht im Vordergrund.

In der Objektorientierung wird die reale Welt mit Hilfe von Objekten modelliert. Diese Objekte tauschen Botschaften aus und reagieren auf diese. Das Innere der Objekte ist nicht sichtbar und für das Funktionieren eines Programms von untergeordneter Bedeutung.

Abbildung in dieser Leseprobe nicht enthalten

Abb. Prozedurale und objektorientierte Programmierung

2.3.3 Abstraktion

Um einen Ausschnitt aus der Wirklichkeit zu modellieren ist es nicht von Bedeutung, sämtliche Details zu implementieren. Wichtig sind nur relevante Eigenschaften, die auf das Wesentliche beschränkt sind. Es gilt das Prinzip, so wenig Informationen wie möglich, so viel wie nötig bereitzustellen. Nach außen sollte ein Objekt nur das zeigen, was ein Programmierer benötigt, um bestimmte Dienstleistungen anfordern zu können. Alles andere soll verborgen bleiben. Auf dieses Prinzip soll beim Punkt Kapselung näher eingegangen werden.

2.3.4 Botschaften

Objekte kommunizieren über Botschaften. Sie reagieren mit einem internen Verhalten darauf und geben Antworten zurück. Botschaften, die sie nicht verstehen, werden von den Objekten ignoriert. Stellen wir uns ein Objekt Ruderboot vor, das die Botschaften „geradeaus rudern“, „links lenken“, „rechts lenken“ versteht. Um dieses Objekt zu steuern, können nur diese drei Botschaften genutzt werden.

2.3.5 Kapselung

Ein solches Ruderboot-Objekt wird die Botschaften so ausführen, wie es dafür eingerichtet wurde. Von außen haben wir keinen Einfluss, wie auf eine Botschaft reagiert wird, wir können nur bestimmen, was getan werden soll. Wir können z.B. nicht vorhersagen, wie stark unser Ruderboot einlenkt, wenn wir die Botschaft „rechts lenken“ an das Objekt senden. Auch das Prinzip, nach dem der Lenkvorgang erfolgt, ist für uns nicht ersichtlich. Zudem können wir die Ausführung auch nicht manipulieren, z.B. auf ein einlenken von 90 Grad bestehen.

Dieses Prinzip, die Interna eines Objekts nicht einsehen und nicht manipulieren zu können, nennt man Kapselung. Der Vorteil ist, dass nicht bekannt sein muss, wie das Objekt im Inneren arbeitet, um einen Service anzufordern. Man nennt dieses Verbergen von Informationen auch Information Hiding.

2.3.6 Klassen und Exemplare

Objekte, die dieselben Botschaften verstehen, dieselben Merkmale aufweisen und sich nur in der Ausprägung dieser Merkmale unterscheiden, bilden eine Klasse von Objekten. Eine Klasse ist mit dem Datentyp einer Variablen vergleichbar. Er legt den „Typ“ eines Objekts fest. Man spricht auch vom Objekttyp. Man kann eine Klasse auch als Abstraktion verstehen, während ein Objekt greifbar ist und konkret einsatzfähig ist. Um auf unser Ruderboot-Beispiel zurückzukommen, nehmen wir an es gäbe eine Klasse Ruderboot und mehrere Objekte dieser Klasse. Sie heißen „Ruderboot Klara“, „Ruderboot Gorch Fock“ und „Ruderboot Titanic“. Jedes Objekt der Klasse Ruderboot hat die gleichen Merkmale (gebaut aus Holz, Ruder, Rettungsringe), sie sind aber unterschiedlich ausgebildet. So hat das „Ruderboot Titanic“ 4 Ruder, ab nur einen Rettungsring, während das „Ruderboot Klara“ nur zwei Ruder hat, aber 3 Rettungsringe.

2.3.7 Vererbung und Taxonomie

Es besteht die Möglichkeit, Klassen von anderen abzuleiten. Die neue Klasse besitzt dann sämtliche Merkmale der alten Klasse, kann aber um neue Merkmale erweitert werden. Wie in der Tier- und Pflanzenkunde gibt es Vererbungshierarchien, in die Klassen eingebunden sind. Merkmale von Klassen höherer Ebene werden weitervererbt. Diese Einordnung nennt man Taxonomie.

So könnte unser Objekt „Ruderboot“ seine Eigenschaften und Methoden an ein neues Objekt „Sportruderboot“ vererben, das eine andere Bauart besitzt und mehr Ruder enthält. Klassen, die von einer bestimmten Basisklasse (Oberklasse) abgeleitet sind, werden auch Unterklassen genannt.

2.3.8 Polymorphie

Objekte von Unterklassen verstehen dieselben Botschaften, wie die Oberklasse, obwohl die interne Umsetzung ganz anders aussehen kann. Wenn wir uns eine Oberklasse „Boot“ vorstellen, mit zwei Unterklassen „Motorboot“ und „Ruderboot“, so reagieren Objekte der beiden Unterklassen beide auf die Botschaft „rechts lenken“, aber die Umsetzung verläuft völlig unterschiedlich. Dieses Prinzip nennt man Polymorphie.

2.3.9 Überladung

Es besteht die Möglichkeit, in einer Klasse verschiedene Varianten derselben Methode zu implementieren, die sich nur in der Parameterstruktur unterscheiden. So kann er eine Botschaft entsprechend seinen Anforderungen mit Parametern belegen, ohne zu wissen, was die Methode intern tut.

2.4 Objektorientierte Programmierung mit Java

2.4.1 Die Java Virtual Machine (JVM)

Java ist eine plattform-unabhängige Programmiersprache, deren Haupteinsatzzweck Anwendungen in Netzwerken und verteilten Systemen sind. Wie bereits erwähnt, wird der Java-Quellcode (mit der Dateinamenserweiterung .java) in Bytecode compiliert. Der Java-Bytecode liegt in sogenannten class -Dateien vor. Bei Ausführung wird dieser Bytecode von der Java Virtual Machine interpretiert und auf Sicherheit überprüft. Ein nach wie vor sp-rbarer Nachteil dieses Mechanismus ist die langsame Geschwindigkeit von Java-Anwendungen im Vergleich von z.B. C++. Allerdings haben die Java-Entwickler in den letzten Jahren einige Verbesserungen vorgenommen, sodass man davon ausgehen kann, dass dieses Manko in Zukunft nur noch ein nebensächliches Problem sein wird.

Das folgende Schaubild soll noch einmal den Ablauf der Verarbeitung des Java-Quellcodes bis zum ablauffähigen Programm verdeutlichen:

Abbildung in dieser Leseprobe nicht enthalten

Abb. Java Virtual Machine

2.4.2 Der Unicode-Zeichensatz

Java verwendet den Unicode-Zeichensatz, der international standardisiert und eine Obermenge des ASCII-Zeichensatzes ist. Jedes Zeichen wird mit 16 Bit dargestellt. Damit können sehr große Mengen von Zeichen codiert werden. Sämtliche lateinische Zeichen mit speziellen nationalen Sonderzeichen werden ebenso abgedeckt, wie arabische, thailändische, japanische und sogar ein Teil der chinesischen Schriftzeichen. Die sogenannte UTF-8-Codierung bietet die Möglichkeit, den 16 bit breiten Unicode-Zeichensatz in einen 8 bit breiten Zeichensatz zu übersetzen, damit er auch auf Plattformen lauffähig ist, die keinen Unicode unterst-tzen. Die Namen der Bezeichner in Java können alle in Unicode bezeichnet werden.

2.4.3 Klassen

2.4.3.1 Definition

Klassen entsprechen in etwa den Datentypen in prozeduralen Programmiersprachen. Sie enthalten Wertemengen und darauf definierte Methoden. Klassen werden in Java mit dem Schlüsselwort class und dem Namen der Klasse eingeleitet. Wird die Klasse gespeichert, so muss die Datei den selben Namen erhalten, wie die Klasse. Eingeschlossen in geschweifte Klammern folgen darauf die Methodendeklarationen und Datenelemente.

Beispiel:

Abbildung in dieser Leseprobe nicht enthalten

Die hier deklarierte Klasse Anzug hat vier Datenelemente (groesse, stoff, farbe, schnitt) des Typs Integer. Sie enthält die Methode farbeAendern mit dem Parameter neueFarbe.

2.4.3.2 Zugriffsrechte

In Klassen lassen sich spezielle Zugriffsrechte regeln. Das Schlüsselwort public gibt an, dass eine Klasse auch außerhalb der Datei erreicht werden, in der sie definiert wurde. In einer Datei kann nur eine Klasse public sein

2.4.3.3 Modifier

Für Klassen gibt es sogenannte Modifier, mit denen einer Klasse neue Eigenschaften zugewiesen bekommen:

- abstract

die Klasse enthält lediglich eine Klassenstruktur, keine ausformulierten Methoden oder Datenelemente. Sie kann nicht instantiiert werden und muss deshalb in einer Unterklasse verwendet werden. Sie kann konkrete und abstrakte Methoden besitzen.

- final

von dieser Klasse können keine Unterklassen gebildet werden (z.B. Systemklassen)

2.4.4 Exemplare

2.4.4.1 Instanzierung

Ein Objekt ist eine Instanzierung einer Klasse. Es wird auch Exemplar genannt. Objekte dieser Klasse werden mit folgenden Anweisungen erzeugt:

Anzug dreiTeiler;

dreiTeiler = new Anzug();

Die Erzeugung von Exemplaren einer Klasse verläuft in zwei Schritten:

- zuerst wird eine Variable, hier dreiTeiler, mit dem entsprechenden Objekttyp, hier Anzug definiert. Dadurch wird aber noch kein neues Exemplar erzeugt. Es besteht lediglich ein Verweis auf die Exemplare von Anzug.

- ein neues Exemplar wird mit dem new -Operator erzeugt

Durch die Erzeugung wird ein Speicherbereich belegt, der das Objekt aufnimmt. Als Operand von new dient immer der Konstruktor der Klasse.

2.4.4.2 Konstruktoren

Ein Konstruktor ist eine Methode, die zur Initialisierung eines Objekts dient. Der Konstruktor hat immer denselben Name wie die Klasse. Es besteht die Möglichkeit mehrere Konstruktoren zu definieren, die unterschiedliche Datenelemente initialisieren. Wenn kein Konstruktor in der Klasse definiert wird, wird auf den impliziten Konstruktor zurückgegriffen. Dieser Konstruktor wird ohne Parameter aufgerufen.

Konstruktoren haben keinen Ergebnistyp, er kann auch nicht void sein. return-Anweisungen ohne Argument sind erlaubt, um eine vorzeitige Beendigung des Konstruktors zu ermöglichen.

Beispiel:

Abbildung in dieser Leseprobe nicht enthalten

Im Beispiel wurde die Klasse Anzug um zwei Konstruktoren erweitert. Über den ersten Konstruktor können die Variablen groesse und schnitt initialisiert werden. Mit dem zweiten können sämtliche Datenlemente initialisiert werden.

Das Schlüsselwort this wird in diesem Fall zur Vereinfachung gebraucht. this verweist auf den ersten Konstruktor mit seinen Parametern neueGroesse und neuerSchnitt. So kann man den ersten Konstruktor in den zweiten einbinden und sich Schreibarbeit sparen.

2.4.4.3 Vererbung

In Java lassen sich Klassen definieren, die auf anderen Klassen basieren. So können diese Unterklassen die Methoden und Eigenschaften ihrer Oberklasse übernehmen, diese aber auch erweitern und bestehende Methoden überschreiben.

Die Ableitung einer Klasse von einer anderen wird mit dem Schlüsselwort extends angezeigt. Eine Klasse kann genau von einer anderen Klasse erben. Eine Mehrfachvererbung wie in C++ gibt es in Java nicht. Für solche Fälle gibt es die sogenannten Interfaces (siehe weiter unten).

Beispiel:

Abbildung in dieser Leseprobe nicht enthalten

Im Beispiel erbt die Klasse Tarnanzug von der Klasse Anzug. Mit dem Konstruktor werden alle Datenelemente initialisiert. Das Schlüsselwort super verweist auf die Oberklasse Anzug. Dieser Ausdruck entspricht dem Konstruktor der Oberklasse. Die Parameterwerte werden an diesen übergeben und die Datenelemente initialisiert.

Ein weiteres Schlüsselwort in diesem Beispiel ist this. Es dient hier als sogenannter Qualifizierer für die Datenelemente einer Klasse, wenn deren Bezeichner durch Methodenparameter verdeckt sind. Haben also Parameter in einer Methode oder in einem Konstruktor dieselben Bezeichner wie Datenelemente, so haben die Bezeichner der Methodenparameter den Vorrang. Ohne diesen Qualifizierer this wären die Datenelemente verdeckt und es könnte nicht mehr auf sie zugegriffen werden. Dies ist praktisch, denn man muss nur noch einen Namen für Parameter und Datenelemente vergeben.

Alle Klassen in Java stammen von der Oberklasse Object ab. Sie vererbt ihre Eigenschaften weiter, auch wenn dies nicht explizit mit extends angegebenen wird.

2.4.5 Interfaces

2.4.5.1 Allgemeines

In Java wurde auf Mehrfachvererbung, wie sie die objektorientierte Theorie vorsieht und die auch C++ anwendet, verzichtet. Dadurch soll die Fehleranfälligkeit von Vererbungen verringert werden. Stellt man sich die Klassenhierarchie in der Theorie als einen Klassenbaum vor, so bezeichnet man in Java solche Vererbungen als gerichtete Graphen. Dadurch gibt es folgende Vorteile:

- die Klassenhierarchie wird durchschaubarer
- die Implementierung kann an beliebiger Stelle im Klassenbaum stehen und kann zu einem späteren Zeitpunkt auch verschoben werden
- Implementierungen können leicht ausgetauscht werden, ohne dass sich das Programm ändert

Der Nachteil ist, dass man nur von exakt einer Oberklasse erben kann. Entweder man übernimmt die Methoden einer Schnittstelle oder man implementiert völlig neu.

2.4.5.2 Beschreibung

Interfaces sind ein Sonderfall abstrakter Klassen. Sie enthalten keinerlei Implementierungen, in ihnen können also nur abstrakte (abstract) Methoden und konstante Datenelemente (final static) deklariert werden. Dies geschieht implizit und muss nicht deklariert werden.

Beispiel:

Abbildung in dieser Leseprobe nicht enthalten

Um ein Interface anwenden zu können, muss es von einer Klasse implementiert werden. Sämtliche Methoden müssen überschrieben werden, aber es können auch Methoden des Interfaces leergelassen werden.

2.4.5.3 Modifier

Auch für Interfaces kann man Modifier einsetzen:

- public

Verfügbarkeit des Interfaces auch außerhalb seines Pakets. Im Gegensatz zur Klasse, sind alle Bestandteile des Interface public.

- abstract

Interfaces sind bereits implizit abstract. Die explizite Angabe ist möglich, aber nicht nötig. Es wird sogar davon abgeraten.

- strictfp

Deaktivierung der erweiterten Gleitpunkt-Darstellung in allen Methoden des Interface

2.4.6 Datenelemente

2.4.6.1 Beschreibung

Datenelemente beschreiben den Zustand eines Objekts. Wird ein neues Exemplar erzeugt, so wird für jedes neue Datenelement ein Speicherbereich angelegt. Die Datenelemente einzelner Exemplare sind also voneinander unabhängig. Die Ausnahme davon sind die static -Variablen, auf die im Anschluss noch näher eingegangen wird. Datenelemente unterscheiden sich von den Variablen, die innerhalb von Methoden deklariert und benutzt werden. Diese Variablen dienen nicht der Zustandsbeschreibung des Objekts, sondern dazu Aktionen mit dem Objekt auszuführen.

Datenelemente werden, falls sie nicht explizit initialisiert werden, vom Compiler mit einem, dem Typ entsprechenden Nullwert initialisiert. Variablen in Methoden muss explizit ein Wert zugewiesen werden.

Beispiel:

Abbildung in dieser Leseprobe nicht enthalten

2.4.6.3 Modifier

In Java gibt es sogenannte Modifier, mit denen ein Datenelement neue Eigenschaften erhält. Die Modifier werden dem Datenelement vorangestellt.

Beispiel:

final int festwert = 23;

Diese Modifier können für Datenelemente verwendet werden:

- final

wird zur Deklaration von Konstanten verwendet. Nicht möglich bei lokalen Variablen.

- static

wird zur Deklaration von Klassen-Variablen verwendet. D.h., für diese Variable wird nicht von jedem neuen Exemplar ein Speicherbereich belegt, sondern sie ist nur einmal vorhanden und wird von sämtlichen Exemplaren der Klasse gemeinsam benutzt.

- volatile

der Compiler stellt sicher, dass die Datenelemente bei Verwendung von Threads immer konsistent bleiben. Greifen verschiedene Threads auf dieselben Datenelemente zu und manipulieren diese, muss gewährleistet werden, dass ein Thread stets den aktuellen Wert verwendet, falls die Threads nicht synchronisiert sind.

- transient

zeigt an, dass ein Datenelement nicht zum persistenten Zustand eines Objekts gehört. Wird ein Objekt persistent gemacht, werden die Werte dieser Datenelemente nicht mitgespeichert. Nach einer Rekonstruktion des Objekts werden diese standardmäßig mit den Initialwerten des entsprechenden Typs besetzt.

2.4.7 Methoden

2.4.7.1 Methodenkopf und -rumpf

Methoden realisieren Botschaften in Objekten. Eine Methode beinhaltet sämtliche Maßnahmen, um auf eine Botschaft zu reagieren. Methoden bestehen aus einem Kopf und einem Rumpf. Der Methodenkopf besteht aus drei Teilen:

- dem Ergebnistyp

Gibt eine Methode einen Wert zurück, so muss der Typ der Variable, deren Wert ausgegeben werden soll, im Kopf angegebenen werden. Wird kein Wert zurückgegeben, definiert man den Ergebnistyp void.

Beispiel:

Abbildung in dieser Leseprobe nicht enthalten

Die erste Methode gibt einen Integer-Wert zurück. Dies wird mit dem Ergebnistyp int dargestellt. Mit dem Schlüsselwort return wird die Rückgabe ausgeführt.

- dem Bezeichner

darf alle Unicode-Zeichen enthalten

- die Parameterliste

dort werden Parameter, die in der Methode benötigt werden übergeben. Parameter werden in runden Klammern eingeschlossen und durch Kommas getrennt. Außerdem muss vor jedem Parameter explizit der Typ angegeben werden. Sollen keine Parameter übergeben werden, bleiben die runden Klammern leer (leere Parameterliste).

Beispiel:

public void farbe (int r, int g, int b)

Diese drei Bestandteile des Kopfes werden zusammen auch Signatur der Methode genannt. Eine Klasse darf keine zwei Methoden mit identischer Signatur haben. Dahingegen ist es möglich, das eine Klasse mehrere Varianten einer Methode besitzt. Diese Technik wird Methodenüberladung genannt. Darauf soll weiter unten noch näher eingegangen werden.

Der Methodenrumpf wird in geschweifte Klammern eingeschlossen. In einer Methode können keine Unterfunktionen, wie es z.B. in Pascal möglich ist, eingeschachtelt werden.

2.4.7.2 Lokale Variablen

In Methoden können lokale Variablen vereinbart werden. Diese werden jedoch nicht wie die Datenelemente der Klasse bei der Erzeugung eines Exemplars initialisiert, sondern müssen explizit mit Werten belegt werden.

Beispiel:

Abbildung in dieser Leseprobe nicht enthalten

2.4.7.3 Methodenaufruf

Wenn Methoden aufgerufen werden, muss man darauf achten, grundsätzlich alle Parameter der Parameterliste zu übergeben. Werden zu wenige, zu viele, oder gar falsche Typen angegeben, meldet der Compiler einen Fehler. Die Methode farbe im vorigen Beispiel wird folgendermaßen aufgerufen:

- in derselben Klasse

farbe(5);

- von außerhalb(Botschaft)

objekt.farbe(5);

2.4.7.4 Methodenüberladung

Es ist möglich mehrere Varianten von Methoden zu deklarieren. Diese Methoden unterscheiden sich nur in Art und Zusammensetzung der Parameter. Der Benutzer eines Objekts kann diejenige Methode auswählen, die seinen Anforderungen am besten entspricht. Der Interpreter erkennt die unterschiedlichen Methoden beim Aufruf an der unterschiedlichen Parameterstruktur.

Beispiel:

Abbildung in dieser Leseprobe nicht enthalten

Folgende Modifier sind möglich:

- final

eine solche Methode kann nicht überschrieben werden. Es ist aber möglich eine Methode mit demselben Namen, aber einer anderen Signatur hinzuzufügen. Methoden können auch implizit final deklariert werden, wenn die Klasse, zu der die Methode gehört, final deklariert wurde.

- static

static -Methoden werden analog zu static -Datenelementen direkt einer Klasse zugeordnet. Solche Methoden können aufgerufen werden, ohne dass vorher ein neues Objekt angelegt wurde. Es gibt Klassen von denen keine Exemplare instatiiert werden können und die nur statische Methoden enthalten, wie z.B. die Klasse java.util.Arrays. Wird eine Methode dieser Klasse aufgerufen, muss so vorgegangen werden:

Arrays.sort(MesswertID);

Besonderheit:

Innerhalb von static -Methoden kann nur auf Datenelemente und Methoden zugegriffen werden, die auch static sind.

- native

der Modifier native ermöglicht das Einbinden von Code, der in einer anderen Programmiersprache geschrieben wurde. Diese Deklaration besteht nur aus dem Methodenkopf, der mit Semikolon abgeschlossen wird.

Beispiel:

public native int hashCode();

Eine native -Methode muss in einer Shared Library befinden, die mit dem Aufruf System.load() oder System.loadLibrary() geladen wird.

- synchronized

stellt sicher, dass eine Methode nicht von mehreren Threads gleichzeitig ausgeführt wird

- abstract

bezeichnet abstrakte Methoden. Diese legen nur die Aufrufstruktur, nicht aber die Implementierung fest. Sie dürfen nur in Klassen deklariert werden, die ebenfalls abstrakt sind. Um sie benutzen können, müssen sie in einer Unterklasse, die nicht abstrakt ist, überschrieben werden. abstract -Methoden werden wie native -Methoden deklariert.

- strictfp

Deaktivierung der erweiterten Gleitpunktdarstellung in der Methode. Laut der IEEE 754 Spezifikation können Gleitpunktzahlen nicht nur mit einfacher und doppelter Genauigkeit (32 bzw. 64 bit) dargestellt werden, sondern mit einer größeren Genauigkeit von 43 bzw. 79 bit. Die JVM unterstützt diesen Standard, aber nicht alle Plattformen tun das. Deshalb ist es möglich diese Darstellung explizit auszuschalten, damit Rechenoperationen auf allen Plattformen dasselbe Ergebnis liefern.

2.4.8 Pakete

2.4.8.1 Allgemeines

Klassen und Interfaces können in Java zu Paketen (Packages) zusammengefasst werden. Dadurch lassen sich Klassen aus demselben Aufgabengebiet bündeln. Diese Pakete sind hierarchisch unterteilt, d.h. ein Paket kann Unterpakete besitzen die ebenfalls wieder unterteilt sind.

Klassen sind bezüglich des Zugriffs aufeinander innerhalb eines Pakets gleichberechtigt. Selbst Klassen in weiter entfernten Unterpaketen können auf Klassen, die in der Hierarchie weiter oben stehen zugreifen.

Um eine Klasse einem Paket zuzuordnen, muss man das Schlüsselwort package verwenden. Hinter package muss der Bezeichner des Pakets stehen. Die package -Anweisung ist die erste Anweisung in der Klasse.

Beispiel:

Abbildung in dieser Leseprobe nicht enthalten

Sämtliche Klassen, in denen diese Anweisung steht, werden dem Paket mathtools zugeordnet. Die Klassen eines Pakets müssen in einem Verzeichnis mit dem gleichen Namen wie das Paket stehen. Ein Unterpaket wird folgendermaßen definiert:

package mathtools.statistics;

Alle Klassen des Unterpakets statistics müssen im gleichnamigen Unterverzeichnis gespeichert werden. Klassen, die keine package -Anweisung beinhalten, werden einem voreingestellten Package zugeordnet, dass keinen Namen hat.

2.4.8.2 Importieren von Paketen und Klassen

Sollen Klassen benutzt werden, die in anderen Paketen gespeichert sind, müssen diese importiert werden. Dies geschieht mit dem Schlüsselwort import. Die Anweisung muss nach package eingefügt werden.

import java.util.Random;

Diese Anweisung ist nur für die Compilierung von Bedeutung, denn durch sie wird die Klasse nicht geladen, sondern nur sichtbar gemacht, damit sie ohne den Namen ihres Pakets aufgerufen werden kann. Dadurch wird auch der Klassenlademechanismus nicht beeinflusst.

import dient also zur Vereinfachung des Aufrufs einer Klasse in der aktuellen Klasse. Jetzt kann die Klasse so aufgerufen werden, als befände sie sich im selben Paket. Sonst müsste sie folgendermaßen aufgerufen werden:

java.util.Random

Soll mehr als eine Klasse aus einem Paket eingebunden werden, kann man den Aufruf verkürzen, indem man solche Klassen zusammengefasst mit einem Stern (*) aufruft:

import java.util.*;

Allerdings werden dadurch nur die Klassen, die direkt im Paket enthalten sind, sichtbar gemacht. Unterklassen werden nicht mit eingebunden.

2.4.9 Archive

2.4.9.1 Beschreibung

Compilierte Pakete oder einzelne Klassen (z.B. Applets oder Servlets) können in Java in Archivdateien gepackt werden. Das ist vor allem bei der Übertragung von Klassen über ein Netzwerk von Vorteil, weil z.B. bei Web-Applikationen die Ladezeiten entscheidend verringert werden können.

2.4.9.2 Archivarten

Zwei Arten von Archivierungs-Dateiarten werden vom JDK unterstützt:

- ZIP-Archive

Unterstützung sowohl unkomprimiert, als auch komprimiert. Klassen müssen zuerst Paketen zugeordnet werden, bevor sie als ZIP-Archiv gepackt werden können. Solche Archive können mit den -blichen Komprimierungsprogrammen erstellt werden.

- JAR-Archive

Standardformat für Klassen-Archive. Sie werden mit dem Tool jar, das im JDK enthalten ist erstellt.

Der Name eines Archivs kann frei gewählt werden und ist nicht abhängig von den enthaltenen Paketen. Der Dateiname muss in der CLASSPATH-Variable eingetragen werden, damit der Compiler die Archive findet und analysieren kann.

3 Java Database Connectivity (JDBC)

3.1 JDBC – Allgemeines

Um mit Hilfe einer Programmiersprache den Zugriff auf relationale Datenbanken zu ermöglichen, benötigt man eine spezielle API. Jedes Datenbankmanagementsystem (DBMS) bietet hierfür eine eigene Schnittstelle an. Weil diese Schnittstellen von Hersteller zu Hersteller differieren und deshalb eine Anwendung beim Wechsel der Datenbank umgeschrieben werden müsste, gibt es genormte Schnittstellen.

Die Open Database Connectivity (ODBC) ist die verbreitetste. Leider hat diese Schnittstelle im Bezug auf Java einige Nachteile. So ist ihre Struktur nicht voll objektorientiert, sie ist plattformabhängig (Windows) und widerspricht, weil sie binär codiert ist, dem Sicherheitskonzept von Java. Sun bietet deshalb die Java Database Connectivity-API (JDBC) als Teil seiner Standard-API an.

JDBC ist eine Low-Level-API. Sie erfordert die Nutzung von SQL-Anweisungen. Sie basiert auf dem SQL/CLI-Standard von X/Open, auf den auch die ODBC-Schnittstelle aufbaut.

3.2 Die Struktur von JDBC

JDBC besteht aus einer Menge von Klassen und Schnittstellen, die im Package java.sql und javax.sql zusammengefasst sind. Die Version 2.0 besteht aus 23 Klassen und Schnittstellen, wobei die folgenden vier die wichtigsten sind:

- java.sql.DriverManager: mit dieser Klasse werden Datenbanktreiber registriert und Verbindungen zu Datenbanken aufgebaut

- java.sql.Connection: stellt eine Datenbankverbindung dar

- java.sql.Statement: ermöglicht die Ausführung von SQL-Anweisungen über eine Verbindung

- java.sql.ResultSet: kontrolliert den Zugriff auf Ergebnisse eines Statements

Abbildung in dieser Leseprobe nicht enthalten

Abb. Datenbankanfrage in JDBC

3.3 Treiber

3.3.1 Der Treibermanager

Der große Vorteil der JDBC-API ist die Unabhängigkeit der Applikation von der verwendeten Datenbank. Im Gegensatz zu ODBC, kann also eine Applikation, die für die Oracle-DBMS geschrieben wurde, auch für MYSQL oder DB2 eingesetzt werden. Dafür muss nur der Datenbanktreiber gewechselt werden.

Dies wird dadurch erreicht, dass JDBC einen sogenannten Treibermanager verwendet, der abstrakte Klassen für den Datenbankzugriff definiert. Die eigentliche Kommunikation mit der Datenbank wird vom jeweiligem Treiber des DBMS durchgeführt. Deshalb muss es für jedes DBMS einen eigenen Treiber geben.

3.3.2 Treiberarten

Abbildung in dieser Leseprobe nicht enthalten

Abb. Treiberarten

In obiger Grafik ist dieses Prinzip dargestellt. Treiber können auf vier verschiedene Arten realisiert werden:

- JDBC-ODBC-Bridge (Typ 1):

der JDBC-Treiber nutzt die ODBC-API für sämtliche Zugriffe auf die Datenbank.

Vorteil: vorhandene ODBC-Treiber können genutzt werden und es müssen keine JDBC-Treiber entwickelt werden.

Nachteil: Sicherheitslücken durch Binärcode und keine Verfügbarkeit von ODBC-Treibern abseits von Windows.

- Native-API-Treiber (Typ 2):

JDBC nutzt für den Zugriff die Client-seitigen Bibliotheken des DBMS.

Nachteil: Einsatz von Binärcode

- JDBC-Net-Treiber (Typ 3):

Eine Middleware, die das DBMS-unabhängige Protokoll in das Protokoll des DBMS übersetzt.

Vorteil: DBMS-unabhängige Implementierung von Javacode und erf-llt die Sicherheitsanforderungen im Internet

- Native-Protokoll-Treiber (Typ 4):

Übersetzung der JDBC-Aufrufe direkt in das DBMS-Protokoll.

Vorteil: effizienterer Zugriff als über Middleware, ebenso sicher.

3.3.3 Aufgaben des Treibers

Der Treibermanager bildet die Vermittlungsschicht zwischen Anwendung und Datenbanktreiber. Es gibt nur statische Methoden, weil es in jeder Anwendung nur einen Treibermanager geben kann. Dieser hat drei Gruppen von Methoden:

- das Registrieren und Laden von Treibern
- das Herstellen einer Verbindung zur Datenbank
- das Konfigurieren der Verbindung (Protokollierung, Login-Timeout)

3.3.4 Laden eines Treibers

Treiber können auf zwei Arten geladen werden:

1. Explizit durch Laden der Treiberklasse:

es muss ein Java-classpath erstellt werden. Dann kann der Treiber über den Aufruf Class.forName direkt geladen werden

Codebeispiel:

Class.forName(„org.gjt.mm.mysql.Driver“).newInstance();

Die newInstance()-Methode trägt in Verbindung mit der Class.forName (Methode) dazu bei, dass ein Exemplar der Treiberklasse dynamisch erzeugt werden kann und die Klasse beim Kompilieren noch nicht bekannt sein muss. Dadurch werden Fehler-meldungen vermieden und die Programmierung kann unabhängig von der Datenbank erfolgen.

2. Über die Systemeigenschaft sql.drivers:

Mit einer Property-Liste können mehrere Treiber, durch Doppelpunkt getrennt, angegeben werden. Bei der Initialisierung werden die Treiber geladen und es wird versucht, diese Treiber anzuwenden. Außerdem wird jeder Treiber auf unsicheren Code hin überprüft.

Beispiel:

java -Djsql.drivers = foo.bar.Driver JdbcDemo1

Informationen, die für den Verbindungsaufbau benötigt werden, können in sogenannten DriverPropertyInfo -Objekten gespeichert werden.

Es definiert eine Reihe von Attributen:

- name: der Bezeichner des Feldelements
- value: der Wert
- description: ein Beschreibungstext
- required: ein boolean-Wert, der angibt, ob der Wert optional oder notwendig ist
- choices: mögliche Werte, die ausgeählt werden können

Methode:

DriverPropertyInfo[] get PropertyInfo (String url, java.util.Properties info)

throws SQLException;

3.4 Die Datenbankverbindung

3.4.1 Das Connection-Objekt

Nachdem ein Treiber geladen wurde, wird die Verbindung mit der Datenbank aufgebaut. Eine Anwendung kann mehrere Verbindungen zu einer oder zu mehreren Datenbanken öffnen. Über eine solche Verbindung werden SQL-Statements zur Datenbank gesendet und Transaktionsabläufe gesteuert . Außerdem können Metainformationen abgefragt werden.

Die dazu benötigte Klasse ist java.sql.Connection. Ein Connection -Objekt repräsentiert eine Verbindung zur Datenbank. Eine Verbindung wird mit dem Aufruf der Methode getConnection im Treibermanager geöffnet. Es gibt drei verschiedene getConnection-Methoden, die sich nur in der Art ihrer Parameter unterscheiden:

- nur URL

static Connection getConnection (String url)

throws SQLException;

Verwendung: wenn zum Verbindungsaufbau nur eine URL benötigt wird und kein Passwort abgefragt wird

- URL und Property-Infos

static Connection getConnection (String url, java.util.Properties info)

throws SQLException;

Verwendung: wenn zusätzliche Daten beim Aufbau der Verbindung benötigt werden oder Daten wie User und Passwort aus einer Properties-Datei gelesen werden

- URL, User, Passwort

static Connection getConnection (String url, String user, String password)

throws SQLException;

Verwendung: bei den -blichen Datenbankverbindungen, wo in der Regel immer auch ein Benutzername und ein Passwort abgefragt wird

Eine Datenbankverbindung kann also folgendermaßen aufgebaut werden:

Connection con;

con = DriverManager.getConnection (url, user, password);

Bei Aufruf dieser Methode versucht der Treibermanager einen passenden Treiber zu finden, der den angegebenen URL akzeptiert. Hat er ihn gefunden, wird die Verbindung hergestellt.

3.4.2 Der JDBC-URL

Jede der drei getConnection -Methoden verwendet den URL, um eine Datenbank zu lokalisieren. Diese URL kann sehr flexibel gestaltet werden und hängt sowohl vom Datenbanktreiber, als auch vom Subprotokoll (z.B. ODBC) oder von der Zugriffsart (lokal/Netzwerk) ab. Das hat folgende Vorteile:

- kein Benutzer eines Clients muss Kenntnisse in Systemadministration besitzen, um auf eine Netzwerk-Datenbank zugreifen zu können, wie es bei Benutzung von ODBC der Fall sein kann

- Adressen können mit Hilfe eines Netzwerk-Namensdiensts aufgelöst werden, ohne dass spezielle Netzwerkadressen im URL festgelegt werden müssen. Es können sämtliche erhältlichen Name-Server verwendet werden

- unterschiedliche Datenbanktreiber können unterschiedliche Namensgebungen verwenden. So kann ein JDBC-ODBC-Treiber einfache ODBC Benamung verwenden oder ein Netzwerkserver die Übertragung des Hostnamens und des Ports fordern.

Sun hat ihr Benamungssystem dem des World Wide Web angepasst, weil es standardisiert ist und die geforderten Eigenschaften erf-llt. JDBC URLs sind folgendermaßen strukturiert:

jdbc:<subprotocol>:<subname>

<subprotocol> steht für eine spezielle Art von Datenbankverbindungs-Mechanismus, der vom Treiber definiert wird.

Der Syntax von <subname> hängt vom <subprotocol> ab und bezeichnet die Datenquelle.

Beispiele:

- JDBC-ODBC-Bridge:

jdbc:odbc:personalstamm

- Netzwerkadresse:

jdbc:mysql://localhost:8080/Fluss

- Naming Service:

jdbc:mynamingservice:gehaltskonten

Das ODBC besitzt die weiteste Verbreitung unter den Datenbankverbindungs-APIs. Deshalb gibt es laut Definition in JDBC einen speziellen Syntax, der dieses Protokoll unterstützt:

jdbc:odbc:<data-source-name>[;<attribute-name>=<attribute-value>]*

Beispiele:

jdbc:odbc:guv

jdbc:odbc:arbeitsplan;UserID=chef;password=rosie12

3.4.3 Schließen einer Verbindung

Eine einmal geöffnete Datenbankverbindung kann natürlich auch wieder geschlossen werden. Dies wird im Normalfall vom Garbage Collector übernommen. Hierbei muss man aber damit rechnen, dass das automatische Schließen erst nach längerer Zeit durchgeführt wird und Ressourcen des DBMS gebunden werden. Deshalb gibt es eine spezielle close() -Methode, die eine Verbindung explizit schließt. Der Zustand einer Verbindung kann mit der Methode isClosed() abgefragt werden.

Methode:

public void close() throws SQLException;

public boolean isClosed() throws SQLException;

3.4.4 Die Schnittstelle PooledConnection

Diese in JDBC 2.0 neu eingeführte Schnittstelle implementiert einen Mechanismus, um die Zeit eines Verbindungsab- oder aufbaus zu verringern. In einem Connection Pool werden Datenbankverbindungen vorgehalten, die beliebig oft wiederverwendet werden können. Sie ist im Package javax.sql.PooledConnection enthalten.

Eine sinnvolle Einsatzmöglichkeit für das Connection Pooling ist eine Multi-Tier-Architektur, wie sie bei web-basierten Applikationen oder z.B. im Enterprise Java-Beans-Umfeld eingesetzt wird. Weil der Verbindungsaufbau in der Regel eine zeit- und ressourcenraubende Aktion darstellt, kommt es durch den Einsatz dieser Schnittstelle zu entscheidenden Performance-Verbesserungen.

Beispiele für Anwendungsgebiete sind z.B. ein web-basiertes Flugbuchungssystem oder ein Online-Shop. Solche Web-Applikationen haben unter Umständen mit tausenden Datenbankzugriffen gleichzeitig umzugehen. W-rde für jede Anfrage eine Verbindung aufgebaut und sofort danach wieder geschlossen, würde die Performance stark zurückgehen. Mit dem Connection Pooling können so offene Verbindungen genutzt und wesentlich bessere Antwortzeiten garantiert werden.

Die Nutzung eines Connection Pools läuft folgendermaßen ab:

Eine geöffnete Verbindung (z.B. durch ein Servlet oder eine Java-Bean) wird beim Schließen nicht wirklich beendet, sondern nur markiert und in den Connection Pool aufgenommen. Versucht eine andere Anwendung eine Verbindung mit den gleichen Eigenschaften aufzunehmen, wird zuerst geprüft, ob eine Verbindung im Pool vorhanden ist. Nur wenn keine existiert, wird eine neue Verbindung aufgebaut.

Man unterscheidet zwischen physischen und logischen Verbindungen. Physische Verbindungen stellen eine Instanz von javax.sql.PooledConnection dar und sind real vorhanden. Logische Verbindungen sind Instanzen der Schnittstelle java.sql.Connection, die auf eine physische Verbindung verweist. Physische Verbindungen und ihre Zuordnung zu logischen Verbindungen werden vom DataSource -Objekt verwaltet, das vom Runtime-Environment (z.B. Servlet-Engine) erzeugt wird.

Ein DataSource -Objekt speichert Informationen über die Verbindung zu Datenbanken, wie den Datenbanknamen, das Passwort oder den Port. Hierzu nutzt es den JNDI -Service (Java Naming and Directory Interface), der dynamischen Zugriff auf Datenquellen innerhalb eines Netzwerks ermöglicht und den Programmierer so von einer festen Implementation der Datenbankadresse entbindet. Dadurch kann auf Datenquellen zugegriffen werden, die durch die Änderung der Netz-Infrastruktur an einem anderen Ort liegen.

Das eigentliche Connection-Pooling ermöglicht dann die Schnittstelle ConnectionPoolDataSource, die von DataSource erbt und sie um das Connection-Pooling erweitert. Um das Prinzip des Connection Pools anschaulicher zu machen, hier eine Übersichtsgrafik:

Abbildung in dieser Leseprobe nicht enthalten

Abb. Connection-Pool

3.5 Escape-Klauseln

Die JDBC-API unterstützt datenbank-unabhängige Programmierung. Leider scheitert dieser Vorteil oft an unterschiedlichen SQL-Implementierungen der DBMS. So können sich Bezeichner und Parameter von Skalarfunktionen ebenso unterscheiden, wie der Syntax bestimmter Anfragen oder der Aufbau von Datum- und Zeitliteralen. Der äußere Verbund, der im SQL-92-Standard formuliert ist, wird z.B. in DB2 exakt übernommen:

SELECT a.Datum, a.Zeit, b.FlussID,

FROM Werte a LEFT OUTER JOIN Fluesse b

ON a.FlussID = b.FlussID;

In Oracle sieht dieselbe Anweisung so aus:

SELECT a.Datum, a.Zeit, b.FlussID,

FROM Werte a , Fluesse b

WHERE a.FlussID = b.FlussID(+);

Um solche Probleme zu lösen verwendet JDBC sogenannte Escape-Klauseln in SQL-Anweisungen, wie sie auch bei ODBC eingesetzt werden. Eine solche Klausel wird in geschweifte Klammern geschrieben und besteht aus einem Schlüsselwort und einer Menge von Parametern:

{ keyword ... parameter ...}

Mit Hilfe dieses recht einfachen Syntax können z.B. Datumsformate und Skalare in das DB-spezifische Format konvertiert werden oder Outer Joins ausgeführt werden.

Der jeweilige Datenbanktreiber erkennt die Escape-Klausel und übersetzt sie in den im DBMS verwendeten SQL-Syntax. Mit der setEscapeProcessing -Methode der Schnittstelle Statement kann die Interpretation der Klauseln ein- oder ausgeschaltet werden.

Methode:

public void setEscapeProcessing (boolean enable)

throws SQL-Exception;

3.6 SQL-Anweisungen

3.6.1 Das Statement-Objekt

Datenbankanweisungen über die JDBC-API werden als SQL-Anweisungen an die Datenbank gesendet. Wie bereits erwähnt ist JDBC eine Low-Level-API, die SQL benötigt, um auf ein DBMS zugreifen zu können und unterstützt keine höherwertigen Abstraktionskonzepte.

Für jede SQL-Anweisung wird ein Statement-Objekt instantiiert. Diese Objekte werden über eine Methode der Connection-Schnittstelle erzeugt. Je nach Einsatzzweck unterscheidet man drei Arten von Statements:

- Statement:

Basisschnittstelle für alle verwandten Klassen, die einfache Anweisungen ohne Parameter verarbeiten kann.

Methode:

public Statement createStatement()

throws SQLException;

- PreparedStatement:

Schnittstelle, die vorkompilierte Anweisungen ausführt. Das vorkompilieren hat den Vorteil der schnelleren Durchführung bei mehrfachem Aufruf mit verschiedenen Parameterwerten.

Methode:

public preparedStatement prepareStatement(String sql)

throws SQLException;

- CallableStatement

Schnittstelle, die den Aufruf von im DBMS gespeicherten Prozeduren ermöglicht.

Methode:

public CallableStatement prepareCall(String sql)

throws SQLException;

3.6.2 Die execute-Methoden

Eine Anfrage an eine Datenbank wird in einem Statement -Objekt gespeichert. Damit sie durchgeführt wird, muss eine sogenannte execute -Methode ausgeführt werden. Diese Methode hat drei verschiedene Ausprägungen:

- executeQuery

Diese Methode gibt ein einzelnes ResultSet- Objekt zurück. Typischerweise wird eine SQL „SELECT“-Anweisung ausgeführt.

Methode:

public ResultSet executeQuery(String sql)

throws SQLException;

Beispiel:

Statement state = con.createStatement();

ResultSet result = state.executeQuery(

„SELECT * FROM FLUESSE“);

- execute

Diese Methode gibt mehrere results zurück, die mit verschiedenen Methoden ausgewertet und bearbeitet werden können. Damit können sämtliche SQL-Anweisungen durchgeführt werden. Gibt true zurück, wenn das Ergebnis ein ResultSet -Objekt ist, false wenn es keine weiteren Ergebnisse gibt, oder ein update count durchgeführt wird.

Sun empfiehlt, diese Methode anzuwenden, wenn im Voraus nicht geklärt werden kann, ob die Anfrage Werte zurückgibt oder nicht. Im Zusammenhang mit dieser Methode, sollten auch die Methoden getMoreResults, getResultSet, getUpdateCount eingesetzt werden.

Methode:

public boolean execute(String sql)

throws SQLException;

- executeUpdate

Mit dieser Methode können die Anweisungen INSERT, UPDATE, DELETE sowie andere , die keine Werte zurückgeben(DDL- oder DML-Anweisungen wie „CREATE TABLE“) durchgeführt werden. Rückgabewert ist die Zahl der Datensätze oder 0, wenn das SQL-Statement nichts zurückgibt.

Methode:

public int executeUpdate(String sql)

throws SQLException;

Beispiel:

int anzTupel = state.executeUpdate(

„DELETE FROM Werte WHERE Datum = '2001-01-10'“);

3.6.3 Ergebnisse von Anfragen

3.6.3.1 Die Schnittstelle Statement

Bei der Ausführung von SQL-Anfragen, die Werte zurückgeben (z.B. SELECT) wird ein sogenanntes ResultSet -Objekt erzeugt, welches das Ergebnis der Anfrage enthält. In der Schnittstelle Statement gibt es verschiedene Methoden, um auf das ResultSet -Objekt zuzugreifen. Die wichtigsten Methoden sind:

[...]

Details

Seiten
144
Erscheinungsform
Originalausgabe
Jahr
2001
ISBN (eBook)
9783832452414
ISBN (Buch)
9783838652412
Dateigröße
1.3 MB
Sprache
Deutsch
Katalognummer
v220766
Institution / Hochschule
Hochschule Reutlingen – Wirtschaftsinformatik
Note
1,3
Schlagworte
servlets jdpl applets- applications

Autor

Teilen

Zurück

Titel: Programmierung mit Servlets und Applets in Java