This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Dietmar Abt s Maste rkurs Clien t/Server-Programm ierung mit Java
Dietmar Abts
Masterkurs ClientjServerProgrammierung mit Java Anwendungen entwickeln mit Standard-Technologien: JDBC, UDP, TCP, HTTP, XM L-RPC, RMI , JMS und JAX-WS Mit 81 Abbildungen 3., erweiterte Auflage STUDIUM
VIEWEG+ TEUBNER
Bibliografische Informationd der Deutschen Nationalb ibliothek Oie Deutsche Nationalbibliothek verzeichnet diese Publikat ion in der Deutsch en Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über abrufbar.
Das in diesem Werk enthal tene Programm-Material ist mit keiner Verpfl ichtung oder Garantie irgendeiner Art verbunden . Der Autor übern immt infolgedessen keine Verantwortung und wird keine daraus folgende oder sonstige Haftung übernehmen, die auf irgendeine Art aus der Benutzung dieses Programm-Materials oder Teilen davon entsteht. Höchste inhaltliche und technische Qualität unserer Produkte ist unser Ziel. Bei der Produktion und Auslieferung unserer Bücher wollen wir die Umwelt schonen: Dieses Buch ist auf säurefreiem und chlorfrei gebleichtem Papier gedruckt. Oie Einschweißfolie besteht aus Polyäthylen und damit aus organischen Grunds toffen, die weder bei der Herstellung noch bei der Verbrennung Schadstoffe freisetzen.
1. Auflage 2003 Diese Auflage erschien unter dem Titel 2. Auflage 2007 3., erwe iter te Auflage 2010
Vorwort zur dritten Auflage Komponenten verteilter Anwendungen kooperieren im Netz miteinander und versuchen dabei die Ressourcen der beteiligten Systeme optimal zu nutzen. Eine sehr verbreitete Anwendung dieser Art ist das World Wide Web. Verteilte Systeme stellen aber auch hohe Anforderungen an die Softwareentwickler. Diese müssen sich mit Problemen der Kommunikation und Koordination nebenläufiger Prozesse in einem heterogenen Umfeld beschäftigen. Moderne Programmierkonzepte, Frameworks und eine ausgereifte technische Infrastruktur bieten Unterstützung bei der Entwicklung verteilter Anwendungen. Das vorliegende Buch bietet eine kompakte Einführung in aktuelle Technologien auf der Basis von Java SE mit zahlreichen Programmbeispielen und übungsaufgaben. Die verschiedenen Themen können mit Grundkenntnissen der Programmiersprache Java erarbeitet werden. Der Quellcode von über 110 Programmbeispielen (inkl, Lösungen zu den Aufgaben) ist im Internet auf der Website des Verlags direkt neben den bibliographischen Angaben zu diesem Buch verfügbar,
http:/ /www. viewegteubner.de Gegenüber der zweiten Auflage "WUrden zahlreiche Verbesserungen vorgenommen und neue Programmbeispiele und Aufgaben hinzugefügt. Sie sind für die aktuellen Versionen der hier eingesetzten Produkte und Frameworks CMySQL. Apache Derby. Apache Tomcat, Apache XML-RPC. Apache ActiveMQ. Metro Web Service Stack) lauffähig.
Einige neue Themen "WUrden aufgenommen: •
Bearbeitung von JüBC-Rowsets ohne Verbindung zur Datenbank,
•
Gruppenkommunikation mit Multicasting,
•
Nutzung des Pakets com.sun.net.httpser ver zur Implementierung von Webservern,
•
Nachrichtenfilter und Transaktionssteuerung bei JMS und
•
Entwicklung von Web Services mit JAX-WS.
Danken möchte ich zum Schluss Frau Dr. Christel Roß vom Lektorat IT für die gute Zusammenarbeit sowie meinen Leserinnen und Lesern, Studierenden und Fachkollegen für die guten Vorschläge. die in dieser Auflage berücksichtigt wurden. Ich hoffe. dass Sie viel Spaß bei der Erarbeitung des Stoffes und der Entwicklung eigener Programme haben. Ratingen, Mai 2010
Dietmar Abts
abts@hs nlederrhel n.de
Inhaltsverzeichnis 1
Einleitung und Grundlagen
1
1.1
Vorbemerkungen
1
1.2
Verteilte Systeme
5
1.3
Das Client/Server-Modell
8
1.4
Mehrstufige Architekturen
11
1.5
Middleware und Transparenz
13
1.6
Grundbegriffe des lntemets
18
1.7
Die Klasse InetAddress
23
1.8
Aufgaben
25
2
Datenbankanwendungen mit]DBC
27
2.1
Die Architektur von ]DBC-Anwendungen
27
2.2
Erste Beispiele Die Beispiel-Datenbank 2.2.1 Verbindungsaufbau 2.2.2 Datenbankabfragen mit SELECT 2.2.3
31 31 33 37
2.3
]DBC-Datentypen
41
2.4
Ausführung von SQL-Anweisungen Transaktionen 2.4.1 2.4.2 Datenbankänderungen Prepared Statements 2.4.3
45 45 46 55
2.5
Ein einfaches Frontend für SQL-Datenbanken
58
2.6
Speicherung großer Objekte
64
2.7
Navigation und Änderungen in der Ergebnismenge
68
2.8
CachedRowSet - eine verbindungslose Ergebnismenge
74
2.9
Exkurs, XML-Dokumente aus SQL-Abfragen erzeugen
80
2.10
Exkurs, Stored Procedures
91
2.11
Aufgaben
95
3
Verbindungslose Konuuunikation mit VDP
99
3.1
Das Protokoll DDP
99
3.2
DatagramSocket und DatagramPacket
3.3
Ein Echo-Server und -Client
103
3.4
Feste Verbindungen
106
3.5
OnIine-Unterhaltung
109
3.6
Punkt-zu-Mehrpunkt-Verbindungen
113
100
VIII
Inhaltsverzeichnis
3.7
Aufgaben
116
4
Client/Server-Anwendungen mit TCP
117
4.1
Das Protokoll TCP
117
4.2
TCP-Sockets
118
4.3
Ein Echo-Server und -Client
121
4.4
Ein Download-Programm
128
4.5
Ein Chat-Programm
135
4.6
Klassen über das Netz laden
143
4.7
Remote Procedure Call
150
4.8
Thread-Pooling
156
4.9
Ein Framework für TCP-Server
158
4.10
Aufgaben
162
5
Implementierung eines HTTP-Servers
165
5.1
Das Protokoll HTTP
165
5.2
Ein einfacher File-Server
175
5.3
Ein HTTP-Serverfür SQL-Abfragen
179
5.4
Ein einfacher Webserver
186
5.5
Webseiten dynamisch erzeugen
192
5.6
Protokollierung von HTTP-Nachrichten
200
5.7
Aufgaben
202
6
XML Remote Procedure Calls (XML-RPC)
205
6.1
Grundkonzept und ein erstes Beispiel
205
6.2
XML-RPC-Datentypen
212
6.3
Komplexe Datenstrukturen
221
6.4
Dynamische Proxies
233
6.5
Filterung von IP-Adressen
235
6.6
Einbettung von XML-RPC in Apache Tomcat
237
6.7
Nutzung einer Erweiterung in Apache XML-RPC
243
6.8
Aufgaben
247
7
Entfernter Methodenaufruf mit RMI
249
7.1
Remote Method Invocation
249
7.2
Dienstauskunft
259
7.3
Transport by reference
260
7.4
Mobile Agenten
264
Inhaltsverzeichnis
IX
7.5
CaIIbacks
268
7.6
Exkurs, RMI mit HOP
274
7.7
Aufgaben
278
8
Nachrichtendienste mitJMS
281
8.1
Java Message Service
282
8.2
Das Point-to-Point-ModeII
285
8.3
Das Request-Reply-ModeII
297
8.4
Das Publish-Subscribe-ModeII
301
8.5
Dauerhafte Subscriber
305
8.6
Filtern von Nachrichten
310
8.7
Transaktionen
314
8.8
FaIIbeispiel Händler/Lieferant
319
8.9
Aufgaben
325
9
Web Services mitJAX-WS
327
9.1
Basiskonzepte von Web Services
327
9.2
Entwicklungsplattform und API
332
9.3
Ein erstes Beispiel
333
9.4
Ein Web Service zur Artikelverwaltung
338
9.5
Web Services mit Tomcat veröffentlichen
343
9.6
Oneway-Operationen
344
9.7
Asynchrone Aufrufe
346
9.8
Web Services und binäre Daten
351
9.9
Top Down oder Bottom Up
356
9.10
Aufgaben
361
Das Werkzeug Apache Ant
363
Internet-Quellen
367
Literaturhinweise
369
Stichwortverzeichnis
373
1
Einleitung und Grundlagen
1.1
Vorbemerkungen
Java wird heute als sehr gut geeignete Programmiersprache für große Intemet- und Intranet-Anwendungen insbesondere auf der Serverseite eingesetzt. Die Plattformunabhängigkeit, reichhaltige Klassenbibliotheken und die Unterstützung zahlreicher APls (Application Programming lnterl'aces) macht Java zur bevorzugten Sprache für verteilte Anwendungen in einem heterogenen Umfeld, Das vorliegende Buch beschäftigt sich mit der Implementierung Zielsetzung von Client/Server-Anwendungen auf Basis der IntemetProtokolle, Angesichts des Unrt'angs der Themen musste eine Auswahl getroffen werden, die auch in einem vier Semesterwochenstunden umfassenden Kurs behandelt werden kann. Ziel des Buches ist es , über die Themenvielfalt angemessen zu informieren und in die Einzelthemen systematisch mit der nötigen Tiefe einzuführen. Besonderer Wert "WUrde dabei auf praxisnahe Programmbeispiele und Übungsaufgaben gelegt. Dieses Buch ist in neun Kapitel gegliedert, die jeweils einen Aufbau des Buches Schwerpunkt behandeln, Obwohl die Kapitel weitestgehend in sich geschlossen sind und unabhängig voneinander bearbeitet werden können, wird empfohlen, diese in der hier vorgegebenen Reihenfolge durchzuarbeiten. Die vorgestellten Themen werden anhand vollständiger, lauffähiger Programmbeispiele verdeutlicht übungsaufgaben am Ende eines jeden Kapitels regen zur selbständigen Beschäftigung mit dem dargebotenen Stoff an, Das Buch hat folgende Kapitel,
Einleitung und Grundlagen Dieses Kapitel gibt eine Einführung in die verteilte Verarbeitung auf der Basis des Client/Seruer-Modells und stellt mehrstufige Architekturen VOL Sinn und Zweck von Middleware werden an einem ersten Programmbeispiel erläutert. Hier werden auch grundlegende Begriffe des lntemets (wie IPAdressen, Adressauflösung), die zum Verständnis der übrigen Kapitel nötig sind, erklärt 2 Datenbankanwendungen mit JDBC In mehrstufigen Architekturen verteilter Systeme, so z. B. bei dynamischen Wehanwendungen, erzeugt der Applikationsserver Datenbankabfragen und schickt sie zur Ausführung an den Datenbankserver. JDEC ist das Standard-API für den Zugriff auf relationale Datenbanken mittels SQL-Anwei-
2
1
Einleitung und Grundlagen
sungen, die zur Laufzeit dynamisch aufgebaut werden können. Wir beschäftigen uns mit den Grundlagen von ]DBC. die interessante Client/Server-Anwendungen in den folgenden Kapiteln ermöglichen. Ein Exkurs zeigt. wie das Ergebnis einer SQL-Abfrage in ein XML- bzw. HTML-Dokument transformiert werden kann. Ein weiterer Exkurs gibt eine kurze Einführung in die ]DBC-Schnittstelle zu Stared Procedures am Beispiel von MySQL. 3 Verbindungslose Kommunikation mit UDP Das User Datagram Protacol (UDP) ist neben TCP ein wichtiges Transportprotokoll im Internet. Es ermöglicht. mit geringem Aufwand Datenpakete zu versenden, ohne vorher eine Verbindung zum Empfänger aufbauen zu müssen. Wir entwickeln u. a. ein Programm, mit dem Benutzer an verschiedenen Rechnern im Netz eine Unterhaltung online führen können, und stellen das Multicasting vor. 4 ClienVServer-Anwendungen mit TCP Fast alle Internet-Dienste nutzen das Transmission Contral Protacol (TCP). Im Gegensatz zu UDP stellt TCP eine verlässliche Ende-zu-Ende-Verbindung zwischen Sender und Empfänger her. Der Anwendungsentwickler muss sich bei der übertragung eines Datenstroms nicht um die Aufteilung in einzelne Pakete kümmern. Sockets sind die Endpunkte einer TCP-Verbindung. Sie ermöglichen es. das Senden und Empfangen von Daten wie das Schreiben und Lesen von Dateien zu behandeln. Multithreading versetzt den Server in die Lage. mehrere Client-Anfragen gleichzeitig zu bearbeiten. Wir entwickeln u. a. ein Dateiübertragungsprogramm sowie eine Anwendung, mit der ein so genanntes "Chatten" innerhalb einer Gruppe von Teilnehmern möglich ist. Der Einsatz wiederverwendbarer Threads eines Thread-Pools reduziert den Ressourcenverbrauch und kann die Effizienz der Anwendung erhöhen. Das Kapitel wird mit der Vorstellung eines Framewarfes für TCP-Server abgeschlossen. 5 Implementierung eines HTTP-5ervers Dieses Kapitel behandelt die Grundlagen des Anwendungsprotokolls Hypertext Transfer Protacol (HTTP). das die Kommunikationsfunktionalität des Warld Wide Weh definiert. HTTP verwendet auf der Transportschicht TCP. Wir implementieren u. a. einen speziellen HTTP-Server, der beliebige SQL-Anweisungen vom Webbrowser entgegennimmt und ausführt, sowie einen einfachen Webserver zur übertragung von statischen Webseiten und anderen Ressourcen. Dieser Webserver wird dann so erweitert, dass anwendungsspezifische Klassen zur Laufzeit nachgeladen werden können, um mit deren Hilfe Webseiten ad hoc zu generieren.
1.1
Vorbemerkungen
6 XML Remote Procedure Call(XML-RPC) In diesem Kapitel behandeln wir Remote Procedure Calls (RPC) und zeigen anhand des XMI-RPC-Verrahrens , wie HTTP und XML als Datenaustauschformat für den entfernten Prozeduraufruf eingesetzt werden können. Auf dieser Basis implementieren wir einfache Web Services und zeigen, wie die XML-RPC-Verarbeitung in Apache Tomcat eingebunden werden kann. 7 Entfernter Methodenaulruf mit RMI Remote Method Invocation (RMI) ist eine einfache und elegante Art, ClientiServer-Anwendungen in Java zu implementieren, Der Entwickler kann sich ganz auf die fachliche Funktionalität konzentrieren, netzspezifische Details bleiben ihm im Gegensatz zur Socket-Programmierung weitestgehend verborgen, Entfernte Methoden werden prinzipiell wie lokale Methoden aufgerufen, Mit Hilfe der Objektserialisierung kann RMI beliebige Objekte als Aufrufparameter entfernter Methoden übertragen, Dabei kann auch der zugehörige Bytecode, sofern er lokal nicht vorhanden ist, ad hoc transferiert werden, Damit sind dann sehr flexible Anwendungen möglich, mobile Agenten, Anwendungen mit automatischem Rückruf (Callback) bei Eintritt bestimmter Ereignisse, Das Kapitel schließt mit einem Exkurs, der zeigt, wie RMI mit Hilfe des CORBA-Protokolls IIOP CORBA-fähig gemacht werden kann, 8 Nachrichtendienste mit JMS Ein wichtiger Standard für die asynchrone Verteilung von Nachrichten auf der Basis nachrichtenorientierter Middleware ist der Java Message Service UMS), Bekannte Kommunikationsmodelle, um Nachrichten auszutauschen sind: das Pointto-Point-Modell und das Publisb-Subscribe-Modell. In diesem Kapitel werden die wichtigsten Funktionen des JMS-API vorgestellt und anhand vieler Beispiele demonstriert. 9 Web Services mit JAX-WS Neben den Basistechnologien für SOAP-basierte Web Services wird das Java API for XMI Web Services (JAX- WS) vorgestellt, mit dem auf einfache Art und Weise normale Klassen mit Hilfe von geeigneten Annotationen zu Web Services erweitert werden können (Code-First-Ansatz), Neben asynchronen Aufrufen von Web Services wird die Übertragung binärer Daten mit MTOM sowie die Generierung von Quellcode aus dem WSDL-Dokument (Contract-First-Ansatz) vorgestellt
3
1
4
Einleitung und Grundlagen
Erwartungen an
Von den Leserinnen und Lesern werden erwartet:
den Leser
•
Grundlegende Java-Kenntnisse, insbesondere: Konzepte der objektorientierten Programmierung, Dateiverarbeitung und Thread-Programmierung .
•
Grundlegende Kenntnisse auf dem Gebiet der relationalen Datenbanken und SQL.
•
Das Wissen um Internet und World Wide Web und der Umgang mit einem Webbrowser.
•
Grundlagenkenntnisse in HTML und XML.
Buchtipps hierzu finden Sie am Ende des Buches im Abschnitt "Literaturhinweise" . Die Beispiele
Alle Beispielprogramme und Lösungen zu den übungsaufgaben wurden mit Hilfe von Java SE 6 unter Windows Vista entwickelt und im Netz getestet. Selbstverständlich sind die hier vorgestellten Programme ohne Änderung auch auf UNIX!Linux-Systemen lauffähig. Als Entwicklungsumgebung wurde Eclipse eingesetzt. Selbstverständlich kann auch jede andere Java-Entwicklungsumgebung genutzt werden.
Werkzeug Ant
Zur Vereinfachung der Programmentwicklung wurde das BuildWerkzeug Ant der Apache Software Foundation genutzt (siehe Quellenverzeichnis am Ende des Buches). Im Anhang befindet sich eine kurze Anleitung zur Installation und Nutzung von Ant. Weitere Einzelheiten, z. B. zu den eingesetzten Datenbanksystemen und JDBC-Treibem. können den entsprechenden Kapiteln entnommen werden.
Download
Sämtliche Programme und Lösungen stehen im Internet zur Verfügung und können heruntergeladen werden. Hinweise zu weiteren Tools und Klassenbibliotheken erfolgen in den Kapiteln. in denen sie erstmalig benutzt werden. Das Quellenverzeichnis am Ende des Buches enthält die Bezugsquellen.
1.2
Verteilte Systeme
1.2
5
Verteilte Systeme
Umfassende gesellschaftliche und wirtschaftliche Entwicklungen zwingen Unternehmen, sich flexibel an Veränderungen anzupassen und schnell auf neue Marktsituationen zu reagieren. Betrieblichen Anwendungssystemen kommt hier eine besondere Bedeutung zu. Wie sind die Anwendungssysteme zu implementieren, um höchstmögliche Flexibilität, Verlässlichkeit und Anpassbarkeit zu erreichen?
Monolithische Systeme (alle Anwendungen laufen auf einem Rechner) sind nur noch bedingt geeignet Heute ist die Strukturierung der Software in Client und Server weit verbreitet. Die folgenden Faktoren haben die fundamentalen Änderungen wesentlich beeinflusst: •
Leistungsexplosion in der Mikroprozessortechnik,
•
schnelle Datennetze,
•
Fortschritte in der Softwaretechnik,
•
Abkehr von hierarchischen Organisationsstrukturen,
•
Bedürfnis nach Integration bisher nicht integrierter Systeme,
•
Internet-Technologie.
Waren die 1960er Jahre noch durch Batch-Processing-Systeme, Lochstreifen und Lochkarten geprägt, konnten Anfang der 1970er Jahre in so genannten Timesharing-Systemen Transaktionen im Dialog gestartet werden, Minicomputer (UNIX-Systeme) erschienen als zweiter Gerätetyp neben dem Host. Anfang der 1980er Jahre kamen die Personal Computer als Stand-alone-Systeme oder vemetzt im LAN hinzu, Der wichtigste Trend ab 1990 ist durch das Aufkommen von ClientlServer-Systemen für die verteilte Verarbeitung bestimmt. Hierzu zählen insbesondere die heutigen Internet-Anwendungen eWeb-Informationssysteme, E-Business-Anwendungen).
Verteilte Systeme ClientlServer-Systeme Personal Computer TimesharingSysteme Batch-ProcessingSysteme
I 1960
I 1970
1980
1990
2000
Bild 1.1.·
Entwicklungsstufen
6
1
Einleitung und Grundlagen
Jede Anwendung kann in drei wesentliche Schichten eingeteilt werden: 1. Präsentation Diese Schicht (auch Benutzungsschnittstelle genannt) hat die Aufgabe, den Dialog mit dem Benutzer zu steuern sowie Daten und Befehle an das System zu übermitteln.
2. Verarbeitung Diese Schicht enthält die Verarbeitungslogik Hier wird der eigentliche Nutzen der Anwendung erzielt. 3. Datenhaltung Diese Schicht hat die Aufgabe, die Daten abzuspeichem und bei Bedarf wieder zur Verfügung zu stellen. Bild 1.2 zeigt eine Webanwendung, die dem Benutzer das Produktangebot eines Unternehmens präsentiert. Die drei Schichten Präsentation, Verarbeitung und Datenhaltung sind jeweils auf eigenen Rechnern implementiert. Der Webbrowser ist die Benutzungsoberfläche, die die abgefragten Informationen grafisch präsentiert. Der Webserver erzeugt Datenbankabfragen, bereitet die Abfrageergebnisse als Webseite auf und übermittelt diese an den Client. Der Datenhankserver verwaltet den Produktkatalog und führt die Datenbankabfragen aus. Webbrowser, Webserver und Datenbankserver sind autonome Teilsysteme, die koordiniert miteinander kooperieren. Lokales Netz einer Firma
Bild 12.· Beispiel einer
Webanwendung Webbrowser
Webserver
Datenbankserver
Webbrowser
Verteiltes System
Verteilung bedeutet die Zuordnung von Funktionen und Daten auf mehrere Rechner. Ein Anwendungssystem wird dabei in funktionale Komponenten zerlegt, die dann bei der Installation im Netz so verteilt werden, dass die Komponenten optimal unterstützt werden. Abstrakt formuliert ist ein verteiltes System eine Menge von Komponenten, die kooperieren, um eine gemeinsame Aufgabe zu erfüllen.
1.2
Verteilte Systeme
7
Da es im Allgemeinen mehrere Varianten für die Installation der KonftgurationsKomponenten im Netz gibt, besteht das Konfigurationsproblem, problern die logiscbe Struktur der Anwendung auf die pbysiscbe Struktur (Rechner im Netz) so abzubilden, dass bei vorgegebenen Randbedingungen ein maximaler Nutzen erzielt wird. üb eine Verteilung überhaupt möglich ist, entscheidet sich bereits beim Entwurf der Anwendungssoftware. Der Entwurf muss unabhängig von einer späteren physischen Konfiguration sein. Dem Benutzer muss die konkrete physische Aufteilung verborgen bleiben,
logische Struktur
physische Struktur Sachbearbeiter
Sachbearbeiter
Bild 13·
Abbildung der logischen auf die physische Struktur
[5 Kundendal en Produktdaten
Auftragsbearbeitung
Produktdaten
Der Vergleich der verteilten Verarbeitung mit der großrechnerdominierten, zentralen Verarbeitung ergibt folgende Vor- und Nachteile,
•
Besseres Abbild der Realität Vorteile Die Verteilung unterstützt die "schlanke" Organisation. Leistungen werden dort erbracht, wo sie benötigt werden. Daten werden dort erfasst, wo sie im Geschäftsprozess entstehen.
•
Wirtscbaftlicbkeit Teure Ressourcen (Geräte, Softwarekomponenten) stehen mehreren Rechnern zur Verfügung und können gemeinsam genutzt werden. Automatisierte Aufgaben werden möglichst dort ausgeführt, wo sie am wirtschaftlichsten sind.
•
Bessere Lastverteilung Da mehrere Rechner mit jeweils eigenem Betriebssystem arbeiten, kann durch teilweise parallele Verarbeitung die Gesamtbearbeitungszeit verkürzt werden.
•
Bessere Skalierbarkeit Einzelne Komponenten können leichter an einen steigenden Bedarf angepasst werden.
•
Fehlertoleranz Beim Ausfall eines Rechners bleiben die anderen betriebsbe-
1
8
Einleitung und Grundlagen
reit. Die Aufgaben des ausgefallenen Rechners können oft von einem anderen Rechner übernommen werden. Nachteile
•
Höhere Komplexität durch Verteilung und Heterogenität Die Verteilung der Komponenten großer Systeme und die Heterogenität bei Betriebssystemen, Programmiersprachen, Datenformaten und Kommunikationsprotokollen stellen hohe Anforderungen an die Entwickler, wenn das Gesamtsystem überschaubar bleiben soll.
•
Komplexe Netzinfrastrukturen Netzarchitektur und Netzmanagement sind das Rückgrat der Systeme eines Unternehmens. Die Integration heterogener Systeme mit Komponenten unterschiedlicher Hersteller- und Standardarchitekturen stellt hohe Anforderungen an die Administration.
•
Höhere Sicherheitsrisiken Verteilte Systeme bieten mehr Möglichkeiten für unberechtigte Zugriffe. Im Netz übertragene Nachrichten können abgehört oder sogar verändert werden. Der Einsatz von Verschlüsselungsverfahren und Firewall-Systemen sind wirksame Schutzmaßnahmen.
Aufgabe von so genannten Verteilungsplattformen ist es, diese Komplexität beherrschbar zu machen.
1.3
Das Client/Server-Modell
Das ClientlServer-Modell ist das am weitesten verbreitete Modell für die verteilte Verarbeitung. Client/Server
ClientiServer bezeichnet die Beziehung , in der zwei Programme zueinander stehen. Der Client stellt eine Anfrage an den Server, eine gegebene Aufgabe zu erledigen. Der Server erledigt die Aufgabe und liefert das Ergebnis beim Client ab. Ein Kommunikationsdienst verbindet Client und Server miteinander. Ein ClientlServer-System besteht also aus
Eine Softwarearchitektur
•
einem oder mehreren Clients (Auftraggebern),
•
einem Server (Auftragnehmer) und
•
einem Kommunikationsdienst (Netzwerk, Vermittler).
Client und Server sind Programme, die auch auf demselben Rechner laufen können. Sie müssen also nicht notwendig über ein Netz verbunden sein. Das macht deutlich, dass es sich hierbei um eine Software- und keine Hardwarearchitektur handelt. In der Praxis werden natürlich Client und Server auf unterschiedlichen Rechnern verteilt, um die bekannten Vorteile eines verteilten Systems zu erzielen.
1.3
Das Client/Server-Modell
9
Gelegentlich werden auch die Trägersysteme (Rechner), auf de- Hardware-Sicht nen die Programme laufen, als Client bzw. Server bezeichnet. Wir nutzen die Begriffe Client bzw. Server als Homonyme. Aus dem Zusammenhang wird dann klar, welche Bedeutung gemeint ist. Ein Server kann die Erledigung einer Aufgabe weiter delegieren und dazu auf andere Server zugreifen, Er spielt dann selbst die Rolle eines Client, Der Webserver in Bild 1.2 hat diese Doppelfunktion.
Clien!
I.
Hnfrage
'I
1. Anfrage
Clien! 4. Antwort
Bild 1.4.· Client/Server-Modell
Server
Server/ Clien!
2. Anfrage
Server 3. Antwort
Im Folgenden sind charakteristische Merkmale von Client und Server zusammengefasst: •
Ein Programm wird vorübergehend zum Client, wenn es ei- Client-Merkmale nen Dienst von einem anderen Programm (Server) anfordert. Darüber hinaus kann das Frogramm andere Aufgaben lokal ausführen.
•
Der Client läuft in der Regel auf dem Rechner eines Benutzers und leitet den Kontakt mit einem Server aktivein.
•
Der Client kann im Laufe einer Sitzung auf mehrere Server zugreifen, kontaktiert aber aktiv nur jeweils einen Server.
•
Der Server ist ein Programm, das einen bestimmten Dienst Server-Merkmale bereitstellt. Er kann in der Regel gleichzeitig mehrere Clients bedienen.
•
Häufig werden Server automatisch beim Hochfahren des Rechners gestartet und beim Herunterfahren beendet.
•
Der Server wartet passiv auf die Verbindungsaufnahme durch einen Client.
•
Da Server eigenständige Programme sind, können mehrere Server unabhängig voneinander auf einem einzigen Rechner als Trägersystem laufen.
1
10
Einleitung und Grundlagen
• Parallelbetrieb
ist ein grundlegendes Merkmal von Servern. Mehrere Clients können einen bestimmten Dienst in Anspruch nehmen, ohne warten zu müssen, bis der Server mit der Erledigung der laufenden Anfrage fertig ist. In den späteren Java-Programmbeispielen werden die Client-Anfragen jeweils in einem neuen Thread bedient.
Bild 105'
Interaktionsarten
synchrone Kommunikation Client
~
Server
Anfrage
warte auf Antwort
r
Antwort
asynchrone Kommunikation Client
Server
Anfrage arbeite weiter
Rückruf mit Antwort
synchron/asynchron Die Interaktion zwischen Client und Server kann synchron oder asyncbron erfolgen (siehe Bild 1.5).
Bei der synchronen Kommunikation wartet der Client nach Absenden der Anfrage an den Server so lange, bis er eine Rückantwort erhält, und kann dann erst andere Aktivitäten ausführen. Im asyncbronen Fall sendet der Client die Anfrage an den Server und arbeitet sofort weiter. Beim Eintreffen der Rückantwort werden dann bestimmte Ereignisbehandlungsroutinen beim Client aufgernfen. Die durch den Server initiierten Rückrufe (Callbacks) erfordern allerdings zusätzliche Kontrolle beim Client, wenn ungewünschte Unterbrechungen der Arbeit vermieden werden sollen.
1.4
1.4
Mehrstufige Architekturen
11
Mehrstufige Architekturen
Bei einstufigen Architekturen wird die gesamte Anwendung (Präsentation, Verarbeitung und Datenhaltung) auf einem Rechner implementiert. Die Benutzerinteraktion erfolgt über Terminals, die direkt oder über einen Terminal- bzw. Kommunikationsserver an den Rechner angeschlossen sind. Verteilte Anwendungen haben eine mehrstufige Architektur Multi- Tier(Multi-Tier-Architektur) , Beispielsweise werden Applikations- Architekturen und Datenbankserver verschiedenen Ebenen zugeordnet. Bei der zweistufigen Architektur ist die Gesamtanwendung zwei Ebenen zugeordnet: Client und Server. Bild 1.6.· .00
•
Netz
Datenbankserver
Client
Für die Arbeitsteilung zwischen Client und Server existieren verschiedene Alternativen, je nachdem, wo die Schichten Präsentation, Verarbeitung und Datenhaltung angesiedelt sind. Bild 1.7 zeigt fünf Altemativen der Aufgabenverteilung bei der zweistufigen Architektur eines Client/Server-Systems. Von Alternative 1 bis 5 wird immer mehr Funktionalität auf den Client übertragen. Altemative 1 zeigt den Fall einer Webanwendung, bei der HTMLSeiten vom Server generiert und vom Webbrowser angezeigt werden. Ein Beispiel zur Alternative 2 ist eine Webanwendung, bei der eingegebene Daten über ein Applet im Webbrowser an den Server zur Verarbeitung weitergeleitet werden. Bei den Altemativen 3 bis 5 hat der elient einen Teil der Verarbeitung bzw, Datenhaltung selbst übernommen. Alternative 4 zeigt den Fall eines typischen Datenbankservers.
Eine zweistuftge Architektur
1
12 Bild 1.7.· Alternativen der ~ Aufgabenvetteilung
1
eI I
Datenhaltung Verarbeitung
~
rn
Pr äsent atioo
I1 I1 I
Daemettuno Verarbeitung
1 1 1 1
Einleitung und Grundlagen
Datenhaltunq Verarbeitung
II I
Datenh atuno
I1
Verarbeitung
I II II
Datenhaltung
I
Netz
"E
.s iJ
1
I
Pr äsent atioo
II
Präsentat ion
1 1
Verarbeitung Präsentation
2
II II
Präsent ation
4
3
Datenhaltung Verarbeitunq Präsentation
I I I
5
Bei der dreistufigen Architektur wird die Gesamtanwendung auf drei Ebenen aufgeteilt z.B, Client, Applikationsserver und Datenbankserver. Dieses Modell ist auch Basis vieler ERP-Systeme (Enterprise Resümee Planning). Bild 1.8.· Eine dreistuftge Architektur
o
Q Applikationsserver
Datenbankserver
Client
Insbesondere bei Internet- und Intranet-Anwendungen findet man eine vierstufige Architektur, bei der ein Webserver dem Applikationsserver vorgeschaltet ist. Bild 1.9· Eine vierstufige Architektur
In mehrstufigen Architekturen sind die Komponenten eines Anwendungssystems auf potentiell heterogene Trägersysteme in einem Netzwerk verteilt. Um die Komplexität des Gesamtsystems bei der Bedienung oder Entwicklung der Anwendung vor dem Benutzer bzw. Entwickler verborgen zu halten, bedarf es einer geeigneten Software-Infrastruktur, die die Interaktion zwischen den Komponenten in besonderer Weise unterstützt. Die verteilte Verarbeitung hat zu einem neuen Typ systemnaher Software geführt, der so genannten Middleuiare.
Middleware ist Kommunikationssoftware, die den Austausch von Informationen zwischen den verschiedenen Komponenten eines verteilten, heterogenen Systems unterstützt. Die Anwendungen werden dabei von den komplexen Details der internen Vorgänge abgeschirmt Bild 1,10·
Anwendungskomponenten 1
1
1
1
1
1
1
Middleware 1
Middleware ___I
1
1
1
_
Betriebssysteme, Protokolle, Netzwerk In der Informatik nennt man eine Sache transparent, wenn sie "durchsichtig", also nicht sichtbar ist. In verteilten Systemen sollen interne Abläufe und Implementie- Verteilung rungsdetails für den Benutzer oder Anwendungsentwickler nicht verbergen sichtbar sein. Verteilungstransparenz verbirgt die Komplexität verteilter Systeme. Es existieren mehrere untergeordnete Transparenzbegriffe, die jeweils einen bestimmten Aspekt bezeichnen. Beispiele hierfür sind:
•
Ortstransparenz Der Ort einer Ressource ist dem Benutzer nicht bekannt. Er identifiziert sie über einen Namen, der keine Information über ihren Aufenthaltsort enthält
•
ZugrijJstransparenz Die Form des Zugriffs auf eine Ressource ist einheitlich und unabhängig davon, ob die Ressource lokal oder auf einem entfernten Rechner zur Verfügung steht. Unterschiede verschiedener Betriebssysteme und Dateisysteme werden verborgen,
1
14
Einleitung und Grundlagen
•
Nebenläufigkeitstransparenz Der gleichzeitige Zugriff mehrerer Benutzer auf dieselbe Ressource (z.B, Datenbanktabelle) erfolgt ohne gegenseitige Beeinflussung und lässt keine falschen Ergebnisse entstehen.
•
Replikationstransparenz Sind aus Verfügbarkeits- oder Performance-Gründen mehrere Kopien einer Ressource (z. B. eines Datenbestandes) vorhanden, so merkt der Benutzer nicht, ob er auf das Original oder eine Kopie zugreift. Das System sorgt dafür. dass alle Kopien bei Änderungen konsistent bleiben.
•
Migrationstransparenz Ressourcen können von einem Rechner auf einen anderen verlagert werden, ohne dass der Benutzer dies bemerkt.
•
Fehlertransparenz Der Benutzer soll nicht mit allen auftretenden Fehlern im System konfrontiert werden. Das Auftreten von Fehlern und die Fehlerbehebung sollen vor dem Benutzer weitestgehend verborgen sein.
Middleware-Produkte stellen dem Entwickler die für die Anwendung benötigten Funktionen meist in Form eines API (Application Programming Interface) zur Verfügung. DatenbankMiddleware
Datenbank-Middleware ermöglicht den Zugriff auf unterschiedliche Datenbanksysteme, ohne das Anwendungsprogramm beim Wechsel des Datenbanksystems ändern zu müssen. Das JDBCAPI erlaubt es einem Java-Programm. SQL-Anweisungen an beliebige SQL-Datenbanken zu schicken (siehe Kapitel 2). Andere Middleware-Produkte verwenden eine synchrone Verbindung zwischen Client und Server, um Prozeduren nach dem RPC-Modell aufzurufen.
Bild 1.11.·
Server
Client
Entfernter Prozeduraufruf
f(x,y)
z
~
f (5, 101
r:">
y
~
: Middleware
1.5
Middleware und Transparenz
15
Der Remote Procedure Calt (RPC) versteckt den Aufruf einer auf RPC einem anderen Rechner im Netz implementierten Prozedur hinter einem lokalen Prozeduraufruf und bietet damit ein sehr vertrautes Programmiermodell an. Das Programm, das die Prozedur aufruft, agiert als Client, das Programm, das die aufgerufene Prozedur ausführt, als Server.
RMI (Remote Method Invocation) ist die objektorientierte Umsetzung des RPC-Modells für Java-Programme (siehe Kapitel 7), Die folgenden Programmbeispiele zeigen, wie einfach eine Anwendung mit lokalem Methodenaufruf in eine Client/ServerAnwendung mit entferntem Methodenaufruf (RMl) umgewandelt werden kann. Die im Beispiel benutze Methode berechnet die Summe zweier Zahlen, Zunächst die lokale Anwendung, public class AddServer ( public double add(double x, double y) {
return x
Programm 1.1
+ y;
public class AddClient ( public static void main(String[] args) (
AddServer serVlce
=
new AddServer();
System.out.println(service.add(2.3. 5.7));
Die beiden Klassen werden nun mittels eines Interface entkoppelt. Um ein Server-Objekt zu erzeugen, wird eine statische Methode benutzt, die den konkreten Namen der Implementierungsklasse (hier, AddServer) gegenüber dem Client verbirgt (Factory-Methode). public interface Add ( double add(double x. double y);
public class AddServer implements Add ( public double add(double x. double y) return x + y;
Programm 1.2
16
1
Einleitung und Grundlagen
public class AddFactory ( public static Add getAdd() return new AddServer();
public class AddClient ( public static void main(String[] args) ( Add service ~ AddFactory.getAdd(); System.out.println(service.add(2.3. 5.7));
Das Klassendiagramm in Bild 1.12 verdeutlicht die Zusammenhänge. Bild 1.12.·
«i nterface»
Klassendiagramm
Add
zu Programm 1.2
r------------
,m
'", ,e
1989
~
1993
s
3-15-010606- 0
Dick ens, Ch"rles
Der We ihn" chts" bend
Gebunden
ua
2006
3-257-20998-3
Dickens, Ch"rles
Nikol" s Nickleby
K" rtoniert
;ce
1997
3-257-21034-5
Dickens, Ch"rles
D"vid Copp erlield
K" rtoniert
3-257-21166 -1( Dicke ns, Ch"rles
Ble"kh" us
K" rtoniert
3-257-21405-7
Dick ens, Ch"rles
Die Pickwickier
K" rtoniert
ece e" es
3-257-21406-5
Dickens, Ch"rles
M"rtin Chu" lewit
K" rtoniert
en
3-351-030 44-4
Dickens, Ch"rles
W eihn" chten mit Dickens
Gebunden
3-458-3 2655-3
Dicke ns, Ch"rles
H" rte Zeiten
K" rtoniert
3-458-32733- 9
Dick ens, Ch"rles
Geschichte " us , wei Stiidten
3-458-32810-6
Dickens, Ch"rles
Ble"k House
3-458-3300 4-6
Dickens, Ch"rles
3-491-9600 7-1( Dickens, Ch"Jles
,C '
1982 1984 2002 1998
, , , , , ~
2005
..
e_' Bild 2.7:
n Ausschnitt aus der
12,6
C~ 14,9 14,9 14,9 12,9 13.9
in
200 4
C
11,5
K" rtoniert
m ,e;
1987
C
12,5
K" rtoniert
1 030
1988
C
1 011
1991
Nikol" us Nickleby
K" rtoniert
W eihn" chtserziihlungen
Gebunden
,~,
2000
3-538-0 5349-9
Dickens, Ch"rles
D"vid Copperliel d
Leinen
1 023
1955
3-538-06656-6
Dickens, Charles
Die Pickwickier
Gebunden
1 039
1997
3-538-0665 7-4
Dickens, Ch"rles
M" rtin Chu" lewit
Gebunden
1 000
1997
3-538-066 58-2
Dickens, Ch"rles
Nichol" s Nickleby
Gebunden
~%
1997
3-538-06982- 4
Dickens, Ch"rles
Nichol" s Nickleby
Gebunden
~%
2004
D" s Geheimnis des Edwin Drood
Leinen
3-7175-197 6-1( Dickens, Charles
verlOOL"
oe,
2001
C
, , , , , a
r
" "
9,95 44,9 12,9 12,9 12,9 24,9 24,9
Tabelle buch
2 Datenbankanwendungen mit JDBC
32 Primärschlüssel
Jede Tabelle enthält einen Prirnärschlüssel, der durch eine Spalte oder eine Kombination von mehreren Spalten repräsentiert wird. Der Wert des Primärschlüssels identifiziert höchstens eine Zeile der Tabelle, Die Schlüsselwerte einer Tabelle sind alle voneinander verschieden, Für die Tabelle buch bietet sich die Spalte isbn als Primärschlüssel an.
Fremdschlüssel
Um Verknüpfungen zwischen Tabellen herstellen zu können, werden so genannte Fremdschlüssel benötigt. Ein Frerndschlüssel einer Tabelle wird durch eine Spalte oder durch eine Kombination von mehreren Spalten repräsentiert. Ein Fremdschlüssel ist in einer anderen Tabelle Primärschlüssel. Durch Vergleich von Fremdschlüsselwert und Primärschlüsselwert werden Beziehungen zwischen Zeilen der beiden Tabellen hergestellt So hat die Tabelle buch den Primärschlüssel isbn, die Tabelle verlag den Primärschlüssel ierlag id. Die Tabelle buch besitzt den Fremdschlüssel uerlagid (siehe Tabellenstruktur weiter unten), Fremdschlüsselwerte innerhalb einer Tabelle sind in der Regel natürlich nicht eindeutig. Die l:n-Beziehung zwischen verlag und buch wird also durch die Fremdschlüssel-Primärschlüssel-Beziehung etabliert Im Weiteren setzen wir Grundkenntnisse der Datenbanksprache SQL voraus, Die SQL-Anweisungen zur Erstellung der zwei Tabellen für das Datenbanksystem MySQL sind,
Tabelle verlag
create table verl ag ( ver l a9_l d lnteger, verlag_na me varchar (30) , webadress e varchar (30) , primary key (ver lag i d)
Tabelle buch
create tabl e buch ( isbn varchar (17) , autor varchar(30), titel varchar(SO), ausgabe varchar(20), sei t enzahl l nte ger , jahr lnteger, ver l ag_l d lnteger, preis double, best and lnteger, stand dat etlme, primary key (isbn), for eign key (ver lag_i d) references verlag (verlag_i d)
2.2
Erste Beispiele
33
Die Programme dieses Kapitels "WUrden mit verschiedenen Datenbanksystemen getestet. Es "WUrden solche Systeme bevorzugt, die weit verbreitet bzw. ohne allzu großen Aufwand installiert werden können. Die folgende Tabelle führt die Datenbanksysteme und eingesetzten ]DBC-Treiber auf. Bezugsquellen können am Ende des Buches nachgeschlagen werden.
Access. MySQL (mit Tabellentyp InnoDB) und Apache Derby unterstützen Transaktionen (siehe Kapitel 2.4.1) und stellen die referentielle Integrität sicher.
2.2.2
Verbindungsaufbau
Alle Programme sind so geschrieben, dass die Datenbanksysteme leicht gewechselt werden können. Dazu sind die datenbankspezifischen Angaben in einer Konfigurationsdatei dbconnect. properties ausgelagert, die mit dem Texteditor bearbeitet werden kann. In diesem Abschnitt zeigen wir den grundsätzlichen Aufbau einer ]DBC-Anwendung. Wir setzen voraus, dass die Datenbank bereits eingerichtet ist und Testdaten enthält. In den folgenden Abschnitten dieses Kapitels werden dann der Aufbau und das Laden von Datenbanken mittels ]DBC-Programmen ausführlich beschrieben. SQL-Skripte und Testdaten sowie Skripte zum Starten und Stop- Online-Seroice pen des Derby-Servers können der zum Download zur Verfügung gestellten Programmsammlung (Online-Service) entnommen werden. Das erste Programmbeispiel gibt Informationen über die benutz- Programm 21 te Datenbank und das eingesetzte DBMS aus. Datenbankspezifische Angaben (Treiber, Identifikation der Datenbank, User-Id und Passwort) befinden sich in der Datei dbconnect.properties.
Hier sind die Angaben zur MySQL-Datenbank aktiviert. Das Zeichen # leitet einen Kommentar ein. Die Access-Datenbank muss mit dem Namen buecher als ODBC-Datenquelle registriert sein. Bei Windows muss zu diesem Zweck das Programm DatenquelZen (ODBC) aus der Systemsteuerung aufgerufen und der MSAccess-Treiber sowie die Access-Datenbank ausgewählt werden.
DBMetaData
import import import import
ja va ja va ja va ja va
io. File InputStream; sql .Connection; sq l . DatabaseMetaData; sq l . Dri verManager ;
impor t ja va util.Properties;
publ ic class DBMetaData ( public stat ic void main(String[] args) throws Except i on ( // OB-Parameter einlesen Fi lel nput St ream in ~ new Fi lelnput St ream( "dbconnect.prop erties"); Properties prop = new Properti es( ); prop.load(in) : in.clos e(): Stri ng Stri ng String String
11 Verb indung zur OB herstellen Connectlon con = Drl verManager.getConnectlon(url, user,
2.2
Erste Beispiele
35
password) ; DatabaseMetaData dbmd ~ con.getMetaData(); System.out. pri nt 1n(" URL; " + dbmd. getURl()); System.out. pri nt 1n(" UserNarre ; " + dbrrd. getUserNarre ()); System.out. pri nt 1n(" DatabaseProductName; " + dbrrd.getDatabaseProductName()); System.out. pr i nt 1n("OatabaseProductVerSl on: + dbrrd.getDatabaseProductVersion ()); System.out. pri nt 1n(" DriverNarre; + dbrrd getDri verNarre ()); System.out. pr i nt 1n(" DrlverVerSl on: + dbrrd.getDri verVersion()); con clos e();
Zunächst werden die Verbindungsparameter aus dbconnect. properties eingelesen und in entsprechenden Variablen abgelegt.
Die Treiberklasse (sun. jdbc. odbc. JdbcOdbcDri ver für Access, }DBC-Treiber laden com.mysql.jdbc.Driver für MySQL und org.apache.derby.jdbc. Cl i entDri ver für Apache Derby) wird mit Class. forNarre ( ... ) explizit geladen. Hierbei wird ein statischer Initialisierungsblock der Treiberklasse ausgeführt. der dafür sorgt. dass eine neue Instanz der Treiberklasse beim Treibermanager (java. sq 1. Drlver Manager) registriert wird. Verwendet man die neuesten Treiberversionen, so kann auf das Autoloading explizite Laden ab Java SE 6 verzichtet werden. Dann kann auch die Eigenschaft driver in der Datei dbconnect.properties weggelassen werden.
Sicherzustellen ist. dass die Klassen des JDBC-Treibers. zusammengefasst in einer Datei (z. B. mysql-connector-java-5. 1. 11bln.jar) vom Class-Loader gefunden werden. Dazu muss für die Programmausführung die entsprechende jar-Datei in den Klassenpfad aufgenommen werden. Die Programmsammlung des Online-Service enthält hierzu Vorschläge. Um eine Verbindung zur Datenbank aufbauen zu können, muss Verbindung zur diese Datenbank genau identifiziert werden. Die Zieldatenbank Datenbank wird in URL-Schreibweise angegeben, herstellen
jdbc:: Hierbei bezeichnet das Subprotokoll die Art des verwendeten Treibers (z. B. odbc, mysql oder derby), Subname bezeichnet die eigentliche Datenbank. Bei Access ist das der Name der ODBCDatenquelle. bei MySQL und Apache Derby ein URL (Uniform Resource Locator): / /1 oca1hostibuecher. Die Datenbank liegt hier
36
2 Datenbankanwendungen mit ]DBC auf dem eigenen Rechner. Statt 1oca1hast kann auch der Name eines im Netz verbundenen anderen Rechners angegeben sein. Die Dokumentation zum Treiber enthält, was als Subprotokoll und Subname anzugeben ist.
DriverManager
Die Klasse java.sql.Drl verManager kann unterschiedliche Treiber zur selben Zeit handhaben. Wird eine Verbindung zu einer Datenbank angefordert, so versucht der Treibermanager, den passenden Treiber zu finden und zur Herstellung der Verbindung zu verwenden. Die Drl verManager-Methode getConnectlon stellt eine Verbindung zur Datenbank her.
statlc Connectlon getConnection(Strlng url) throws SQLException static Connection getConnection(String url, Properties info) throws SQLException static Connection getConnection(String url, String user, String password) throws SQLException Alle Methoden liefern ein Connecti on-Objekt und erwarten einen URL und ggf, weitere Angaben als Parameter, Datenbank-User und Passwort können angegeben werden. Bei der zweiten Methode sind die zusätzlichen Verbindungsparameter user und password im Properties-Objekt info eingetragen. SQLException
Fehler werden in ]DBC grundsätzlich als Ausnahmen der Klasse java. sq1.SQLExcepti on ausgelöst
Connection
Ein Objekt vom Schnittstellentyp java. sq1.Connecti on präsentiert eine Verbindung zur Datenbank.
re-
Ein SQL-Anweisungsobjekt wird über die Connecti on-Schnittstelle erzeugt: Statement createStatement() throws SQLException Eine aktive Verbindung zur Datenbank wird mit der Methode close geschlossen: voi d close() throws SQLException Der Zustand einer Verbindung kann abgefragt werden, boolean isClosed() throws SQLException DatabaseMetaData
Das Interface DatabaseMetaData repräsentiert Informationen über die Datenbank und das DBMS, Die folgende ConnectiorcMethode liefert ein Metadaten-Objekt
2.2
Erste Beispiele
37
Database MetaData getMetaData() t hr ows SQLException Die Schnittstelle umfasst weit über 100 Methoden. Hier werden nur einige aufgeführt, die auch im nächsten Beispiel verwendet werden.
String getURL() throws SQL Exception liefert den URL der Datenbank. String getUserName() throws SQLException liefert den Namen des Datenbank-Users. String getDatabaseProductName() throws SQLException liefert den Produktnamen des Datenbanksystems. Str ing getDatabaseProductVersion() throws SQLException liefert die Version des Datenbanksystems. String getDriverName() throws SQLException liefert den Namen des ]DBC-Treibers. String getDriverVersion() throws SQL Exception liefert die Version des ]DBC-Treibers. mkd i r bi n javac -d bin src/DBMetaData.java java -cp bin %JDßC DR IV ER% DEMetaData
Die Umgebungsvariable JDßC_DR IVER referenziert die jar-Datei des ]DBC-Treibers. Ausgabe, URL: jdbc: mysq 1: / /1 oca1hostlbuecher
UserName: root@localhost DatabaseProductName: MySQL
Das Grundgerüst einer typischen ]DBC-Anwendung besteht aus den folgenden Schritten, 1. ]DBC-Treiber laden (entfällt bei Autoloading)
2. Verbindung zur Datenbank herstellen 3. SQL-Anweisungsobjekt erzeugen 4. SQL-Anweisung ausführen 5. Ergebnisse der SQL-Anweisung verarbeiten 6. Ressourcen freigeben
Test
2 Datenbankanwendungen mit ]DBC
38 Programm 2.2
Das zweite Programmbeispiel zeigt Informationen zu Büchern in Form einer sortierten Liste an. Die hierzu nötige SQL-Anweisung lautet select autor, t i t el . isbn from buch order by autor, titel
Buecherlistel
import ja va io.File InputStream; import ja va sql .Connection; import ja va sq l . Dri verManager; import java sql.ResultSet; import java sql.Staterrent; import java util.Properties; public class Buecherlistel ( public static void main(String [] args) throws Exception (
// OB-Parameter einlesen FilelnputStream in
~
new FilelnputStream(
"dbconnect.properties"); Properties prop = new Properties(); prop.load(in) ; in.close(); Stri ng ur 1 ~ prop. getProperty(" ur 1"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". "");
/I Autoloading // Verbindung zur OB herstellen
Connection con
=
DriverManager.getConnection(url, user,
password) ; // SQL-Abfrage ausführen
Statement stmt
=
con.createStatement();
ResultSet rs = stmt.executeQuery("select autor, t i t el . " + "i sbn from buch order by autor. ti te 1");
// Ergebnisse ausgeben whi 1e (rs. next ()) ( System.out.println(rs.getS tring(l)); System.out.println(rs.getString(2)); System.out.println("ISBN " + rs.getString(3)); System.out.println();
rs.close(); stmt.close(); con.close() ;
2.2
Erste Beispiele
Das Interface java.sql.Statement ist die Basisschnittstelle für alle Statement SQL-Anweisungsformen. Mit der Staterrent-Methode executeQuery wird eine SELECTAnweisung ausgeführt: ResultSet executeQuery(String sql) throws SQLException Die Methode liefert ein ResultSet-Objekt mit dem Abfrageergebnis. Die SQL-Anweisung sq1 enthält kein Abschlusszeichen wie z.B. ";". Solche Abschlusszeichen können von DBMS zu DBMS variieren und werden demzufolge vom Treiber gesetzt. void close() throws SQLException gibt die mit der Ausführung gebundenen Ressourcen frei. Hierdurch wird auch das evtl. vorhandene ResultSet-Objekt geschlossen. Das Ergebnis einer SQL-Abfrage (eine Relation mit Zeilen und ResultSet Spalten) wird durch ein Objekt mit der Schnittstelle Resu ltSet repräsentiert. Zum Navigieren über die Zeilen der Ergebnismenge wird die Resu ltSet-Methode next genutzt boolean next() t hr ows SQLException Ein interner Cursor zeigt auf die aktuelle Ergebniszeile. Zu Beginn ist der Cursor vor der ersten Zeile positioniert. next liefert so lange true, wie das Weitersetzen des Cursors erfolgreich war. 1st die Ergebnismenge leer oder das Tabellenende erreicht und keine weitere Zeile mehr vorhanden, so wird fa l se zurückgeliefert. void close() t hr ows SQLException gibt die genutzten Ressourcen frei. Nachdem der interne Cursor auf eine Ergebniszeile positioniert SpaltenzugriJJ "WUrde, kann auf die Spaltenwerte dieser Zeile von links nach rechts zugegriffen werden. Für den Spaltenzugriff existieren verschiedene ResultSetMethoden getXxx. Es kann über den Spaltenindex. der bei 1 beginnt. oder über den Spaltennamen zugegriffen werden. Zu beachten ist, dass der Spaltenindex sich auf die Spaltennummer in der Ergebnismenge und nicht in der Originaltabelle bezieht. getStri ng liefert den Spaltenwert als ein Stri ng-Objekt Str ing getString(int column lndex) t hrows SQL Exception String getString(String columnName) throws SQL Exception
39
2 Datenbankanwendungen mit ]DBC
40 Programmausgabe:
Dl ckens, Char les 81eak House IS8N 3-458-32810-6 Ol ckens, Charles 81 eakhaus IS8N 3-257-21166-X Dlckens, Charles Das GehelmnlS des Edwl n Drood IS8N 3-7175-1976-X
Programm 23
Im Unterschied zu Programm 2.2 wird im folgenden Programm auch der Verlag eines Buches ausgegeben:
Dlckens, Charles Bl eak House IS8N 3-458-32810 6 Insel
Die SQL-Anweisung enthält einen so genannten join zwischen den Tabellen buch und verlag. Buecherliste2 (Ausschnitt)
ResultSet rs ~ stmt .executeQuery("select autor, tt tel . r sbn. verlag narre + "fram buch inner join verlag" + "on buch. verla9_l d = verlag.v erlag~ld + "order by autor. ti te 1"); whi 1e (rs. next ()) ( System.out.pr i ntln(rs.getString( I)); System.out.pr i ntln(rs.getString(2)); System.out.println(" IS8N " + rS.g etString(3) + rS.g etString(4)); System.out.println();
Programm 2.4
+ " "
Mit dem folgenden Programm kann gezielt nach einem Titel gesucht werden. Dazu muss als Kommandozeilenparameter der Titel oder nur ein Teil des Titels beim Aufruf angegeben werden.
2.3 JDBC-Datentypen ResultSet rs
~
41
stmt
Buecherliste3 (Ausschnitt)
.executeQuery("select autor, t i t el , isbn, verlag name + + +
"fram buch inner join verlag"
"on buch.verla9_id
=
verlag.verla9_id
"where titel like '%" + titel + "%' order by autor, titel");
2.3
JDBC-Datentypen
Bei den SQL-Datentypen, die von den verschiedenen Datenbanksystemen unterstützt werden, gibt es zum Teil erhebliche Unterschiede. SQL arbeitet mit anderen Datentypen als Java. Beim Zugriff auf die Spaltenwerte und bei der Belegung von Parametern in JDBCProgrammen (siehe PreparedStatement im nächsten Abschnitt) muss daher eine Abbildung zwischen beiden Typsystemen erfolgen. Um die am meisten verwendeten SQL-Datentypen einheitlich zu ]DBC-Datentypen repräsentieren, "WUrden so genannte JDBC-Datentypen definiert, die in der Klasse java.sql.Types als Konstanten vom Typ int zusammengefasst sind: public static final int XXX Bild 2.8 gibt eine Übersicht über die ]DBC-Datentypen und ihre Entsprechungen für die Datenbanksysteme Access, MySQL und Apache Derby. Bild 2,8.'
Abbildung zwischen ]DBC-Typen und SQL-Typen SQL-Typ Access
SQL-Typ MySQL
TINYINT
BYTE
TINYINT
SMALLINT
SMALLINT
SMALLINT
SMALLINT
INTEGER
INTEGER, COUNTER
INTEGER,
INTEGER
JDBC-Typ
SQL- Typ Derby
Integerzahlen
MEDIUMINT BIGINT
BIGINT
BIGINT
REAL
FLOAT
REAL
DOUBLE, FLOAT
DOUBLE, REAL
DOUBLE, FLOAT
Gleitkommazahlen REAL FLOAT DOUBLE
2 Datenbankanwendungen mit JDBC
42
SQL-Typ Access
JDBC-Typ
SQL- Typ MySQL
SQL-Typ Derby
DECIMAL(p,s),
DECIMAL(p,s)
NUMERIC(p,s)
NUMERIC(p,s)
DATE
DATE, YEAR
DATE
TIME
TIME
TIME
DATE, TIME,
TIMESTAMP,
TIMESTAMP
DATETIME
DATETIME
CHAR
CHAR(n)
CHAR(n)
CHAR(n)
VARCHAR
VARCHAR(n), TEXT
VARCHAR(n)
VARCHAR(n)
LONGVARCHAR
LONGCHAR,
TINYTEXT, TEXT,
LONG VARCHAR
LONGTEXT
MEDIUMTEXT,
Festkommazah len DECIMAL NUMERIC
CURRENCY
Datum und Uhrzeit
TIMESTAMP
Zeichenketten
LONGTEXT
Binärdaten BIT
BIT
BINARY
BINARY(n)
BOOLEAN CHAR(n) FOR BIT DATA
VARBINARY
VARBINARY(n)
TINYBLOB
VARCHAR(n) FOR BIT DATA
LONGVARBINARY
LONGBINARY
BLOB,
LONG VARCHAR
MEDIUMBLOB,
FOR BIT DATA
LONGBLOB
Beim Lesen der Werte aus einem Resu ltSet-Objekt bzw. bei der Übergabe von Parametern an ein Pre par edSt at ement (siehe nächsten Abschnitt) werden getXxx- bzw. setXxx-Methoden verwendet Hier findet eine Konvertierung von SQL-Typen in Java-Typen bzw. umgekehrt statt Bild 2,9 zeigt die Abbildung zwischen JDBC-Typen und Java-Typen,
2.3
43
]DBC-Datentypen
saa z». Abbildung zwischen ]DBC- und]ava-
Typen JDBC-Typ
Java-Typ
Java-Objekttyp
TINYINT
byte
Int eger
SMALLINT
short
Inte ger
INTEGER
l nt
Int eger
BIGINT
l ong
Long
REAL
float
Fl oat
FLOAT
double
Doub 1e
DOUBLE
doubl e
Doub 1e
DECIMAL
java.math Bi gl:€ci ma 1
ja va.math. Big Deci mal
NUMERIC
java.math Bigl:€ci ma 1
ja va.math. BigDecimal
DATE
java sql · Date
ja va sql . Date
TIME
java sq l · Tü re
java sql .Tlme
TIMESTAMP
java sql · Tl Jn2stamp
ja va sql .Tlmestamp
CHAR
Str i ng
Stri ng
VARCHAR
Stri ng
Stri ng
LONGVARCHAR
Stri ng
Stri ng
BIT
boolean
Bool ean
BINARY
byte[]
byte[]
VARBINARY
byte[]
byte[]
LONGVARBINARY
byte[]
byte[]
Die get-Methoden der Klasse ResultSet sind dann entsprechend, byte getByte (...) short getShort(...) int getlnt(...) long getLong (...) floa t getFloat(...) doubl e getDouble(...) java. math. BigDeci ma1 getBi gDecima1 (...) java.sql.Date getDate(...) java.sql. Time getTime(...) java.sql. Timestamp getTimestamp( ..) String getString(...) boolean getBoolean(...) byte[] getBytes(...)
getXxx
2 Datenbankanwendungen mit JDBC
44
Beim Aufruf einer get-Methode ist als Parameter eine Spaltennummer (Typ i nt) oder ein Spaltenname (Typ St ri ng) anzugeben. Alle get-Methoden können die Ausnahme SQLExcepti on auslösen. Die Resu ltSet-Methoden Object getObject(int column Index) throws SQLException Object getObject(String columnName) throws SQLException liefern den Spaltenwert als ein Java-Objekt. dessen Typ dem entsprechenden JDBC-Datentyp aus der obigen Tabelle (Bild 2.9) entspricht. So wird z.B. für einen Spaltenwert vom JDBC-Typ I NTEGE R ein Objekt vom Typ Int eger geliefert.
Datum/Zeit
Die Klassen Date, Time und Timestamp des Pakets ja va.sql sind Subklassen von java.util.Date und repräsentieren die JDBCDatentypen DATE. TIME und TIMESTAMP. Objekte dieser Klassen enthalten Datumsangaben. Uhrzeitangaben bzw. Angaben zu Datum und Uhrzeit.
Date
Date(long dat e ) erzeugt ein Date-Objekt mittels des Zeitwerts date in Millisekunden (seit 01.01.1970. 00,00,00 GMT). void setTime(long date) setzt das Datum. static Date valueOf(String s) liefert ein Date-Objekt aus der Zeichenkette mm-tt".
5
im Format "jjjj-
String toString() liefert eine Zeichenkette im Format "jjjj mm-tt". Time
Time(long time) erzeugt ein Time-Objekt mittels des Zeitwerts ti me in Millisekunden (seit 01.01.1970. 00,00,00 GMT). voi d setTime(long time) setzt die Uhrzeit. static Time valueOf(String s) liefert ein Time-Objekt aus der Zeichenkette "hh :mm: 55".
String toString() liefert eine Zeichenkette im Format "hh :mm: ss".
5
im Format
2.4
Ausführung von SQL-Anweisungen
45
Timestamp(long t l me ) Timestamp erzeugt ein Tirrestamp-Objekt mittels des Zeitwerts time in Millisekunden (seit 01.01.1970. 00,00,00 GMT).
void setTirre(long ti rre) setzt Datum und Uhrzeit. long getTirre() liefert Datum und Uhrzeit in Millisekunden (seit 01.01.1970, 00,00,00 GMT). static Timestamp valueOf(String s) liefert ein Tirrestamp-Objekt aus der Zeichenkette s im Format "jjjj- rrrn-tt hh:mm:ss.f", wobei f die Nanosekunden bezeichnet. Str ing toString() liefert eine Zeichenkette im Format "jjjj -rrrn-tt hh:mm:
55.
f".
boolean after( Tirrestamp t s ) prüft, ob diese Uhrzeit später als ts ist. boolean before(Timestamp ts) prüft, ob diese Uhrzeit früher als ts ist. int compareTo(Ti rrestamp ts) vergleicht diese Uhrzeit mit ts. boolean equals( Timestamp ts) prüft, ob dieses Timestamp-Objekt inhaltlich gleich dem TirrestampObjekt ts ist.
2.4
Ausführung von SOL-Anweisungen
In diesem Abschnitt beschäftigen wir uns mit der Änderung von Datenbankstrukturen und -inhalten, der Abfrage von Metadaten über Ergebnismengen zur Laufzeit und mit der Schnittstelle
PreparedStatement.
2.4.1
Transaktionen
Im Fall von Änderungen steht der Begriff der Transaktion im Vordergrund. Eine Transaktion ist eine Folge von Datenbankoperationen Transaktion CSQL-Anweisungen), die die Datenbank von einem konsistenten Zustand in einen neuen konsistenten Zustand überführen. Die Anweisungen einer Transaktion werden entweder alle ausgeführt und abgeschlossen (committed] oder alle zurückgenommen
Crolled back). Transaktionen können automatisch oder manuell gesteuert werden. Eine ]DBC-Anwendung ist standardmäßig im Auto-CommitModus, Jede einzelne SQL-Anweisung bildet eine Transaktion, die automatisch mit Commit bestätigt wird.
46
2 Datenbankanwendungen mit ]DBC Um mehrere Anweisungen innerhalb einer einzigen Transaktion ausführen zu können, muss Auto-Commit ausgeschaltet werden. Das Interface Connectlon deklariert Methoden zur Steuerung von Transaktionen.
Auto-Commit
void setAutoCommit(boolean autoCommit) throws SQLException schaltet Auto-Commitein (true) oder aus (false). boolean getAutoCommit() throws SQLException prüft. ob Auto-Commit eingeschaltet ist.
Commitund
Rollback
void commit() throws SQLException zeigt an, dass die Transaktion abgeschlossen werden soll und alle Änderungen in der Datenbank permanent gespeichert werden sollen. void rollback() throws SQLException bricht die aktive Transaktion ab. alle bisherigen Änderungen innerhalb dieser Transaktion werden rückgängig gemacht. Transaktionen werden automatisch begonnen. Die erste SQLAnweisung nach einem Commit oder Rollback gehört schon zu einer neuen Transaktion. Eine manuell gesteuerte, noch nicht abgeschlossene Transaktion wird explizit mit der Methode commi t oder ro II back geschlossen.
2.4.2
Datenbankänderungen
SQL-Anweisungen. die die Datenbankstruktur betreffen (z. B. CREATE TABLE) und die Änderungsbefehle INSERT. UPDATE und DELETE werden mit Hilfe der folgenden StatementMethode an die Datenbank geschickt int executeUpdate(String sql) throws SQLException Im Fall von INSERT. UPDATE oder DELETE liefert die Methode die Anzahl der von der Änderung betroffenen Zeilen. Programm 2.5
Programm 2.5 richtet die im Kapitel 2.2 eingeführten Tabellen verlag und buch ein. Die Properties-Datei tables.properties enthält die Verweise auf die entsprechenden SQL-Skripte.
tables.properties
#tab1es . ./ db/Access/ create_ver 1ag. sq1 # . ./ db/Access/ create_buch. sq1 t abl es .. /db/MySQL/create_verlag.sql .. /db/MySQL/create_buch.sql #tabl es . ./db/Derby/create_verl ag .sql # . ./ db/Derby/create_buch. sq1
2.4 Ausführung von SQL-Anweisungen
47
Das Programm ist unabhängig von einem konkreten Datenmodell und somit universell einsetzbar.
lmport lmport lmport lmport lmport import lmport
java.lo BufferedReader; java.lo Fl l eI nput St ream; java.lo. FlleReader; java sql.Connectlon; java sql.Drl verManager; java sql.SQLException; java sql.Statement;
public class CreateTables ( public static voi d main(String[] args) { Connectlon con = null; try ( File lnputStream in ~ new File lnputStream( "dbconnect. propertles");
Properties dbProp dbProp.load (in); in.close() ;
~
new Properties() ;
String url ~ dbProp.getPropert y("url ") ; String user ~ dbProp .getProperty("user". ""); String password ~ dbProp.getProperty("password". ""); con
in ~ new File lnputStream("tables.propert ies"); Propertles tablesProp = new Propertles(); tab 1es Prop .load (i n) ; in.close() ; StrlngTokenlzer tables = new StrlngTokenlzer(tablesProp .getProperty( "tab 1es")); while (tables.hasMoreTokens()) ( BufferedReader reader ~ new BufferedReader( new File Reader (t able s .next Token( )) ) ; Strlng l tne:
In vielen Datenbanksystemen (wie auch bei MySQL) wird eine Transaktion durch create table automatisch abgeschlossen. Deshalb bleibt hier Auto-Commit eingeschaltet. Programm 2.6
Das folgende Programm ermöglicht das Laden von Tabellen aus externen Quellen.
Importdatei
Die Importdatei enthält pro Zeile einen in die Tabelle einzutragenden Datensatz, wobei die Spaltenwerte in der gleichen Reihenfolge auftreten, in der die Tabellenspalten mit der SQLAnweisung "CREATE TABLE" definiert "WUrden. Die Werte in der Datei sind durch ein Sonderzeichen (z. B.; oder das Tabulatorzeichen), das in keinem der Werte enthalten sein darf, getrennt (Feldtrenner).
Beispiel, Die Importdatei für die Tabelle verlag hat den Inhalt.
1;Anaconda; IN 2;Artemis &Winkler;www.patmos de 3;Aufbau;www.aufbau- verlag.de 4;Dlogenes;www.dlogenes.ch
Feldtrenner ist hier;. Evtl. nicht vorhandene Werte sind durch \N zu kennzeichnen. Diese entsprechen den NULL-Werten in der Datenbank Jede Zeile enthält drei Werte, die den Spalten uerlagid, verlag_name und webadresse entsprechen. Es folgen einige Konventionen für die Notierung der Werte in der Importdatei •
Zeichenketten werden ohne Begrenzer wie z. B. " eingegeben.
•
Numerische Werte werden wie Literale in Java notiert.
usw.
2.4
Ausführung von SQL-Anweisungen
•
Nicht vorhandene Werte (NULL-Werte) werden durch IN gekennzeichnet.
•
Formate für Datum, Uhrzeit und Zeitstempel sind "jjjj- rrrntt", "hh: rrrn:ss" bzw. "jjjj- rrrn-tt hh:mm:ss".
49
Das Programm ist allgemeingültig geschrieben und kann zum Laden beliebiger Tabellen genutzt werden. Es wird ntit drei Parametern aufgerufen: 1. Name des Verzeichnisses, in dem sich die Importdateien be-
finden C". " kennzeichnet das aktuelle Verzeichnis) 2. Feldtrenner, z. B. ; oder It (falls das Tabulatorzeichen benutzt wurde) 3. Name der Tabelle Der Name der Importdatei für die Tabelle xxx muss dann xxx.txt lauten. Das Programm generiert für jede eingelesene Zeile der Importdatei eine INSERT-Anweisung. Da die Tabellenstruktur (insbesondere die verwendeten SQL-Datentypen) dem Programm zur übersetzungszeit nicht bekannt ist, müssen Informationen hierüber zur Laufzeit abgefragt werden. Hierzu wird die ResultSet-Methode get l'etaData verwendet. die ein Resu ltSetl'etaData-Objekt bereitstellt. das die gewünschten Informationen enthält ResultS etMetaData getMetaData() throws SQL Exception Das Interface Resul tSet rvetaData deklariert u. a. Methoden, um Resu!tSetMetaData den ]DBC-Datentyp einer Spalte. die Anzahl der Spalten und einen Spaltennamen der Ergebnisrelation abzufragen. int getColumnType(int column) throws SQL Exception int getColumnCount() throws SQLException String getColumnLabel (int column) throws SQLException
finally { try { if (con !~ null) con.close(); if (tab !~ null) tab.close(); catch (Exception e) ( System.err.println(e);
// typgerechte Aufbereitung der Spaltenwerte für INSERT private static String getValue(int type. String s) { / / aus IN wi rd null if (s.equals("IIN")) re turn "null"; else if (type ~~ Types.CHAR 11 type ~~ Types.VARCHAR 11 type ~~ Types LDNGVARCHAR) { // der einfache Anführungsstrich ' wird verdoppelt s ~ s. replaceAll (" "'. """); return "'" + 5 + "'''; else if (type ~~ Types.DATE) return "{d '" + s + "')"; else if (type ~~ Types.TIME) return "{t '" + s + "')"; else if (type ~~ Types.TIMESTAMP) { // bei Access entsprechen die SQL-Typen DATE. TIME. // DATET IME alle dem JDBC-Typ TIMESTAMP
2 Datenbankanwendungen mit ]DBC
52
if (s.indexOf(' ') !~ -1) return "( ts '" + s + "'}"; else if (s.indexOf('-') !~ -1) return "{d + s + "'}"; else return "{t + s + "'}"; else
return
5;
Um Informationen über die ]DBC-Datentypen der Tabellenspalten zu erhalten, wird zunächst eine SQL-Abfrage der Form select
*
from tabelle where 0
~
1
ausgeführt, die eine leere Ergebnismenge liefert. Die Resu ltSet-Methode getMetaData liefert dann ein Resu ltSetMetaData-Objekt. Mit Hilfe von StrlngTokenlzer wird nun jede Zeile in ihre Werte zerlegt und die INSERT-Anweisung aufgebaut. lnsert into tabelle values (wertl, ... )
Die Hilfsmethode getValue(int type, String s) sorgt für die SQL-konforme Aufbereitung des eingelesenen Wertes. Hier liegen die folgenden Regeln zugrunde, Ist
5
gleich "\N", so liefert die Methode "null"
ZUIÜCk.
Ist der ]DBC-Typ type gleich CHAR, VARCHAR oder LONGVARCHAR, wird der Wert in einfache Hochkommata eingeschlossen. Ein einfaches Hochkomma • im Wert selbst wird verdoppelt. Ist type gleich DATE, TIME oder TIMESTAMP, so werden EscapeKlauseln der Form {d 'xxx'}, {t 'xxx'} bzw. {ts 'xxx'} zurückgeliefert, wobei xxx für das Literal für Datum, Zeit bzw. Zeitstempel steht (siehe obige Beschreibung der Importdatei). Test
Wegen der bestehenden Fremdschlüssel-Primärschlüssel-Beziehung muss zuerst die Tabelle verlag, dann die Tabelle buch geladen werden.
Batch-llpdates
Mehrere INSERT- oder UPDATE-Anweisungen können in einem Staterrent-Objekt mit addBatch gesammelt werden und dann in einem Zug mit executeBatch ausgeführt werden. Diese Vorgehensweise ist in der Regel effizienter als die einzelne Ausführung von SQL-Anweisungen.
2.4 Ausführung von SQL-Anweisungen
53
Hier die beiden Staterrent-Methoden, void addBatch(String sql) throws SOLException int[J executeBatch() throws SOLException Das i nt-Array enthält für jede SQL-Anweisung die Angabe darüber, wie viele Datensätze eingefügt bzw. verändert "WUrden, den
Wert Statement.SUCCESS NO INFO oder Staterrent.EXECUTE FAILED im Fehlerfall. Das nächste Programm exportiert die Daten einer Tabelle im Programm2.7 oben beschriebenen Import-Format. Damit können die Daten der Datenbank in einem Textformat gesichert werden.
public class Export ( public static void main(String[J args) { String dir ~ args[DJ; String delimiter ~ args[IJ; String table ~ args[2J; if (delimiter.equals("llt")) delimiter ~ "It"; Pri ntWrHer out = null; Connection con = null; try ( out = new PrintWrHer(new FlleWrlter(dir + ".txt"). true);
FileInputStream in
~
+ "I" +
table
new FileInputStream(
"dbconnect. properties");
Propertles prop prop.load (i n) ; in.close() ;
=
new Propertles();
String url ~ prop.getProperty("url"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". ""); con
=
DrlverManager.getConnectlon(url, user, password);
2
54 Statement stmt
=
Datenbankanwendungen mit ]DBC
con.createStatement();
ResultSet rs = stmt. executeQuery( "se1ect * from " +table); ResultSet MetaData rsmd ~ rS.getMetaData(); int n ~ rsmd.getColumnCount();
public class UpdateBestand ( public static void main(String[] args) { String datei ~ "bestand.txt"; BufferedReader input ~ null; Connection con = null; try ( input ~ new BufferedReader(new FileReader(datei)); FilelnputStream in
~
new FilelnputStream (
"dbconnect. properties");
Properties prop prop.load(in) ; in.close() ;
=
new Properties();
String url ~ prop.getProperty("url"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". ""); con = DriverManager.getConnection(url, user, password); con setAutoCommit(false); PreparedStatement stmt = con.prepareStatement( "update buch set bestand ~ bestand + ? + "stand = ? where isbn = ?"); String isbn; i nt bestand; i nt count;
StringTokenizer st; String line;
while ((line ~ input.readLine()) !~ null) ( st = new StringTokenizer(line); isbn ~ st.nextToken(); bestand ~ Integer.parselnt(st.nextToken()); stmt.setlnt(l. bestand); stmt.setTi mestamp(2. new Timestamp(System .currentTi me Hi 11 i s ())) ; stmt setString(3. isbn);
57 UpdateBestand
2 Datenbankanwendungen mit ]DBC
58 count
~
stmt executeUpdate();
i f (count > 0) System.out.println(isbn + "; Bestand aktualisi ert"); con .commlt ( ) ; stmt.close(); catch (Exception e) ( System.err.pr i ntln(e); try { if (con !~ null) con. rollback(); catch (SQL Exception ex) System. err .println(ex); } finally { try { if (con !~ null) con.close() ; if (i nput !~ null) i nput. cl ose () ; catch (Exception e) ( System.err.println(e);
2.5
Ein einfaches Frontend für SOL-Datenbanken
In Kapitel 2.2 wurden bereits Metadaten über die Datenbank (OatabaseMetaData) genutzt. Hier folgen zwei weitere Methoden: ResultSet getTables(String catalog . String schemaPattern. String tableNamePattern. String[] types) throws SQLException liefert eine Beschreibung der Tabellen der Datenbank. Im Beispiel rufen wir die Methode mit den Parametern null , null, "%", null auf. Das ResultSet-Objekt enthält u. a. die Spalten TABLE- NAME und TABL E- TYP E. ResultS et getColumns(String catalog. String schemaPattern. Str ing tableNamePattern. String col umnNamePattern) throws SQLException liefert eine Beschreibung der Tabellenspalten. Im Beispiel rufen wir die Methode mit den Parametern null, null, Tabellenname, "Z" auf. Das ResultSe t-Objekt enthält u. a. die Spalten
2.5
Ein einfaches Frontend für SQL-Datenbanken
COLUMN_NAME, DATA_TYPE TYPE_NAME, COLUMN_SIZE.
ODBC-Datentyp
aus
java sql. Types),
Das folgende Programm bietet ein Frontend für SQL-Daten- Programm2.9 banken, mit dem beliebige SQL-Anweisungen (SELECT, INSERT, UPDATE, DELETE) an eine Datenbank geschickt werden können.
Die Abfrageergebnisse können auf Wunsch in der Datei log.txt protokolliert werden. Das Programm bietet neben den SQLAnweisungen noch die folgenden Eingabemöglichkeiten show tables
alle Tabellennamen anzeigen
show table xxx
Metadaten zur Tabelle xxx anzeigen
log on
Protokollierung einschalten
log off
Protokollierung ausschalten
q
Programm beenden
In diesem Programm nutzen wir die Staterrent-Methode Unbekannte execute, um beliebige, zur Compilierungszeit unbekannte SQL- Anweisungen Anweisungen an die Datenbank zu schicken: ausführen
boolean execute(String sql) throws SOLException Der Rückgabewert ist true, wenn ein Abfrageergebnis vorliegt, und false, wenn es sich um eine Änderung handelt.
Mit den Statement-Methoden getResultSet und getUpdateCount kann die Ergebnismenge bzw. die Anzahl der geänderten Zeilen ermittelt werden: ResultSet getResultSet() throws SOLException int getUpdateCount() throws SOLException Auch die Schnittstelle PreparedStatement enthält eine executeMethode, boolean execute() throws SOLException Bei schwerwiegenden Fehlern Methoden einen Fehler durch java. sq1 .SOLExcepti on (Subklasse lermeldungen können in einem verpackt sein.
melden die meisten ]DBC- SQLException eine Ausnahme der Klasse von Excepti on). Mehrere FehObjekt vom Typ SOLException
SOLExcepti on enthält folgende Methoden,
59
2 Datenbankanwendungen mit ]DBC
60
String getSQLState() liefert eine Fehlerbeschreibung nach X!OPEN- bzw. SQL,2003Konventionen.
int getErrorCode() liefert einen herstellerspezifischen Fehlercode. SQLException getNextException() liefert das nächste Ausnahme-Objekt, falls mehrere Fehlermeldungen vorliegen. Sql
lmport lmport lmport lmport lmport lmport lmport l mport l mpor t import import import lmport lmport
java java java java java java ja va ja va java java java java java java
public static void main(String[] args) try ( FilelnputStream in ~ new File lnputStream( "dbconnect.properties"); Properties prop = new Properties(); prop.load(in) ; in.close(); Stri ng ur1 ~ prop. getProperty(" ur1"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". "");
con
=
DriverManager.getConnection(url, user, password);
input ~ new BufferedReader(new InputStreamReader( System. in)); String query;
2.5
Ein einfaches Frontend für SQL-Datenbanken whi 1e (true) ( System.out.print("> "); query ~ input.readLine(); if (query.equals("q")) break; if (query.equals("log on")) if (output ~~ null) (
output
=
new PrlntWrlter(
new FileWriter("log.txt"). true);
} log
~
true;
contlnue;
if (query.equals("log off")) log ~ false;
contlnue;
i f (log) output.println ("Query; " + query); try ( process(query) ; catch (SQLException e) printSQLException(e);
} catch (Exception e) ( System.err.println(e); finally { try { if (con !~ null) con.close(); if (input !~ null) input.close(); if (output !~ null) output. cl ose () ; catch (SQLException e) printSQLException(e); catch (IQException e) System.err.println(e);
61
62
2 Datenbankanwendungen mit ]DBC private static void printSQLException(SQLException e) ( System. err. pri nt1n( "SQLExcepti on: ") : while (e !~ null) ( System. err. pri nt1n( "SQLState: " + e. getSQLState ()): System.err.println("Message: + e.getMessage()): System. err. pri nt1n( "ErrorCode: " + e. getErrorCode ()): e ~ e.getNextException():
if (isResultSet) ( ResultSet rs ~ stmt.getResultSet(): ResultSet MetaData rsmd ~ rs.get MetaData(): int numCols ~ rsmd.getColumnCount(): l nt count = 0; whi 1e (rs. next () ) count++ ;
System.out.println("#" + count ): if (log) output prin tln("#" + count ): for (int i ~ 1: i log on > show tables
TABLE: buch TABLE: verl ag > show table buch isbn VARC HAR( 17) autor VARCHAR(30 ) titel VARCHAR(80) ausgabe VARCHAR(20) sei tenzah I INT( 10) jahr INT( 10) ver l ag_id I NT(10) preis DOUBL E(22) bestand I NT(l O) stand DATET IME(19) > select * from buch where titel like '%Twist%' # 1 i sbn: 3-7466-2200-X autor: Dlckens, Charles titel: Oliver Twist ausgabe: Kartonlert sei t enzahI: 564 jahr: 2005 verlag_id : 3 preis: 9.95 bestand: 50 stand: 2010 02-07 00:00:00.0 > log off > q
2.6
Speicherung großer Objekte
Wir wollen nun die Struktur der Tabelle buch um eine zusätzliche Spalte, die ein Bild des Covers aufnehmen kann, ergänzen. Die hierfür geeignete SQL-Anweisung lautet ""ccess:
alter tabl e buch add bild longbinary
MySQL:
alter table buch add bild longblob
Apache Derby: alter table buch add bild blob Große Binärobjekte schreiben und lesen
Mit Streams können die Daten blockweise geschrieben bzw.
gelesen werden.
PreparedState rrent-Methode: vOl d setBinaryStream(lnt parameterIndex, Input St ream x, int length) throws SQLException
2.6 Speicherung großer Objekte
65
1ength ist die Größe der Datei in Byte. Resu1tSet-Methoden, InputStream getBinaryStream(int columnlndex) throws SQLException InputStream getBinaryStream(String columnName) throws SQLException ImportImage speichert ein jpeg-Bild für eine bestimmte ISBN-
Programm2.10
Nummer. Die Bilder sind in einem vorgegebenen Verzeichnis zu finden und tragen jeweils den Dateinamen: . jpeg.
Aufrufbeispiel
java -cp bin %JDBC DRIVER% ImportImage bilder 3-15-001308-9
public class Import Image ( public static void main(String[J args) ( String dir ~ args[OJ; String isbn ~ args[IJ; Input St ream fi n ~ null; Connection con = null; try ( FilelnputStream in ~ new FilelnputStream( "dbconnect. properties");
Properties prop prop.load (i n) ; in.close() ;
=
new Properties();
String url ~ prop.getProperty("url"); String user ~ prop.getProperty("user". ""); String password ~ prop.ge tProper ty("password". ""); con
=
DriverManager.getConnection(url, user, password);
String image
=
dir + "I" + isbn +
File file ~ new File(image); int length ~ (int) file.length(); fin ~ new FilelnputStream(file);
Jpeg
66
2 Datenbankanwendungen mit ]DBC PreparedSt at ement ps = con.prepareStatement( "update buch set bild ~ ? where isbn ~ ?"); ps setBinaryStream( l, fi n, length ); ps setString(2, isbn); int count ~ ps executeUpdate(); i f (count> 0) System.out.prl ntln(lmage + " wurde lmportlert"); ps.cl osct ):
catch (Exception e) ( System,err,println(e); f inally { try { if (con !~ null) con.close t ) ;
i f (fin
!~
null)
f iu.cl ose O ;
catch (Except i on e) ( System, err ,println(e);
Programm 2.11
Mit ExportImage kann ein solches gespeichertes jpeg-Bild wieder in ein Dateiverzeichnis exportiert werden.
public class ExportImage ( public stat ic void main(String[J args) { Str ing dir ~ args[OJ; Str ing isbn ~ args[ IJ ; Input St ream fin ~ null; Fi l eOut put St ream fout ~ null; Connectl on con = null; try { Fi l el nput St ream in ~ new Fi l el nput St ream(
ResultSet rs ~ stmt .executeQuery( "sel ect bi l d fram buch where i sbn + isbn + '" and bi Idis not null");
=
'"
i f (rs. next ()) ( fin ~ rS.getBinaryStream(l); Strlng lmage = di r + "I" + i sbn + Jpeg fout ~ new FileOutputStream(image); byte[] buffer ~ new byte[1024]; int si ze : while ((size ~ fin.read(buffer)) !~ -1) { fout. wri te (buffer. O. si ze); ) fout. fl ush () ; System. out. pr i nt ln (" Bll d wurde exportl ert: " + l mage);
else System. out. pri nt In (" Kei n BiI d vorhanden"); rs .close(); stmt. cl ose () ; catch (Exception e) ( System.out.println(e); finally { try { if (con !~ null) con.close(); if (fin !~ null) fin.close(); if (fout !~ null) fout. cl ose () ; catch (Exception e) ( System.err.println(e);
2 Datenbankanwendungen mit ]DBC
68
2.7
Navigation und Änderungen inder Ergebnismenge
Bei der Erzeugung von SELECT-Anweisungen kann auf die Art der Verwendung der Ergebnismenge Einfluss genommen werden. Hierzu existieren die beiden Connect l on-Methoden
Statement createStatement (int resultSetType, lnt resultSetConcurrency) throws SQLException PreparedSt at ement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException Der Parameter resultSetType bestimmt, wie in der Ergebnismenge mit Hilfe eines internen Cursors navigiert werden kann. Der Parameter resu l tSetConcurrency legt fest, ob Datensätze der Ergebnismenge direkt geändert werden können. Navigierbarkeit
Für resultSetType können folgende Konstanten verwendet wer-
den, ResultSet, TYP E- FORWARD- ONLY
Der Cursor kann nur zum nächsten Satz bewegt werden. Dies ist die Standardeinstellung für die parameterlose Connectlon-Methode createStatement.
ResultSet, TYP E- SCROLL - INSENSITI VE
Der Cursor kann frei bewegt werden.
ResultSet,TYP E- SCROLL - SENS ITIVE
Der Cursor kann frei bewegt werden. Änderungen durch andere Transaktionen sind sichtbar. Änderbarkeit
Für resu ltSetConcurrency können folgende Konstanten verwendet werden:
ResultSet,CONCUR READ ONLY Änderungen können nicht vorgenommen werden. Dies ist die
Standardeinstellung für die parameterlose Connect ion-Methode createStatement .
ResultSet,CONCUR UPDATABL E Datensätze können in der Ergebnismenge geändert werden. Die freie Navigation kann bei entsprechender Einstellung mit
den folgenden ResultSet-Methoden erfolgen : boo1ean next() boolean prevlous()
boolean relative(lnt rows) void beforeFirst() void beforeLast() Der Cursor wird zur nächsten, vorherigen, ersten, letzten Zeile bewegt, auf die Zeile mit der Nummer row positioniert (die Zählung beginnt bei 1), um rows Zeilen vorwärts bzw, rückwärts (bei negativer Zahl) bewegt, vor die erste Zeile, hinter die letzte Zeile bewegt Ferner existieren die Prüfmethoden:
boolean boolean boolean boolean
isFirst() isLast() isBeforeFirst() isAfterLast()
int getRow() liefert die Nummer des aktuellen Datensatzes Cl für den ersten Satz), Das folgende Programm demonstriert die verschiedenen Mög- Programm2.12 lichkeiten der Navigation durch die Ergebnismenge.
java.lo BufferedReader; java.lo Fll eI nput St ream; j ava. jo . InputSt reamReader ; java sql.Connectlon ; java sql.Drl verManager; java sql.ResultSet; java sql.SQLExcep tion; java sql.Statement; java.utll.Propertles;
public class Na vigation ( public static void main(String[] args) {
Connectlon con
=
null;
Statement stmt ~ null; ResultSet rs ~ null; try ( Fi l elnput St r eam in ~ new File lnputStream(
"dbconnect.propertles"); Propert les prop = new Propertles (); prop . load (i n) ; in.close() ; String url ~ prop. get Propert y( "ur l "); String user ~ prop.getProperty("us er". "" ); String password ~ prop.getProperty("password". "");
con
=
DrlverManager.getConnectlon(url, user, password);
69
2 Datenbankanwendungen mit ]DBC
70
stmt
= con.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE. ResultSet.CONCUR_READ_ONLY); rs = stmt.executeQuery( "select isbn. titel from buch order by isbn");
if (rs.last()) ( System.out.println("Anzahl Zeilen; " + rs.getRow()); else ( System. out. printl n( "Ergebnl smenge i st leer"); return;
} rs. beforeFi rst(); System.out.println("Ihre Eingabe bitte; " + "next , prevl aus, fl rst . l ast oder qult"); BufferedReader br ~ new BufferedReader( new InputStreamReader(System. in)); Strlng I i ne ; whi 1e (( 1i ne ~ br readLi ne ()) ! ~ null) try { if (line.equals("next")) ( if (rs.next()) System.out.println(rs.getRow() + " " + rs.getString(1) + " " + rs.getString(2)); else if (line.equals("previous")) ( if (rs.previous()) System.out.println(rs.getRow() + " + rs.getString(1) + " " + rs.getString(2)); else if (line.equals("first")) ( if (rs.first()) System.out.println(rs.getRow() + " " + rs.getString(1) +"" + rs getString(2)); else if (line.equals("last")) ( if (rs .last()) System.out.println(rs.getRow() + " " + rs.ge tString(1) +"" + rs getString(2)); else if (line.equals("quit")) { break; } catch (SOLException e) System.err.println(e); } catch (Exception e) ( System.err.println(e);
2.7
Navigation und Änderungen in der Ergebnismenge
71
finally { try { if (rs !~ null) rs .close(); if (stmt !~ null) stmt. cl ose () ; if (con !~ null) con.close(); catch (SQLException e) {
Ist die Ergebnismenge als änderbar eingestellt, können Datensätze direkt in einem ResultSet-Objekt eingefügt, geändert und gelöscht werden. In der Regel darf die SELECT-Abfrage nur eine Tabelle umfassen (keine JOINs) und keine GROUP-Klausel enthalten. Die Abfrage muss den Primärschlüssel mit einschließen. Die genauen Regeln hängen vom DBMS ab. Für das Folgende setzen wir voraus, dass der Cursor auf einem bestimmten Datensatz der Ergebnismenge positioniert ist.
Aufruf der Resu ltSet-Methoden Ändern void updateXxx(int idx. typ x) throws SQLException analog zu den setXxx-Methoden aus Kapitel 2.4.3, also z. B. rs.updatelnt(3. 100); Aufruf der Resu ltSet-Methode void updateRow() throws SQLException Aufruf der Resu ltSet-Methode void moveTolnsertRow() throws SQLException
Einfügen
Aufruf der update Xxx-Methoden. Aufruf der Resu ltSet-Methode void insertRow() throws SQLException Ggf. Aufruf der ResultSet-Methode vOld moveToCurrentRow() throws SQLExceptlon um zum aktuellen Datensatz zurückzukehren, Aufruf der Resu ltSet-Methode void deleteRow() throws SQLException
Löschen
2 Datenbankanwendungen mit ]DBC
72 Programm 2.13
Das folgende Programm demonstriert die drei Änderungsarten. Zu einem Buch kann die Bestandszahl geändert werden, ein neues Buch mit ISBN-Nummer kann eingefügt werden, ein Datensatz kann gelöscht werden.
l mport lmport lmport lmport l mpor t import import
lmport ja va sql.Tlmestamp ; lmport ja va util.Properties;
publ ic class Update ( public static voi d main(String[] args) ( Connection con = null; Staterrent stmt ~ null; ResultSet rs ~ null; try ( Fi l el nput St ream in ~ new Fi l elnput St r eam( "dbconnect.properties"); Properti es prop = new Proper ties(); prop.load( in) ; in.clos e(); Stri ng ur1 ~ prop. getProperty(" ur1"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". ""); con
= con.createStatement( ResultSet. TYP E_SCROLL_INS ENS ITIVE. ResultSet.CONCUR_UPDATA ßL E); rs = stmt.executeQuery( "select isbn. titel. bestand. stand from buch");
if (rs.last()) ( System. out pri nt 1n( "Anzah1 Zei1en; " + rs. getRow()) ; else ( System. out pri nt l n( "Ergebnl smenge l st leer"); return; } rs. fi rst(); System. out println(); System. out. pri nt 1n( rs. getRow() + " " + rs. getStri ng (l) + " " + rs .ge tString(2) + " " + rs.getString(3)
2.7
Navigation und Änderungen in der Ergebnismenge + " " + rs .getString(4)); System.out.println ();
System.out println (" Ihre Ei ngabe bitte; ");
System.out.println(", insert , del ete, + "nur RETURN oder quit");
BufferedReader br ~ new BufferedReader( new Input St r eamReade r (Sys t em. i n ) ) ;
String line; wh ile ((line ~ br.readLine()) i f (line equals("q uit")) br eak;
rs. i nser tRow(); rs .moveToCurrentRow(); System. out. pri nt 1n (" insert OK"); el s e i f (line.equals("del ete")) rs .de1eteRow () ; System.out.println("del ete OK");
rs .previous(); el s e { try ( int bestand ~ Int ege r . par s el nt (l i ne ) ; rs.update lnt(3. bestand); rs.updateTimestamp(4. new Timestamp(System .currentTi me Hi 11 i s ())) ; rs. updateRow(); System.out.println(rs.getRow() + " " + rs.getString(l) + " " + rs.getString(2) + " "+ rs.getStr ing(3)); catch (NumberFormatException e) ( System. err. pri nt 1n ( "Keine Int ege r z ahl " ) ;
73
2 Datenbankanwendungen mit ]DBC
74
catch (SQL Exception e) System.err.println( e); } catch (Exception e) ( System. err println(e); finally { t ry { if (rs !~ null) rs.close(); if (stmt !~ nul l ) stmt.close(); if (con !~ null) con.close() ; catch (SQL Exception e ) (
2.8
CachedRowSet - eine verbindungslose Ergebnismenge
Ein Resu ltSet bietet den Zugriff auf das Abfrageergebnis mit Hilfe von internen Zeigern. Allerdings handelt es sich hierbei nicht um einen Container, der Daten speichert. Spätestens mit dem Schließen der Datenbankverbindung sind alle Daten verloren. Implementierungen des Interfaces j avax. s q1 . RowSet speichern die Zeilen des Abfrageergebnisses und ermöglichen auch die Navigation und Änderungen. RowSet ist ein Subinterface von ResultSet. CachedRowSet
Wir behandeln hier ein Subinterface von RowSet: j avax. sq1. rowset. CachedRowSet
Seit Java SE 5 wird eine Implementierung dieses Interfaces mit ausgeliefert:
com.sun.rowset.CachedRowSet Impl CachedRowSet Imp l-Instanzen sind serialisierbar.
Die CachedRowSet-Methode voi d populate(Resul tSet rs) t hr ows SQLException
übernimmt das übergebene Abfrageergebnis rs.
2.8
CachedRowSet - eine verbindungslose Ergebnismenge
75
Ohne eine Datenbankverbindung können die Daten mit den entsprechenden Resu ltSet-Methoden geändert werden (siehe Kapitel 2.7). Diese Änderungen können dann später in der Datenbank nachvollzogen werden.
Hierzu dient die folgende Methode,
vOld acceptChanges(Connectlon con) throws SyncProvlderExceptlon javax. sq l . rowset. spi .SyncPro vi derExcepti on ist Subklasse von SQLException.
Ein CachedRowSet eignet sich vor allem in verteilten Umgebungen. Daten können z. B. auf ein mobiles Endgerät übertragen werden und dort ohne Datenbankverbindung geändert werden. Später kann eine Übertragung zurück zur Datenbank erfolgen und die Daten können eingespielt werden.
Folgendes Szenario soll die Verwendungsmöglichkeit demonst- Programm2.14 rieren:
1. Buchdaten werden abgefragt und in einem CachedRowSet ge-
speichert. Anschließend wird dieses serialisiert (RowSetTestl). 2. Das CachedRowSet wird rekonstruiert (Deserialisierung) und der erste Datensatz wird geändert. Die Instanz wird wiederum serialisiert (RowSetTest2). 3. Das CachedRowSet wird rekonstruiert (Deserialisierung) und die Änderungen in der Datenbank eingespielt (RowSetTest3). Bild2.11.·
Cached RowSet
Testszenario
OB buch.dat
CachedRowSet
CachedRowSet
®
Update
Zur Vereinfachung der Prozesse dient die Klasse RowSetUti 1i ty. Sie enthält den Konstruktor RowSetUtility(String url. String user. String passwordl zur übergabe der Verbindungsparameter, die Methode
boolean executeQuery(String query)
76
2 Datenbankanwendungen mit ]DBC zur Erzeugung des Abfrageergebnisses mit Speicherung in einem
CachedRowSet-Objekt, die Methode boolean propagate() zur übernahme der Änderungen in die Datenbank, die get/setMethoden CachedRowSet get RowSe t() und void setRowSet(CachedRowSet rowSet) sowie die Methoden zur Serialisierung und Deserialisierung
statlc vOld serlallze(CachedRowSet crs, Str lng fll ename) throws IOExcept l on und static CachedRowSet deser ialize(String fil ename) throws IOExcept i on, ClassNot FoundException Row5etUtility
lmport ja vax.sql .rowset.CachedRowSet; lmport com.sun.rowset.CachedRowSet Impl; public class RowSetUtility ( prlvate CachedRowSet rowSet; pr i vat e Strl ng ur 1 ;
prlv at e Strlng user; prlvate Strlng password; public RowSetUtility(String ur}, String user. String password) ( this.url -vurl : thls.user = user; thlS password = password; public boolean executeOuery(String query) { Connect l on con = null; Statement stmt ~ null; ResultSet rs ~ null;
rowSet ~ new CachedRowSetlmpl(); rowSet.popu1ate( rs); return true;
catch (SQLException e) return false;
finally { try { if (rs !~ null) rs .close(); if (stmt !~ null) stmt. cl ose () ; if (con !~ null) con.close(); catch (SQLException e)
public void setRowSet(CachedRowSet rowSet) ( this.rowSet = rowSet; public CachedRowSet getRowSet() return rowSet;
public boolean propagate() Connection con = null; try ( con = DriverManager.getConnection(url, user, password); con.setAutoCommit(false); rowSet.acceptChanges(con); re turn true;
publlC statlc vOl d serlallze (CachedRowSet crs, String filename) throws IOExcept i on ( ObjectOutputStream out ~ new ObjectOutputStream( new Fi le Out put St ream(fi l ename )) ; out writeObject(crs); out fl ush() ; out close(); public stat ic CachedRowSet deserialize(String filename) throws IOExcept i on, ClassNotFound Except ion ( ObjectlnputStream in ~ new ObjectlnputStream( new Fi l el nput St ream(f i l ename) ) ; CachedRowSet crs ~ (CachedRowSet) in,readObj ect(); in,clos e(); r eturn crs;
RowSetTestl
lmport java l o.Fl l eI nput St ream; lmport java utll.Propertles;
l mport javax.sql .rowset.CachedRowSet; public class RowSetTest l ( public static voi d main(String[] args) { Fi lel nput St re am in; try ( j n = new Fl l eI nput St re am( "dbconnect propertles" ); Propertles prop = new Propertl es(); prcp. load(in); in,close(); Stri ng ur1 ~ prcp .getProperty(" ur1"); String user ~ prop,getProperty("user", ""); String password ~ prop,getProperty("password", ""); RowSetUtility util password) ;
~
new RowSetUtility(url, user,
= "select i sbr , t i tel . bestand, stand" "from buch order by isbn"; i f (uti 1, executeOuery(query)) ( CachedRowSet crs ~ util ,getRowSet();
Strlng query
while (crs,next()) ( System,out,println( crs getRow() +" " + crs,getString(l) + " " + crs getString(2) + " " + c rs .get St r l ng (3) + " " + crs,getString(4));
+
2.8
CachedRowSet - eine verbindungslose Ergebnismenge
79
RowSetUti 1i ty. seri al i ze(crs. "buch.dat") ; } catch (Exception e) ( System.err.println(e);
lmport java.sql .Tl mestamp;
RowSetTest2
lmport javax.sql.rowset.CachedRowSet;
public class RowSetTest2 ( public static void main(String[] args) { try ( CachedRowSet crs ~ RowSetUtility.deserialize( "buch.dat") ; crs.absolute(I); int bestand ~ crs.getlnt(3) + 10; crs.updatelnt(3. bestand); crs.updateTl mestamp(4, new Tlmestamp(System .currentTi rreMi 11 i s ())) ; crs. updateRow(); RowSetUti 1i ty. seri al i ze(crs. "buch.dat") ; catch (Exception e) ( System.err.println(e);
public class RowSetTest3 ( public static void main(String[] args) { FilelnputStream in; try { tn = new FII er nputSt ream ("dbconnect propertl es" ) ; Propertles prop = new Propertles(); prop.load(in) ; in.close() ; String url ~ prop.getProperty("url"); String user ~ prop.getProperty("user". ""); String password ~ prop.getProperty("password". "");
RowSetTest3
2 Datenbankanwendungen mit ]DBC
80 RowSetUtility util password) ;
~
CachedRowSet crs
RowSetUtility,deserialize(
~
new RowSetUtility(url, user,
"buch.dat"»:
uti 1, setRowSet(crs); i f (uti 1, propagate()) ( System,out,println("OB-Update erfolgreich"); else ( Systeri.out println("Fehler bei OB-Update"); } catch (Exception e) ( System,err,println(e);
2.9
Exkurs: XML-Dokumente aus SOL-Abfragen erzeugen
Thema dieses Kapitels ist die Entwicklung eines Programms, das das Ergebnis einer beliebigen SQL-Abfrage in eine XML-Struktur überführt und diese dann in ein anderes Format (z. B, HTML) mittels XSLT (Extensible Stylesheet Language Transformation) transformiert.
XMI
Zunächst einige Bemerkungen zu XML: Mit so genannten Auszeichnungssprachen (wie z. B, HTML) können beliebigen Textelementen Eigenschaften zugewiesen werden, wodurch deren Formatierung oder Bedeutung festgelegt wird, Die Textelemente werden durch Markierungen (tags) gekennzeichnet. Auf eine Startmarkierung folgt das Textelement, das in der Regel durch eine Endemarkierung abgeschlossen wird, Dabei sind die Markierungen im Allgemeinen geschachtelt, d.h. im markierten Text können weitere Markierungen enthalten sein. Das ermöglicht eine hierarchische Strukturierung des Inhalts, XML (Extensible Markup Language) ermöglicht mit selbst definierten Tags eine genaue Beschreibung strukturierter Informationen, die dann maschinell ausgewertet werden können. Im Unterschied zur Sprache HTML, deren Tags fest vorgegeben sind, ist XML eine Metasprache, mit der anwendungsspezifische Auszeichnungssprachen definiert werden können.
2.9
Exkurs, XML-Dokumente aus SQL-Abfragen erzeugen
81
Das XML-Dokument param.xml enthält Angaben zur Herstellung Programm 2.15 einer Datenbankverbindung, eine SQL-Abfrage und einige Dateinamen. Der allgemeine Aufbau dieser Datei sieht wie folgt aus:
Parametername Parameterwert Das zu entwickelnde Programm 2.15 (Db2Xm1) liest diese Parameter mit Hilfe der Klasse XMLPararreters zu Beginn ein.
url jdbc:mysql ://localhost/buecher
user root
password root SQL-Abfrage --> sql select autor, tl tel, lsbn, prels, bestand, stand from buch order by autor. titel
xmlFlle result/result.xml
param.xml
2 Datenbankanwendungen mit ]DBC
82
xs lFlle template.xsl
outputFlle result/result.html
Die Leistung der Klasse XMLParameters besteht im Parsen des XML-Dokuments mit Hilfe eines SAX-Parsers (Simple API for XML) und der Bereitstellung der Parameternamen und -werte in Form eines Propertl es-Objekts. Ein SAX-Parser bietet ein ereignisorientiertes API. Der Parser liest das XML-Dokument sequentiell durch und ruft spezielle Callback-Methoden immer dann auf, wenn er im Eingabestrom ein Tag oder ein Textelement entdeckt.
XMLParameters
package xmlutils; l mport l mport l mport l mport
java. i c. FileNotFoundException; java. i c. FileReader; java. i c. IOExceptlon;
java .uti l . Propertl es;
lmport javax.xml .parsers.ParserConflguratlonExceptlon; lmport javax.xml .parsers.SAXParser; lmport javax.xml .parsers.SAXParserFactory; l mport l mpor t l mpor t l mpor t l mpor t
public class XMLParameters extends DefaultHandler prlvate Strlng element; pr i vate Strl ng narre = " " : pr i vate Strl ng val ue = " " ; prlvate Propertles propertles = new Propertles(); public XMLParameters(String filename) throws FileNotFoundException. IOException. ParserConflguratlonExceptlon, SAXExceptlon
2.9
Exkurs, XML-Dokumente aus SQL-Abfragen erzeugen
SAXParserFaetory f aet ory ~ SAXParser Faetory.newlnstanee(); SAXParser parser ~ faetory.newSAXParser(); XMLReader reader ~ parser.getXMLReader(); reader.setContentHandler (this); reader.parse(new Input Souree (new Fi l eReader (f i l ename ) ) ) ; publie voi d startElement(String namespaeeURI. String loealName. String qName. Attributes attrs) throws SAXExeeption el ement
=
qName ;
publie void eharaeters(ehar[] eh. int start. int length) throws SAXExeeption // Textlnhalte können slch über me hrere Zell en ers trecken. String text ~ new String (eh. start. length).trim(); i f (text.length() ~~ 0) return ; if (element.equals("param-name")) if (name.length() > 0) name += " ": name += t ext ; else if (element.equals("param val ue") ) ( if (value.l ength() > 0) value += " '": value += text;
publie void endEl ement(String namespaeeURI. String loealName. String qName) throws SAXExeeption { if (qName.equals("param")) ( propertles.setProperty (name, value); name = uo. val ue = "";
XML Pararreters ist von der Klasse l:ef aultH andl er abgeleitet, die Default-Implementierungen für alle Callback-Methoden enthält Die Callback-Methoden startElerrent, endEl ement und characters werden in der Subklasse XMLParameters überschrieben. Im Konstruktor von XMLPar amet er s wird ein SAXParserFactoryObjekt erzeugt, mit dessen Hilfe dann der eigentliche Parser erzeugt wird, Die Methode parse des Interface XMLReader liest das XML-Dokument und ruft Callback-Methoden auf Die Callback-Methoden in XMLPar amet er s sind so implementiert, dass führende und nachfolgende Leerzeichen beim Parameternamen und -wert ignoriert werden . Insbesondere kann sich der Parameterwert über mehrere Zeilen erstrecken. Die Methode con vert der Klasse XMLCon verter erzeugt aus dem Ergebnis einer SQL-Abfrage (ResultSet) ein XML-Dokument der Form:
Spaltennamel
Wertl
Die kritischen Zeichen &, , ' und" werden durch so genannte Entity-Rejerenzen ersetzt.
XMLConverter
package xmlutils; import ja va sql.ResultSet; import ja va sql.ResultSetM2taData; import ja va sql .SQL Exception; publ ic class XMLConverter
prlvate ResultSet rs; publ i c XMLCon verter(Res ultSet rs) {
thls.rs
=
rs;
2.9
Exkurs, XML-Dokumente aus SQL-Abfragen erzeugen
public String conver t() throws SQLException ( StringBuilder sb ~ new StringBuilder(); String sep ~ System.getProperty("line.separator"); sb append( "" + sep); sb append( "" + sep); sb append("lt" + sep); ResultSetMetaOata rs md ~ rS.getMetaOata(); int n ~ rs md.getColumnCount(); for (int i ~ 1; i ":
sb. append( ">"); break;
85
86
2
Datenbankanwendungen mit ]DBC
case '\": sb.append("'"); break; case'\"':
sb. append("""); break; defau l t: sb. append(c);
return sb.toString();
Die Methode trans form der Klasse XML Transforrrer wandelt ein XML-Dokument in eine andere Struktur (z. B. ein HTMLDokument) mit Hilfe eines Stylesheets um. Die Extensible Stylesbeet Language Transformation (XSLT) beschreibt, wie XMLDokumente in andere Formate transformiert werden können. In unserem Beispiel wird das Stylesheet template.xsl verwendet. template.xsl
SOL Ergebnis
2.9
Exkurs, XML-Dokumente aus SQL-Abfragen erzeugen
87
template-Elemente legen fest, wie die Ausgabe aussehen soll. Ihr Inhalt definiert eine Vorlage, die mit den Daten des XMLDokuments gefüllt wird. Das Attribut match des Tags xs1 :t emp1ate gibt an, für welches Element des XML-Dokuments diese Vorlage gilt.
public class XMLTr ans f orme r ( prlvate Transfor mer transformer; public XMLTransformer(Reader xsl lnputl throws TransformerConflQuratlonExceptlon
TransformerFactory factory = TransformerFactory .new Instance () ; transformer = factory.newTransformer(new StreamSource( xslInputll: public Str ing trans form(Read er xml lnputl throws TransformerExceptlon { StrlngWrlter out = new StrlngWrlter(); transformer.transform(new StreamSource(xml Input ) , new StreamResult(outll: return out.toString():
XMLTransfonner
88
2 Dat enbankan wendungen mit JDBe Im Konstruktor von XMl Tr ansf ormer wird ein TransformerFacto ryO bjekt erzeugt , mi t dessen Hilfe dann der eigentliche Transfor mer auf der Basis eines Sryle sheets erzeugt wird Die Methode t ransfo rm der Klasse Transformer wand elt d as XML-Dokument um.
Die ma i n-Methode der Kla sse Db2Xm 1 führt di e folgende n Schri tte
aus: •
Einle sen der Paramet er au s param.xml mit Hilfe von XML -
Paramete rs
•
Aufb au der Datenbankverbindung
•
Au sfüh rung der &2L-Abfrage
•
Erzeug ung des XML-Dokumenrs mit Hilfe von X""-.Con ver ter
•
XSL-Transfo rma tion auf Basis de s Srylesheers ternplate .xsl mit Hilfe von XMLTr ansformer
Die Datei WEB- INFlsun jaxws-xml enthält die Endpoint-Beschreibung des Web Service,
Hier können mehrere -Tags auftreten. Die Bytecodes des Web Service aus dem letzten Abschnitt können in einer jar-Datei zusammengefasst werden. Im Verzeichnis Prog09021bi n ausführen, jar cf artikel.jar delTD/artikel/* artikel.jar dann nach WEB-INFIlib kopieren. Die Context-Datei ws.xm1 mit dem Inhalt
muss anschließend nach \conf\Catalina\localhost kopiert werden. Nun kann Tomcat gestartet werden:
\bin\startup.bat Test mit Web Browser: http://loca lhost:8080/ws/artikel
9.6
Oneway-Operationen
Mit der Annotation @Oneway kann für eine voi d-Methode festgelegt werden, ob eine Operation eine leere Nachricht als Antwort erhalten soll. Beim Aufruf einer solchen Methode wird die Kontrolle an den Aufrufer zurückgegeben, bevor die Methode ausgeführt wird.
9.6
Oneway-Operationen
345
Das folgende Beispiel zeigt den Unterschied zwischen dem Auf- Programm 9.4 ruf einer normalen VOl d-Methode und einer Oneway-Operation.
Ruft man das Client-Programm auf, sieht man deutlich den Unterschied: send! kehrt erst nach 5 Sekunden zurück, send2 praktisch sofort. SOAP-Antwort von sendl :
Bei der Ausführung von send2 wird lediglich der HTIP-Header HTTP/! .! 202 Accepted als Antwort übertragen, keine SOAP-Nachricht.
9.7
Asynchrone Aufrufe
Der Client kann eine Methode so aufrufen, dass der Aufruf nicht so lange blockiert, bis das Ergebnis vorliegt. Vielmehr kann der Client weiter arbeiten. Er wartet asynchron auf das Ergebnis. Polling oder Callback
Programm 9.5
Hierzu gibt es zwei Möglichkeiten, •
Der Client fragt von Zeit zu Zeit nach, ob die Antwort da ist (polling), oder
•
er stellt eine Callbac~Methode bereit, die dann automatisch aufgerufen wird, wenn die Antwort vorliegt.
Wir demonstrieren diese beiden Möglichkeiten anhand eines einfachen Beispiels. Der Web Service ermittelt, ob eine bestimmte Menge eines Produkts verfügbar ist. Die Bearbeitung kann unterschiedlich lange dauern (3 bis 8 Sekunden).
public interface Product ( @Web Method boolean isAvailable(@WebParam(name ~ "id") int id. @WebParam(naJn2 = "amount") i nt anount.) ;
package demo.product;
lmport java.utll.Random;
Implementierung Productlmpl
lmport javax.jws.WebServlce; @WebService(endpoi ntln terface ~ "demo.product. Product") pub1i c cl ass Productlmp 1 i mp 1ements Product ( public boolean isAvailable(int id. int amount) ( Random r ~ new Random(); int delay ~ 3 + r.next lnt(6); try ( Thread.sleep(delay * 1000); ) catch ( InterruptedException e) ( )
return r.nextBoolean();
wsimport erzeugt aus dem WSDL-Dokument die für die Entwicklung des Clients nötigen Artefakte. Damit Code für den asynchronen Aufruf (Polling, Callback) ge-
neriert wird, muss folgendes XML-Dokument erstellt werden und beim Aufruf von WSl mport berücksichtigt werden: true
bindings-Dokument custom.xml für tösimport
9 Web Services mit JAX-WS
348 Ant Task
Es folgt der vollständige Quelleode des Client-Programms, der im Anschluss erläutert wird. ProductClient
import ja va.util .concurrent. Execut i onException; import javax.xml .ws .AsyncHandl er ; import javax.xml .ws .Response ; import client. IsAvailableResponse; import client Product; import client Product ImplService; public class ProductClient private Product port; public ProductClient() ( Product ImplService servic e = new ProductImplService(); port ~ service.getProduct lmplPort(): public stat ic void main(String[] args) ( ProductCli ent client ~ new ProductClient(): client. invokeBlocking(); client. invokeNonblockingPoll() ; client. invokeNonblockingCallback() ; System.out.println(" Warte auf Antwort"): try ( Thread.sl eep(IOOOO): catch ( Int erruptedExc eption e) (
private voi d invokeBlocking( ) ( blocking ---"): Syst em.out.prin tln("boolean isAvai labl e ~ port.isAvailable( 47I I, 10) : System.out.println ("isAvailable: " + isAvailable):
9.7 Asynchrone Aufrufe
pri vate void invokeNonblockingPoll() ( System.out.println ("nonblocking. polling ---"); Response res = port.lsAvallableAsync( 4711. 10) ; while (!res.is [);me()) ( System.out.println ("Warte auf Antwort"); try ( Thread.sleep( 1000); catch (InterruptedException e) {
try ( IsAval l abl eResponse response = res.get(); boolean lsAvailabl e = response.isRet urn(); System.out.println("isAvailabl e; " + isAvailabl e ); catch (Interrupted Exception e) ( System.err.println (e.g et Message( )); catch (Execut i onExcept i on e) ( System.err.println (e .get Message( ));
pri vate void invokeNonblockingCallback() ( System.out.println ("nonblocking. callback ---"); port.isAvailableAsync( 4711. 10. new MyHandler()); private class MyHandler implements AsyncHandler public voi d handleResponse( Response res) IsAvai l abl eResponse response; try ( response = res.get(); bool ean isAvailable = response.isReturn(); System.out.println("isAvailable; " + isAvailable); catch (Interrupted Exception e) ( System. err.println(e.getMessage()); catch (ExecutionException e) ( System.err.println(e.get Message());
349
9 Web Services mit JAX-WS
350 invokeNonblockingPoll
cl i ent. Pr oduct enthält die Methode
Response< IsAvallableResponse> isAvailableAsync( i nt i d. i nt anount )
Das Interface j avax. xm1 .ws. Response repräsentiert die Antwort des Aufrufs. Es enthält u. a. die beiden folgenden Methoden,
boolean isDone()
T get() throws Int er r upt edE xcept i on . java. uti l .concurrent. Execut l onExceptl on i sDone liefert true, sobald die Antwort vorliegt, sonst fa 1se. get liefert die Antwort, hier ein Objekt vom Typ IsAvai 1ab1eRes pons e. Diese Klasse enthält die boo1ean-Property re turn . Innerhalb einer Schleife wird im Abstand von einer Sekunde nachgefragt, ob die Antwort vorliegt (polling). Ist die Schleife beendet, so kann die Antwort mit get geholt werden. invokeNonblockingCallback
cl i ent. Product enthält ebenfalls die Methode
Fut ure isAvailableAsync(lnt ld, lnt amount, Async Handler< IsAvallableResponse> asyncHandl er) Respons e ist ein Subinterface von Fut ur e. Das Interface ja vax.xml.ws.Async Handler deklariert die Methode
vOl d handleResponse(Response res), die automatisch aufgerufen wird, sobald die Antwort vorliegt. Die innere Klasse MyHand ler implementiert diese Methode für den Typparameter IsAvai 1ab1eResponse .
Nach dem Aufruf von i nvokeNonb1ocki ngea11 back wartet das Programm 10 Sekunden, damit die ma in-Methode nicht endet, bevor der Rückruf per Callback erfolgt ist. Programmausgabe (Beispiel)
nonblocking. polling
Warte Warte Warte Warte Warte
auf auf auf auf auf
Antwort Antwort Antwort Antwort Antwort
isAvailable: false nonblocking. callback
Warte auf Antwort lsAvallable: true
9.8
Web Services und binäre Daten
9.8
351
Web Services und binäre Daten
Bei SOAP-Nachrichten handelt es sich um Xlvll-Dokumente, die ausschließlich Textzeichen enthalten dürfen. Will man nun Nachrichten mit binärem Inhalt übertragen, so kann man die binären Daten vor der Übertragung in eine Texrform überführen (Codierung) und beim Empfänger wieder dekodieren. Wir demonstrieren diesen Sachverhalt anhand eines Web Service , Programm 9.6 der Upload und Download von Dateien anbietet.
public i nt erface Tr ans fe r ( 8'W ebMe t hod void upload( 8'WebParam(narre ~ "narre") String narre. 8'WebParam(narre ~ "data") byte[] data): 8'W ebMe t hod byte[] downl oad(8'WebPar am( narre "narre") String narre) throws Fi 1eNotFoundE xcept i on:
Die byte-Arrays enthalten den Inhalt der Datei. name enthält den Dateinamen.
352 public class TransferImpl ( private final static String DIR
~
"C:/temp":
public void upload(String name, byte[] data) try ( Stri ng path ~ DIR + "/" + name: BufferedOutputStream out ~ new BufferedOutputStream( new FileOutputStream(path)): out write(data): out flush(): out.close() : catch (IOException e) ( throw new WebServiceException("Upload failed"):
public byte[] download(String name) throws FileNotFoundException Stri ng path ~ DIR + "/" + name: File file ~ new File(path): if (!file,exists()) throw new Fi l eNotFoundExcepti on(" Fi l e does not exi st"); try ( ByteArrayOutputStream out ~ new ByteArrayOutputStream(): BufferedInputStream in ~ new BufferedInputStream( new FileInputStream(path)): byte[] buffer ~ new byte[2048]: intn~O:
while ((n ~ in,read(buffer)) out,write(buffer, 0, n): } in,close():
Im generierten WSDL-Dokument ist für den Parameter data der
XML-Schema-Datentyp base64Bi nary eingetragen, Der Up 1oadCl i ent überträgt die Datei test .pdf an den Web Service, der sie dann im Verzeichnis C:\ temp speichert.
9.8
Web Services und binäre Daten
353
lmport java. ia Buf feredInput St ream; import java. la ByteArrayOutputStream; import java. la Fi l eI nput St ream;
UploadClient
import client Transfer; import client Transfer ImplService; public class UploadClient ( public static voi d main(String[] args) ( Transfer ImplService service = new Transfer ImplService(); Transfer port = service.getTransfer ImplPort(); String name ~ "test.pdf"; try ( ByteArrayOutputStream out ~ new ByteArrayOutputStream(); Buffered lnputStream in ~ new BufferedlnputStream( new Fi l el nput St ream(name )) ; byt e[] buffer ~ new byte[2048]; i nt n
=
0;
while ((n ~ in.read (buffer)) out.write(buffer. O. n); } in.close() ;
Die Binärdaten werden nach dem Base64-Verfabren codiert Base64 (siehe Kapitel 6.2). Dabei werden Bytes in eine Folge von ASCIIZeichen umgewandelt.
test.pdf JVBERiOxL ... RU9GDQo:::x::/data>
Die Codierung führt zu einer Vergrößerung des Datenvolumens um ca. ein Drittel.
SOAP-Anfrage
9 Web Services mit JAX-WS
354 DownloadClient
lmport ja va lo.BufferedOutputStream; lmport ja va lo. FlleOutputStream; lmport cllent Trans fer ; lmport cllent Transfer ImplServlce; public class Downl oadCl i ent ( public static voi d main(String [] args) ( Transfer ImplServlce ser Vlce = new Transfer ImplSer vlce(); Transfer port = servlce.getTransfer ImplPort(); Str ing narre ~ "test.pdf"; try ( byte[] data ~ port.download(name); BufferedOutputStream out ~ new BufferedOutputStream( new Fi l eOut putSt re am(narre )) ; out write(data); out fl ush() ; out.close() ; catch (Exception e) ( System.err.println(e.getMessage());
MTOM
SOAP Message Transmission Optimization Mechanism (MTOMy ist ein Verfahren zur Optimierung der übertragung binärer Daten. Der in der SOAP-Nachricht im Base64-Format enthaltene Inhalt wird aus der Nachricht entfernt und in den Anhang verschoben. In der Nachricht selbst befindet sich dann eine Referenz auf die Originaldaten (raw bytes) im Anhang.
optimierte
SOAP-Nachricht
test .pdf
9.8
Web Services und binäre Daten
355
Mit der Annotation @javax.xml.ws.soap.MTOM in der Implementie- Programm9.7 rung des Web Service wird die Optimierung eingeschaltet.
0WebServlce(endpolnt Interface Ii\'1TOM public class Transfer lmpl (
"demo.transfer. Transfer")
MTOM einschalten
Das generierte WSDL-Dokument enthält einen Policy-Eintrag. der aussagt. dass MTOM verwendet wird.