Multimedia und Internet (MI) Teil 2: Internet und Datenbanken Vorlesungsskript Wintersemester 2000/01 Prof. Dr. Edwin Schicker FH Regensburg 1 2 3 4 5 Überblick ......................................................................................................................................... 2 1.1 Rechnerumgebung ................................................................................................................... 3 Hypertext Markup Language (HTML) ............................................................................................ 3 2.1 HTML-Grundlagen.................................................................................................................. 3 2.2 HTML-Formulare .................................................................................................................... 5 2.3 Einfaches Beispiel zu Formularen ........................................................................................... 7 Active Server Pages......................................................................................................................... 8 3.1 Installation eines eigenen WWW-Servers ............................................................................... 8 3.2 Active Server Pages (ASP) ...................................................................................................... 9 3.3 Ein- und Ausgabe in Active Server Pages ............................................................................. 11 3.4 Javascript ............................................................................................................................... 12 3.5 Übungsaufgaben .................................................................................................................... 12 ActiveX Data Objects .................................................................................................................... 12 4.1 ODBC, OLE DB.................................................................................................................... 12 4.2 ActiveX Data Objects (ADO)................................................................................................ 14 4.3 Erster Zugriff auf Datenbanken ............................................................................................. 14 4.4 Suche mehrerer Daten in Datenbanken ................................................................................. 16 4.5 Mehrfacher Zugriff auf Datenbank........................................................................................ 18 4.6 Transaktionen ........................................................................................................................ 22 4.7 Weitere Möglichkeiten .......................................................................................................... 25 4.8 Übungsaufgaben .................................................................................................................... 26 Überblick über weitere Möglichkeiten .......................................................................................... 26 5.1 Das Application-Objekt ......................................................................................................... 26 5.2 Die Datei Global.asa.............................................................................................................. 27 5.3 Das Command-Objekt in ADO.............................................................................................. 28 5.4 Die Methode Redirect des Session-Objekts........................................................................... 29 5.5 Providerangaben .................................................................................................................... 30 2 1 Überblick Datenbanken, insbesondere relationale Datenbanken, sind heute weit verbreitet. Sie finden in den letzten Jahren auch ein sehr stark zunehmendes Interesse im Internet. Nicht nur ECommerce, sondern auch viele andere Internetanwendungen greifen auf Datenbanken zu. Wir wollen im zweiten Teil dieser Vorlesung zeigen, wie einfach die dazu erforderliche Programmierung heute ist. Es gibt mehrere Möglichkeiten, vom Internet aus auf Datenbanken zuzugreifen. Und wir werden sehen, dass diese Zugriffsweise in manchen Punkten sogar einfacher ist als manche herkömmliche Remote-Zugriffe. Internetzugriffen liegt immer das Client/Server-Konzept zugrunde. Wir benötigen einen WWW-Server und zahlreiche Clients, die alle mit einem Browser ausgestattet sind. Kommen Datenbanken hinzu, so brauchen wir zusätzlich einen Datenbank-Server. Auf diesen Datenbank-Server greift der WWW-Server als Datenbank-Client zu. Der WWW-Server ist also Server bezüglich der Client-Browser und ist selbst Client, wenn er die Datenbankanforderungen des Client-Browsers ausführt und auf die Datenbank zugreift. Wir erhalten in einer etwas vereinfachten Form folgende Zugriffsreihenfolge: Browser Der Browser wird vom Benutzer gestartet. Von hier aus greift also der Benutzer auf die Internet-Datenbank zu. WebServer Der WWW-Server ist das Bindeglied zwischen Browser und Datenbank. Auf dem WWW-Server wird die Datenbankverbindung (ODBC, OLE DB, JDBC) hergestellt. DB-Server Der Datenbank-Server verwaltet die Datenbank selbstständig. Auf kleineren Datenbanken und WWW-Servern sind häufig WWW-Server und DB-Server auf dem selben Rechner installiert. Mit dem Aufruf der WWW-Seite durch den Browser des Benutzers wird der WWW-Server aktiv und liefert dem Browser die gewünschte Seite. Diese liest der Benutzer und gibt gegebenenfalls in entsprechenden Feldern Daten ein. Durch einen Klick auf einen Button oder einen speziellen Link wird der WWW-Server aufgerufen und bearbeitet diese Aktion. Ist ein Datenbankzugriff erforderlich, um benötigte Informationen zu holen, so wird eine entsprechende Anforderung an den DB-Server gestellt. Dieser bearbeitet diese und reicht die gewünschten Daten an den WWW-Server zurück. Diese bereitet der WWW-Server auf und schickt die komplette Seite mit den gewünschten Daten an den Client zurück. Der Browser stellt diese Daten dann dar, der Benutzer liest die Seite. Der mit Abstand am weitesten verbreitete WWW-Server ist der Apache-Server, gefolgt vom Microsoft Internet Information Server (Microsoft IIS). Zugriffssprachen sind beispielsweise Perl, verschiedene Basic-Dialekte, Java, Javascript, PHP. Wir wollen in dieser Vorlesung einige Grundlagen kennenlernen. Die Umgebung spielt eine untergeordnete Rolle, die Arbeitsweise ist wichtiger. Da wir Zugriff auf einen Win2000-Server mit Microsoft IIS haben, werden wir mit der Microsoft-Umgebung ADO/ASP und der Zugriffssprache Javascript den Zugriff auf Datenbanken via Internet üben. Literatur: Online-Hilfe zum Option Pack von Microsoft (empfehlenswert) Krause: Microsoft Active Server Pages, Addison Wesley, 99,90 DM Weltner: Active Server Pages lernen und beherrschen, Microsoft Press, 89 DM Millier, Mezick: Active Server Pages – Programmierung, Microsoft Press, 89 DM Multimedia Entwicklung (Teil2) Edwin Schicker 3 Internet: Software: 1.1 Selfhtml 7.0, www.teamone.de/selfaktuell/ www.microsoft.com (sehr viele Zusatzinfos zu ADO/ASP mit vielen Beispielen) Frontpage 2000 (umfangreiches HTML-Werkzeug im Office-Paket) Dreamweaver (Macromedia, sehr gutes HTML- und Design-Werkzeug) Arachnophilia (kostenlose SW) HTMLEDIT (kostenlose SW, www.meybohm.de) Rechnerumgebung Übungsraum sind auch in diesem Vorlesungsteil die Räume 512 und 513 mit Windows95/NT. Als Server wird der Windows2000-Server rfhpc8322 im Raum 511 verwendet. Da die ASP-Seiten vom Server interpretiert werden müssen, müssen diese Seiten auch dort unterhalb des wwwroot-Verzeichnisses hinterlegt werden. Um dies für alle Studenten zur Verfügung zu stellen, sind im Raum 511 auf den Windows2000-Rechnern Kennungen aller Studenten eingetragen. Dabei besitzt jeder Student unterhalb des Verzeichnisses Dokumente und Einstellungen das Unterverzeichnis public_html. Diese lokalen Verzeichnisse werden auch auf dem Server gehalten und bei jedem Herunterfahren abgeglichen. Der IIS-Server interpretiert diese Verzeichnisse als Internet-Root-Verzeichnisse des Benutzers. Damit diese Einstellungen auch wirksam werden, muss sich ein Benutzer mindestens einmal im Raum 511 unter Windows2000 eingeloggt haben. Im Folgenden wird dies vorausgesetzt. Es kann nun auf den Windows2000-Server zugegriffen werden. Sollen HTML- oder ASP-Seiten oder eine kleine Datenbank dort abgelegt werden, so verwenden wir dazu einfach den Explorer. Mittels Netzwerk verbinden im Menü Extras geben wir folgenden Pfad ein: \\rfhpc8322\user\XXXXXXXX eventuell nur: \\rfhpc8322\user ‚XXXXXXXX’ ersetzen wir durch die eigene Benutzerkennung. Nach einer Autorisierung sind wir mit dem entsprechenden Homeverzeichnis des Benutzers auf dem Windows2000-Server verbunden. Im Unterverzeichnis public_html können wir jetzt unsere HTML-/ASP-Seiten ablegen! Diese ASP-Seiten werden jetzt beim Zugriff vom IIS-Server interpretiert und als HTML-Seite an den Aufrufenden zurückgegeben. Im Browser lautet die URL zu diesen Seiten: http://bt-win2k-server/~XXXXXXXX Wieder ist ‚XXXXXXXX’ durch die eigene Benutzerkennung zu ersetzen. Beachten Sie auch das Zeichen ‚~’ (Tilde) vor der Kennung! Im obigen Fall wird eine Datei namens index.html, index.htm oder default.htm erwartet. Natürlich kann gegebenenfalls auch eine andere Datei aufgerufen werden, indem diese im obigen Aufruf einfach mit angegeben wird (dazwischen steht ein Schrägstrich /). Weitere Hinweise, etwa zum erstmaligen Einloggen werden in der Vorlesung gegeben. 2 Hypertext Markup Language (HTML) Um auf Datenbanken über das Internet zuzugreifen, benötigen wir Wissen über SQL, Programmiersprachen (hier: Javascript) und HTML-Dokumente. Die ersten beiden werden vorausgesetzt. Mit HTML-Dokumenten wollen wir uns daher nur kurz auseinandersetzen. 2.1 HTML-Grundlagen Hier erfolgt nur eine sehr knappe Zusammenfassung zu HTML. Die meisten Beispiele wurden dem Programm Selfhtml 7.0 von Stefan Münz entnommen. Ich kann das kostenlose Produkt Selfhtml nur wärmstens weiterempfehlen. Selfhtml enthält neben einer ausführlichen Beschreibung von HTMLBefehlen und StyleGuides auch eine gute Einführung in Javascript. Multimedia Entwicklung (Teil2) Edwin Schicker 4 HTML ist ein normaler Text, der sogenannte Tags enthält. Dies sind Bezeichner, die in spitze Klammern eingeschlossen sind. Diese Tags wirken auf den folgenden Bereich, bis diese durch einen entsprechenden Ende-Tag beendet werden. Dieser Ende-Tag unterscheidet sich vom Tag-Bezeichner durch einen vorangestellten Schrägstrich (‚/’). Das Grundgerüst eines HTML-Programms enthält einen Kopf, in dem globale Informationen zu dem Programm angegeben werden und einen Rumpf, der die eigentlichen Texte und Grafiken enthält. Ein HTML-Dokument hat somit folgendes Aussehen: <html> <head> <title>Text des Titels</title> </head> <body> Text, Verweise, Grafikreferenzen usw. </body> </html> Der Titel erscheint in der Titelleiste des Browsers. Der eigentliche Text muss noch strukturiert werden. Hier stehen zahlreiche Tags zur Verfügung. Eine erste Auswahl ist: <hi>Text</hi> <p>Text</p> Text<br>Text <b>Text</b> <i>Text</i> <u>Text</u> Überschrift der Ordnung i mit i=1,…,6 Textabschnitt (Ende-Tag ist nicht zwingend) Zeilenumbruch Fettdruck Kursivschrift Unterstreichung Auch Nummerierungen lassen sich in HTML-Dokumenten durchführen: <ol> <li>Listeneintrag</li> <li>Listeneintrag</li> </ol> Diesen geordneten Listen (ol=ordered list) stehen Aufzählungslisten (ul=unordered list) gegenüber. Wird also in obigem Beispiel der Tag ol durch ul ersetzt, so wird eine reine Aufzählung ohne Nummerierung erzeugt. Wichtig sind für uns Tabellen. Auch hier wollen wir nur die Grundkonzepte ansprechen. Tabellen enthalten Zeilen, jede Zeile enthält Spalten. Wir geben daher in einer Tabelle eine Zeile nach der anderen aus. Innerhalb jeder Zeile werden die Spalten nacheinander angegeben. Es muss nicht jede Zeile die gleiche Spaltenanzahl besitzen. Das Grundgerüst sieht daher wie folgt aus: <table border> <tr> <th>Kopfzelle</th> <th>Kopfzelle</th> </tr> <tr> <td>Datenzelle</td> <td>Datenzelle</td> </tr> </table> Diese Tabelle besitzt eine Kopfzeile mit zwei Spalten und eine weitere Zeile mit ebenfalls zwei Zeilen. Eine Tabelle wird in das Tag table eingeschlossen. Wird die Angabe border im Tag verwendet, so erhält die Tabelle einen Rahmen, ansonsten wird kein Rahmen ausgegeben. Zu weitergehenden Tabellen-Formatierungen sei beispielsweise auf Selfhtml verwiesen. Eine Zeile wird in das Tag tr eingeschlossen, eine Spalte in das Tag td. Die erste Zeile darf stattdessen auch das Tag th enthalten. Diese Multimedia Entwicklung (Teil2) Edwin Schicker 5 Spalten werden dann als Spalten einer Kopfzeile interpretiert. Die Darstellung dieser Kopfzeilen hängt vom verwendeten Browser ab (meist Fettdruck, Zentrierung und größere Spaltenhöhe). Beim Auslesen von Datenbankinhalten mit dem Select-Befehl werden wir häufig auf Tabellen zugreifen müssen. Doch auch der Verweis auf Referenzen im Internet, auf Bilder oder auf andere HTMLSeiten sind häufig erforderlich. Die Syntax von Referenzen lautet: <a href="URL">Verweistext</a> Als URL kann entweder eine Internetadresse oder eine lokale Datei angegeben werden. Beispiele: <a href="http:/homepages.fh-regensburg.de/~sce39014"> Homepage von Edwin Schicker</a> <a href="superbild.jpg">Verweis auf ein Superbild</a> <a href="weiter.html">Nächste Html-Seite</a> Das erste Beispiel enthält einen Verweis auf eine Internetseite, das zweite einen Verweis auf ein Bild, das dritte einen Verweis auf eine andere HTML-Seite. Zu beachten ist, dass diese Verweise existieren sollten. In den beiden letzten Beispielen sollten also im gleichen Verzeichnis, in dem sich die aktuelle HTML-Seite befindet, auch die beiden angegebenen Dateien stehen. Ein Problem stellen deutsche Umlaute dar. Viele HTML-Editoren setzen die deutschen Umlaute automatisch in den notwendigen Code um. Im Folgenden ist dieser Code angegeben: &auml; &ouml; &uuml; &szlig; statt statt statt statt ä ö ü ß &Auml; &Ouml; &Uuml; statt statt statt Ä Ö Ü Natürlich sollten auch Kommentare in einem HTML-Dokument nicht fehlen. Diese werden wie folgt geschrieben: <!-- Dies ist ein Kommentar --> 2.2 HTML-Formulare Während zum Auslesen von Daten aus Datenbanken häufig Tabellen verwendet werden, müssen zur Auswahl der gewünschten Daten meist Formulare ausgefüllt werden. Im Bereich von ECommerce müssen persönliche Angaben einschließlich der Kreditkartennummer erfasst werden. Auch bei der Suche von bestimmten Daten wie Büchern oder CDs müssen Suchbegriffe und Rubriken eingegeben werden. Hier unterstützen auch Auswahllisten die Suche erheblich. Weiter müssen die eingegebenen Daten durch entsprechende Buttons bestätigt oder widerrufen werden. Hierzu verwenden wir HTMLFormulare. Das Formulargerüst lautet (die Gänsefüßchen bei Stringangaben sind hier und auch später nur notwendig, wenn die Strings Leerzeichen oder andere Steuerzeichen enthalten): <form action="Programm" method=post> <!-- Formularelemente --> </form> Ein Formular ruft ein Programm auf, das nach Eingabe der Formulardaten aufgerufen wird. Nur in wenigen Fällen reichen hier HTML-Dokumente als Ziel aus, da die eingegebenen Daten meist überprüft und weiterverarbeitet werden müssen. Wir verwenden in der Vorlesung ausschließlich ASP-Programme, es können aber genauso CGI-, Perl-, PHP- oder andere Programme eingesetzt werden. Die Methode post gibt an, dass die übergebenen Formularinhalte auf dem WWW-Server in stdin zur Verfügung gestellt werden. ASP-Programme können diese Eingaben auf einfache Art und Weise lesen. Wahlweise steht auch die Methode get zur Verfügung. Hier werden die Formularinhalten zusammen mit dem Programmnamen verschickt und sind für den Benutzer im Browserfenster sichtbar. Auf diese Multimedia Entwicklung (Teil2) Edwin Schicker 6 Methode werde ich im Weiteren verzichten. Werden keine Formularinhalte weitergereicht, so ist die Angabe einer Methode nicht erforderlich. Ein Formular kann unterschiedliche Eingabemöglichkeiten verwenden, etwa: • • • • Einzeiliges Eingabefeld Auswahlliste Absendebutton Abbruchbutton Weitere Möglichkeiten sind mehrzeilige Eingabefelder, Radiobuttons oder Checkboxen und einige Neuerungen, die erst ab HTML V4.0 zur Verfügung stehen und daher nicht auf allen Browsern ablauffähig sind (z.B. allgemeine Klickbuttons und Gruppierung). Wieder wird dazu auf Selfhtml verwiesen. Wir stellen kurz die HTML-Tags vor, die die obigen Eingabemöglichkeiten beschreiben. Beginnen wir mit den einzeiligen Eingabefeldern: <input size=x maxlength=x name="Elementname" value="Text"> Size gibt die Größe des Eingabefeldes in Anzahl von Zeichen an. Maxlength beschreibt die maximale Anzahl von Zeichen, die eingegeben werden dürfen. Überschreitet die Eingabe die Größe size, so wird die Eingabe gescrollt. Der Name des Eingabefeldes ist für die Weiterverarbeitung wichtig. Das im Form-Tag angegebene Programm findet die Eingabe unter diesem Namen. Die Angabe value ist wahlfrei und gibt eine Vorbelegung an. Variationen sind beispielsweise: <input readonly size=x maxlength=x name="Elementname" value="Text"> <input type=password size=x maxlength=x name="Elementname"> Im ersten Fall ist das Eingabefeld nicht veränderlich, die Angabe value muss hier angegeben werden. Im zweiten Fall erfolgt die Eingabe ohne Echo, um etwa Passwörter eingeben zu können. Auswahllisten, auch als Comboboxen bezeichnet, können auf einfache Weise in HTML erstellt werden. Der Aufbau dieser Listen sieht wie folgt aus: <select multiple name="Elementname" size=x> <option> Eintrag <option> anderer Eintrag </select> Das Select-Tag enthält Option-Tags. Die Größe der Auswahlliste entspricht also der Anzahl der Option-Tags. Die wahlfreie Angabe size bewirkt, wie viele Elemente angezeigt werden. Ist x=1, so wird eine Drop-Down-Box generiert. Dies ist die Standardeinstellung. Die ebenfalls wahlfrei Angabe multiple bewirkt, dass mehrere Elemente ausgewählt werden können. Standardmäßig ist nur eine Auswahl erlaubt. Betrachten wir noch zwei interessante Varianten. <option selected> noch ein Eintrag <option value="Wert"> weiterer Eintrag Die Angabe selected bewirkt eine Vorauswahl. Die Angabe value gibt einen Ersatzwert an, der statt des Listeneintrags an das Programm (im Form-Tag angegeben) weitergereicht wird. Innerhalb eines Form-Tag-Bereichs können auch noch zwei Buttons verwendet werden, ein Button zum Absenden der Daten und ein Button zum Rücksetzen. Diese Buttons werden wie folgt definiert: <input type=submit value="Beschriftung"> <input type=reset value="Beschriftung"> Die innerhalb eines Formularbereichs eingetragenen Daten werden erst durch Drücken des SubmitButtons abgeschickt. Umgekehrt werden alle Einträge wieder zurückgesetzt, wenn der Reset-Button Multimedia Entwicklung (Teil2) Edwin Schicker 7 angeklickt wird. In HTML 4.0 können auch weitere Buttons definiert werden. Ältere Browser unterstützen diese Buttons aber noch nicht. Radiobuttons und Checkboxen existieren dafür bereits seit Langem. Die Definitionen lauten: <input type=radio name="Name" value="Wert"> Text <input type=checkbox name="Name" value="Wert"> Text Es ist zu beachten, dass alle Radiobuttons mit dem gleichen Namen zusammengehören. Es kann also immer nur ein Radiobutton dieser Gruppe angeklickt sein. Der angegebene Wert wird beim Klicken des Submit-Buttons übertragen. Das Gleiche gilt sinngemäß auch für Checkboxen, nur können hier kein, ein oder mehrere Elemente einer Gruppe markiert sein. 2.3 Einfaches Beispiel zu Formularen Nach so viel Stoff wollen wir ein kleines Beispiel folgen lassen. Bisher haben wir aber noch nicht kennengelernt, wie wir innerhalb eines Formulars eingegebene Werte im Ziel-Dokument auswerten können. Aus diesem Grund wollen wir uns mit einem recht einfachen Formular begnügen. Dieses Formular ist Ausgangspunkt unserer Vorlesung und die Defaultseite auf dem Server (default.htm). Wir definieren mehrere Buttons. Ein Klick auf einen dieser Buttons führt auf von mehreren Beispielseiten, die wir alle im Laufe der Vorlesung noch kennenlernen werden. Betrachten wir den HTML-Code in leicht gekürzter Form: <html> <head> <title>Erstes Beispiel einer Formularseite</title> </head> <body> <center><h1>Vorlesung MI</h1></center> <center><h3>Edwin Schicker</h3></center> Diese Seite ist die Einstiegsseite zur Vorlesung Multimedia und Internet (MI) von Edwin Schicker. Von hier kann durch Klick auf den entsprechenden Button zu den einzelnen Beispielen der Vorlesung verzweigt werden. <p> Folgende Seiten stehen zur Verfügung: <p> <table cellpadding=10> <tr> <td align=right> <form action="name.html"> <input type="Submit" value="Erstes ASP-Beispiel"> </form> </td> <td valign=top> Die Datei <i>name.html</i> wird angesprungen </td> </tr> <tr> <!-- Die folgenden Tabellenzeilen sind analog aufgebaut --> </tr> </table> <p> Ende der HTML-Datei </body> </html> Die Tabellenausgabe wurde leicht optimiert, indem einige Optionen hinzugefügt wurden. Cellpadding gibt den Abstand des Textes vom Zellrand (in Pixeln) an, so dass Spalten und Zeilen einen gewissen Abstand voneinander haben. Mit der Option Align kann die horizontale Ausrichtung und mit Valign die vertikale Ausrichtung beeinflusst werden. Es ist zu beachten, dass hier und auch in allen weiteren Beispielen die deutschen Umlaute nicht umschrieben wurden. Wir hätten an Stelle des Wortes gewünschten das Wort gew&uuml;nschten schreiMultimedia Entwicklung (Teil2) Edwin Schicker 8 ben müssen. Wir haben aus Gründen der Übersichtlichkeit aber darauf bewusst verzichtet. Viele HTML-Editoren, wie auch der von Uli Meyborn, setzen deutsche Umlaute automatisch um. Wir können hier also wie gewohnt arbeiten. Jedes Formular-Tag enthält genau einen Submit-Button. Im Formular-Tag wurde mit der Option Action eingetragen, wohin beim Klick auf den Submit-Button verzweigt werden soll. In der Regel soll jedoch nicht nur verzweigt werden. Es sollen auch eingetragene Daten ausgelesen werden. Dies ist aber nur noch mittels Programmierung möglich, z. B. mit DHTML (Dynamic HTML). Wir wollen diese Möglichkeiten am Beispiel von Active Server Pages aufzeigen. 3 Active Server Pages Internetprogramme sind oft in der Sprache Perl geschrieben. Dies liegt vor allem daran, dass Perl auf allen Plattformen (Windows, Unix, Mac) verfügbar ist und dass WWW-Server meist auf Unix-Rechnern (etwa Linux) zusammen mit der Software von Apache installiert sind. In der Windows-Welt dominieren hingegen Active Server Pages. Inzwischen wurden Active Server Pages auch auf Unix portiert. Die Entscheidung obliegt jedem Einzelnen und ist auch Geschmackssache. Prinzipielle Unterschiede gibt es kaum. Das mit Perl oft benutzte CGI-Interface ist allerdings veraltet und sollte nicht mehr verwendet werden. In letzter Zeit verdrängt PHP die Sprache Perl. Inwieweit sich PHP durchsetzt, wird die Zukunft zeigen. Da ich in meinem letzten Industriesemester einige Erfahrung mit Active Server Pages gesammelt habe, werde ich in der Vorlesung auf diese Erfahrungen zurückgreifen. 3.1 Installation eines eigenen WWW-Servers Um die Beispiele der Vorlesung auch auf dem eigenen Rechner nachvollziehen zu können, benötigen wir einen WWW-Server. Ein WWW-Server benötigt entweder das Betriebssystem Unix oder Windows NT/2000, zur Not auch Windows95/98. Beginnen wir mit WinNT. Hier haben wir die Auswahl: entweder wir verwenden den Personal Web Server oder die Peer Web Services. Die Unterschiede: Peer Web Service wird zusammen mit Windows NT Workstation und Server ausgeliefert. Es ist beispielsweise auf der Installations CD zu Windows NT Workstation im Verzeichnis /i386/inetsrc enthalten. Die Installation erfolgt durch Start der Datei inetstp.exe in diesem Verzeichnis, Administratorrechte werden vorausgesetzt. Der Peer Web Service ist ein einfacher WWW-Server. Zum Aufruf von HTML-Seiten ist dieser Service aber ausreichend. Active Server Pages hingegen kann dieser Server nicht verarbeiten. Für diese Vorlesung reicht dieser Service also nicht aus. Alternativ kann von Microsoft kostenlos das Option Pack 4.0 heruntergeladen werden. Microsoft fordert lediglich eine Registrierung. Dieses Option Pack gibt es sowohl für Windows NT Server als auch für NT Workstation. Die Workstation-Version hat einen wesentlich geringeren Umfang (35 MBytes) als die Server-Version (ca 80 MBytes), reicht für unsere Zwecke jedoch vollkommen aus. Active Server Pages (ASP) werden unterstützt, ebenso der Zugriff auf Datenbanken mittels ActiceX Data Objects (ADO). Eine umfangreiche Dokumentation zu ASP, ADO, MTS (Microsoft Transaction Server), Visual Basic und Javascript ist enthalten, zahlreiche Beispiele erleichtern die Einarbeitung. Die Installation unter Win2000 Workstation ist erheblich einfacher. Die Installations-CD bietet beim Einlegen im Anfangsmenü gleich die Möglichkeit der Installation des IIS-Servers an. Es ist einfach den Anweisungen zu folgen. Die Dokumentation ist allerdings nicht ganz so umfangreich wie beim Option Pack 4.0. Auch Windows 98 enthält auf der Installations-CD einen kleinen IIS-Server, der für unsere Zwecke ebenfalls voll ausreicht. Natürlich können unter Fat32 keine Zugriffsrechte vergeben werden, so dass dieser IIS-Server ausschließlich zum Experimentieren verwendet werden sollte. In allen Fällen muss auf dem Rechner ein Netzwerk mit TCP/IP-Protokoll eingerichtet werden, etwa mit der Netzwerkadresse 192.168.255.1. Die WWW-Installation richtet ein WWW-Root-Verzeichnis ein, standardmäßig c:\inetpub\wwwroot. Wird ein HTML-Dokument namens default.htm in diesem Multimedia Entwicklung (Teil2) Edwin Schicker 9 Verzeichnis abgelegt, so ist dieses Dokument auf allen Rechnern, die mit diesem Server verbunden sind, unter der Adresse http://192.168.255.1/default.html zu erreichen. Sind Client und Server identisch, greifen wir also lokal auf den Server zu, so können wir statt der IP-Adresse auch schreiben: http://localhost/default.html. Ein erster Vorteil eines kleinen WWW-Servers liegt darin, dass wir Default-Verzeichnisse für Bilder und andere Sourcen angeben können. Dort können wir dann diese ablegen und benötigen nicht jeweils den vollständigen Pfad in unserem HTML-Dokument. Doch insbesondere bei Active Server Pages sind wir auf den WWW-Server sogar angewiesen. Dieser interpretiert nämlich den vorhandenen Code und gibt als Ergebnis den erzeugten HTML-Text an den Browser weiter. Diese serverseitige Interpretation erfolgt aber nur, wenn diese Seite nicht direkt als Datei, sondern über den Server in den Browser kopiert wird. 3.2 Active Server Pages (ASP) Active Server Pages wurden von der Firma Microsoft eingeführt. Hier handelt es sich um HTML-Seiten, die um einen Programmcode erweitert sind. Diese Dateien besitzen zur Unterscheidung von reinen HTML-Seiten die Endung .asp. Wir müssen nur ganz wenige Regeln hinzulernen: • In der ersten Zeile muss mitgeteilt werden, welche Programmiersprache verwendet wird. Wir schreiben daher eine der folgenden Zeile, je nachdem ob wir mit Javascript oder Visual Basic arbeiten wollen: <%@ LANGUAGE = "JScript" %> <%@ LANGUAGE = "VBScript" %> • Der Programmcode wird eingeklammert in <% … %> . Die Klammern wirken über beliebig viele Zeilen. Der WWW-Server gibt den in diesen Klammern stehenden Code nicht aus, sondern interpretiert diesen zunächst. Erst das Ergebnis dieser Interpretation wird an den Browser weitergeleitet und dort als HTML-Code behandelt. Auf dem Server sollten daher ASP-Dateien keine Lese-, sondern nur Ausführungsrechte besitzen. In Javascript und Visual Basic können mittels der Methode POST übergebene Werte mit dem in ASP definierten Objekt Request mittels der Methode Form ausgegeben werden. Diese Methode benötigt als String-Parameter den Name des Formularfeldes und liefert den Inhalt dieses Feldes als Funktionswert zurück. Betrachten wir ein Beispiel: Wir fordern in einem HTML-Dokument einen Namen in einem Eingabefeld an. Weiter wollen wir die Anrede wissen. Hier geben wir Radiobuttons vor. Das Dokument könnte wie folgt aussehen (gespeichert in der Datei name.html): <html> <head> <title>Erstes ASP-Beispiel</title> </head> <center><h1>Vorlesung MI</h1></center> <center><h3>Edwin Schicker</h3></center> Der Name ist in einem Eingabefeld und die Anrede mittels eines Radiobuttons anzugeben. Durch Klicken des Submit-Buttons wird auf eine ASP-Seite gewechselt, in der diese Daten ausgelesen und im Browser ausgegeben werden. <p>Bitte geben Sie die entsprechenden Daten ein und klicken dann den Weiter-Button. Der Rücksetzbutton erlaubt eine Neueingabe.</p> <form action="name.asp" method=post> <!--Sprung nach name2.asp--> <table cellpadding=10> <tr> <td align=right>Bitte geben Sie Ihren Namen ein: </td> <td> <!-- Eingabefeld: --> <input size=30 maxlength=30 name=Name> </td> Multimedia Entwicklung (Teil2) Edwin Schicker 10 </tr> <tr> <td align=right>Ihre Anrede: </td> <td> <!-- Radiobutton: --> Herr<input type=radio name=Anrede value=1> Frau<input type=radio name=Anrede value=2> </td> </tr> <tr> <td align=right> <input type="Submit" value="Weiter"> </td> <td> <input type="Reset" value="Rücksetzen"> </td> </tr> </table> </form> </body> </html> Dieses Beispiel enthält genau einen Formular-Tag, der wiederum ein Eingabefeld mit dem Namen Name und einen Radiobutton mit dem Namen Anrede enthält. Diese Eingaben sind wieder mittels einer Tabelle übersichtlich angeordnet. Durch Klick auf den Submit-Button wird in die Datei name.asp, unsere erste ASP-Datei, verzweigt. Diese Datei kann nun mittels der Funktionen Request.Form("Name") und Request.Form("Anrede") auf die Inhalte der beiden Eingaben zugreifen. Sehen wir uns also auch die Datei name.asp an: <%@ LANGUAGE = "JScript" %> <html> <head> <title>Erstes ASP-Beispiel, Ergebnis</title> </head> <center><h1>Vorlesung MI</h1></center> <center><h3>Edwin Schicker</h3></center> <!-- Die mit der Methode POST uebergebenenen Daten werden ausgelesen: --> <% // Javascript! Das Objekt REQUEST liefert mittels der Methode // FORM die uebergebenen Daten! name = Request.Form("Name"); geschlecht = Request.Form("Anrede"); if (geschlecht==1) anrede = "Sehr geehrter Herr "; else anrede = "Sehr geehrte Frau "; %> <%=anrede%> <%=name%>! Geschafft! Die erste ASP-Seite wurde ausgeführt und erfolgreich interpretiert. Es wird nicht der Code, sondern nur das Ergebnis an den Client weitergereicht. <p> Das erste ASP-Beispielprogramm wäre damit bearbeitet. Mit dem folgenden Link kehren Sie zur Anfangsseite zurück. <p> <center><a href="default.htm">Startseite</a></center> </body> </html> Wie bereits erwähnt enthält die erste Zeile den Hinweis auf die verwendete Sprache, hier Javascript. Der Code selbst ist geklammert in <% … %>. Im Wesentlichen haben wir nur die übergebenen Werte ausgelesen und dann an den Browser weitergegeben. Die Ausgabe erfolgt am Einfachsten mittels der Schreibweise <%= … %>, doch mehr im nächsten Abschnitt. Multimedia Entwicklung (Teil2) Edwin Schicker 11 3.3 Ein- und Ausgabe in Active Server Pages Die Eingabe haben wir bereits im letzten Abschnitt kennengelernt. Sie erfolgt mittels Formularen. Wir wollen hier aber etwas genauer die Möglichkeiten der Weiterverarbeitung dieser Eingaben betrachten. Mittels des Form-Tags geben wir ein Zieldokument an. Im Zieldokument müssen wir die übergebenen Daten auswerten. Dies erfolgt in ASP am Einfachsten mittels des Request-Objekts. Das Request-Objekt besitzt unter anderem die Methoden Form und Querystring. Die Methode Form liest Werte ein, die mittels der Methode Post übergeben wurden, die Methode Querystring liest die mittels Get übergebenen Werte. Die Syntax ist identisch, so dass wir nur die Methode Form betrachten wollen: Request.Form("Name") Request.Form("Name").Count Request.Form("Name") (i) // Inhalt, der im Formular Name eingegeben wurde // Anzahl der im Formular Name eingegebenen Werte // Inhalt des i-ten Wertes aus dem Formular Name Die erste Zeile haben wir bereits in einem Beispiel verwendet. Zu bemerken ist, dass eingegebene Daten immer als Zeichenketten weitergegeben werden. Doch Javascript ist mit der Umwandlung in andere Datentypen sehr großzügig. Wurden bestimmte Formularfelder nicht ausgefüllt, so können wir dies leicht mit der Methode Count überprüfen. Der Wert von Count ist 0, falls nichts eingegeben wurde, und 1 sonst. In Select-Boxen mit Mehrfachauswahl kann dieser Zähler natürlich auch größere Werte annehmen. Jede einzelne Auswahl kann dann mittels der dritten Schreibweise einzeln angesprochen werden, der Zählindex beginnt bei 0. Die Ausgabe innerhalb eines Programmabschnitts ist ebenfalls einfach. Konstante Inhalte werden am Einfachsten dadurch ausgegeben, dass der Programmabschnitt beendet und nach der Ausgabe der Zeichenkette wieder begonnen wird: … %> Dies ist eine konstante Zeichenkette <% … Es ist zu beachten, dass mit dem Beenden eines Programmabschnitts nicht auch automatisch Programmblöcke ( { … } ) beendet werden. Eine geöffnete geschweifte Klammer muss innerhalb eines ASP-Programms auch wieder geschlossen werden! Ansonsten erhalten wir einen Interpreter-Fehler. Aus Sicht des ASP-Programms werden also HTML-Texte außerhalb von Programmabschnitten wie Kommentare, also als nicht existent betrachtet. Für die Ausgabe von Variablen existiert zunächst eine bereits angewandte Möglichkeit: <%= Variablenname %> Diese Ausgabemöglichkeit ist aber oft nicht flexibel genug. Genaugenommen ist diese Schreibweise nur eine Abkürzung für <% Response.Write( Variablenname ); %> Das in ASP definierte Ausgabeobjekt Response ist also das Gegenstück zum Eingabeobjekt Request. Mittels der Methode Write können Variablen und konstante Texte ausgegeben werden. Write erwartet genau einen Parameter, entweder eine Zeichenkette oder einen numerischen Ausdruck. Dank der impliziten Typumwandlung können aber auch Zeichenketten und numerische Ausdrücke mittels des Konkatenierungsoperators (‚+’) verknüpft werden, etwa wie folgt: Response.Write( "Variableninhalt: " + Variablenname); Es sollte nicht übersehen werden, dass zwar das Zeichen ‚\n’ innerhalb einer Zeichenkette verwendet werden darf, in HTML-Dokumenten bei der Ausgabe aber keinerlei Wirkung zeigt. Stattdessen ist einer der beiden HTML-Tags ‚<br>’ oder ‚<p>’ zu benutzen. Multimedia Entwicklung (Teil2) Edwin Schicker 12 3.4 Javascript An dieser Stelle soll Javascript nicht ausführlich vorgestellt werden. Gute Referenzen finden wir in Selfhtml und in der Online Dokumentation des Option Packs von Microsoft. Ganz grob ist Javascript ein extrem abgespecktes Java, so stark abgespeckt, dass kaum Unterschiede zu C++ (außer bei Strings) feststellbar sind. Eine ganz grobe Zusammenfassung sei hier gegeben: Kontrollstrukturen (Schleifen und Verzweigungen), Anweisungsblöcke ( { … } ), Zuweisungen und Kommentare sind wie in Java und C++ definiert! Ebenso können Funktionen definiert werden, die dann in der gesamten Datei sichtbar sind. Natürlich sind auch die meisten Operatoren implementiert, auch die Bindungshierarchie ist wie in Java aufgebaut. Wichtige Unterschiede gibt es allerdings in der Handhabung von Variablen. Insbesondere müssen in Javascript Variablen nicht vordefiniert werden. Es gibt also keine Int- oder Float-Variablen im herkömmlichen Sinne. Stattdessen gibt es vordefinierte Objekte, z.B. number, date oder string. Javascript entscheidet bei der ersten Verwendung einer Variable, welchem Objekttyp diese Variable zugeordnet wird. Bei String-Variablen ist bei der Verwendung der Operatoren ‚==’ und ‚!=’ Vorsicht geboten. Es wird nicht der Inhalt der Objekte verglichen, sondern die Objekte selbst! Der Vergleich der Inhalte funktioniert nur bei den anderen Vergleichsoperatoren. Auch leistet die Konkatenierung (‚+’) häufig große Dienste. Die folgenden Zeilen vergleichen zwei Strings, die dritte Zeile ist allerdings semantisch falsch: if (str1 >= str2 && str1 <= str2) if (str1.toString() == str2.toString()) if (str1 == str2) // Fehler, da Vergleich der Objekte! Bevor wir es vergessen: In Javascript muss eine Anweisung am Ende einer Zeile nicht mit einem Semikolon beendet werden. Die Anweisungen break und continue arbeiten wie bekannt, return darf allerdings nur innerhalb einer Funktion benutzt werden. Das „ASP-Hauptprogramm“ kann also nicht einfach mit Javascript-Mitteln abgebrochen werden. 3.5 Übungsaufgaben 1. Schreiben Sie eine HTML-Seite, die mehrere Formulareingaben anfordert (Eingabefeld, Checkbuttons, Radiobuttons). Durch Klick auf den Submit-Button werden diese Daten tabellarisch ausgegeben. Verwenden Sie die Methode post. 2. Wie Aufgabe 1, nur dass die Methode get verwendet wird. 3. Schreiben Sie eine HTML-Seite, die mehrere Multiple-Choice-Fragen enthält. Der Anwender soll diese Fragen beantworten und dann den Submit-Button drücken. Werten Sie die Antworten aus und geben das Ergebnis zurück. 4 ActiveX Data Objects Im letzten Kapitel haben wir kennengelernt, im Internet zu programmieren. Es fehlt noch der Datenbankzugriff. Hier gibt es einige Begriffe wie ADO (ActiveX Data Objects), ODBC (Open Database Connectivity), OLE-DB (Object Linking and Embedding for Data Bases), die vorab geklärt werden müssen. Anschließend wollen wir dann mittels ADO auf Datenbanken zugreifen. 4.1 ODBC, OLE DB Zum Zugriff auf Datenbanken benötigen wir vordefinierte und international normierte Schnittstellen. Eine solche Schnittstelle, die darüberhinaus weit verbreitet ist, ist Open Database Connectivity (ODBC). Diese Schnittstelle wurde von DEC, Lotus, Microsoft und Sybase unter Federführung von Kyle Geigers entwickelt, wurde 1992 definiert und ist heute ein Weltstandard. Insbesondere in der Microsoftwelt ist es die Standardschnittstelle. Praktisch alle CGI-Programme (CGI = Common Gateway Interface) setzen auf der ODBC-Schnittstelle auf. Multimedia Entwicklung (Teil2) Edwin Schicker 13 Ziel dieser Schnittstelle ist es, dem Programmentwickler nur den Namen einer Datenbank weiterzugeben. Der physische Ort der Datenbank und der Typ der Datenbank werden ihm verborgen. Damit kann ein Programm die Datenbank wechseln, ohne dass auch nur eine Zeile Code neu geschrieben werden müsste. Die Verwaltung übernimmt ODBC. Das Programm selbst kennt also nur den ODBCDatenbanknamen. ODBC kapselt also die Datenbank und verwaltet weitere Details selbst. Die ausschließliche Zugriffssprache ist SQL (Structured Query Language). Zum Zugriff auf Datenbanken über ODBC benötigt der WWW-Server folglich einen ODBC-Treiber. In Windows existiert dazu in der Systemsteuerung der Eintrag „ODBC-Datenquellen“. Hier werden in der Regel im Ordner System-DSN die entsprechenden Daten eingetragen. Zumindest der Pfad zur Datenbank sollte angegeben werden. Auch bei OLE-DB (OLE = Object Linking and Embedding) handelt es sich um eine Schnittstelle zwischen Datenbanken und den Benutzerprogrammen. Diese Schnittstelle wurde von Microsoft entwickelt und wurde in den letzten Jahren erheblich erweitert und verbessert. Diese Schnittstelle wird direkt von ADO (ActiveX Data Objects) unterstützt und gilt als sehr performant. Im Unterschied zu ODBC ist OLE-DB nicht allein auf Datenbanken fixiert. Damit ist auch nicht mehr die Sprache SQL als Zugriffssprache vorgeschrieben. OLE-DB kann dabei direkt oder über ODBC auf Datenbanken zugreifen. Wir werden beide Wege kennenlernen. Der Weg über ODBC schottet die Datenbank sauber vom Programm ab, so dass Datenbank-Änderungen (andere Datenbank, neuer Pfad) keine Änderungen am Programm erfordern. Der direkte Weg ohne ODBC spart wiederum eine Schnittstelle und ist daher performanter. Betrachten wir jetzt den schematischen Weg vom Browser zu den Daten: Multimedia Entwicklung (Teil2) Edwin Schicker 14 In unseren Programmen werden wir über ADO auf Datenbanken zugreifen. Die Wege OLE-DB und ODBC leiten wir nur an Hand eines entsprechenden Befehls ein. Der Hauptaufwand zur Datenbankprogrammierung liegt also bei ADO. 4.2 ActiveX Data Objects (ADO) ADO ist eine Programmschnittstelle zum Zugriff auf Datenbanken. ADO ist nahtlos in ASP eingebettet. Genaugenommen stellt ADO nur einige zusätzliche Objekte zur Verfügung. Diese Objekte unterstützen den Aufbau einer Verbindung zu einer Datenbank und deren Zugriff. Wir benötigen aus ASP folgende Objekte: • • • Request-Objekt zum Lesen der übergebenen Daten Response-Objekt zur Ausgabe von Daten Server-Objekt zum Verwalten von Informationen und Strukturen Wir benötigen von ADO die folgenden Objekte: • • • • • Connection-Objekt zum Verbindungsaufbau zur Datenbank RecordSet-Objekt zum Auslesen der Daten aus der Datenbank Command-Objekt zum Auslesen und Schreiben in die Datenbank Field-Objekt zum Lesen einzelner Datenfelder (Attribute) Error-Objekt zur Fehlerbehandlung Grau ist jede Theorie. Wir wollen die einzelnen Möglichkeiten an Beispielen kennenlernen. 4.3 Erster Zugriff auf Datenbanken Wir wollen eine Verbindung zur Beispieldatenbank Radl herstellen. Dazu setzen wir voraus, dass auf dem WWW-Server die Datenbank Radl eingerichtet ist und eine ODBC-Verbindung zu dieser Datenbank hergestellt wurde. Wir wollen nun aus der Relation Personal den Namen und den Wohnort des Mitarbeiters auslesen, der die Personalnummer 2 besitzt. Der entsprechende SQL-Befehl ist einfach und lautet: Select Name, Ort From Personal Where Persnr = 2; Diesen Befehl müssen wir jetzt in unserem ASP-Programm unterbringen. Wir benötigen dazu folgende Schritte: • • • • Verbindungsaufbau und Einloggen in die Datenbank Senden des Select-Befehls Empfang und Auswerten der Daten Schließen der Datenbank Diese vier Schritte werden mit Hilfe von ADO-Objekten realisiert. Die Umgebung bleibt weiterhin ASP, die Programmiersprache Javascript. Betrachten wir gleich das vollständige Beispiel: <%@ LANGUAGE = "JScript" %> <html> <head> <title>Erster DB-Zugriff</title> </head> <body> <center><h1>Vorlesung ME</h1></center> <center><h3>Edwin Schicker</h3></center> Multimedia Entwicklung (Teil2) Edwin Schicker 15 Es erfolgt ein Einloggen in die Datenbank RADL mit dem ODBC-Namen "Radl". Name und Ort des Mitarbeiters mit Persnr 2 wird ausgegeben, falls existent! <p> <% Response.Write("Verbindung zur Datenbank herstellen.<br>"); conn = Server.CreateObject("ADODB.Connection"); // Verbindungs-Objekt conn.Provider = "Microsoft.Jet.OLEDB.4.0" // MS-Access-Treiber conn.Open("F:\\user\\XXXXXXXX\\public_html\\Radl.mdb"); // Pfad if (conn.Errors.Count==0) // kein Fehler { Response.Write("Die Verbindung zur Datenbank wurde hergestellt.<p>"); // Die Datenbank ist geoeffnet, jetzt wird zugegriffen: sqlstring = "Select Name, Ort From Personal Where Persnr = 2"; suchliste = conn.Execute(sqlstring); // Select-Befehl ausfuehren // Ausgeben der gelesenen Daten: if (!suchliste.EOF) { %> <% %> <% Mitarbeiter (Persnr 2): <%= suchliste("Name")%> aus <%= suchliste("Ort")%> } else { Der Mitarbeiter mit der angegebenen Nummer existiert nicht! } Response.Write("<p>Die Verbindung zur Datenbank wird geschlossen."); conn.Close() } else // Fehler beim Einloggen { Response.Write("Fehler beim Verbindungsaufbau: " + conn.Errors(0)); } %> </body> </html> Betrachten wir das kleine Programm Zeile für Zeile. Die Methode CreateObject des Server-Objekts erzeugt ein neues Connection-Objekt, in dem die gesamten Verbindungsdaten aufgenommen werden. Die Variable conn ist also ein Zeiger auf ein solches Connection-Objekt und wird im Folgenden für alle Verbindungen zu unserer Datenbank benötigt. Die Methode Open des Connection-Objekts stellt eine Verbindung zu einer Datenbank her und öffnet diese. Wir geben dazu vorher den MS-AccessTreiber an, um eine direkte OLE-DB-Verbindung zu erhalten. Als Open-Parameter ist die MS-AccessDatenbank anzugeben. Wurde diese Zeile erfolgreich abgearbeitet, so ist die Verbindung zur Datenbank hergestellt und aktiviert. Die Datenbankverbindung wird erst mit der Methode Close wieder aufgegeben. Wird die Datenbank nicht explizit wieder geschlossen, so wird nach einer vordefinierten Zeit (ca. 15 Minuten) die Verbindung automatisch abgebrochen. Diese Zeitspanne kann mittels ADO auch geändert werden. Möglicherweise gibt es Probleme beim Einloggen in die Datenbank. In diesem Falle werden Fehlermeldungen erzeugt, die mittels des Error-Objekts abgefragt werden können. Die Methode Count zählt die Fehlermeldungen und die Indizierung erlaubt die Ausgabe der einzelnen Fehler. Im Fehlerfall (Count > 1) wird an das Ende des Programms verzweigt, die erste Fehlermeldung wird ausgegeben. Nach erfolgreichem Einloggen wird der Select-Befehl abgeschickt. Dies wurde im Programm in zwei Schritten erledigt. Erst wird der Select-Befehl einer String-Variablen zugewiesen und dann wird die Methode Execute des Connection-Objekts aufgerufen. Diese Methode führt die übergebene ZeichenMultimedia Entwicklung (Teil2) Edwin Schicker 16 kette als Befehl aus, baut eine Liste auf, speichert die erhaltenen Daten dort ab und gibt einen Zeiger auf diese Liste zurück. Alle Listen besitzen die Methode Count. Wurden keine Daten zurückgeliefert, so ist die Liste leer und der Zähler ist gleich Null. Andernfalls liegen Daten vor. Diese Daten werden mit dem Attributsnamen angesprochen. Es ist zu beachten, dass also gegebenenfalls Aliasnamen im Select-Befehl verwendet werden müssen, um erlaubte Bezeichner zu erhalten. Wie bereits im vorigen Abschnitt erwähnt wurde, ist der Zugriff über ODBC etwas imperformant. Dafür muss im Programm kein Pfad und kein Treiber angegeben werden (diese Angaben finden wir direkt in ODBC), die Datenbank kann daher ohne Code-Änderung gewechselt werden (nur Eintragsänderung in ODBC). Die Anweisungen im Programm lauten jetzt einfach: conn = Server.CreateObject("ADODB.Connection"); conn.Open("Radl"); // Angabe des ODBC-Namens // internes Objekt Die erste Zeile blieb unverändert. Der Open-Befehl enthält jetzt nur den eingetragenen ODBC-Namen. Wird die Eigenschaft Provider nicht gesetzt, so wird standardmäßig der ODBC-Treiber gewählt. Der Treiber für Access-Datenbanken lautet immer Microsoft.Jet.OLEDB.x, wobei x die aktuelle Version angibt (zur Zeit 4.0). Um bei Änderung des Treibers und/oder der Datenbank nicht immer das Programm ändern zu müssen, gibt es auch die Möglichkeit, diese Daten aus einer Datei zu lesen, worauf wir aber nicht näher eingehen. Da auf dem Server nicht für jeden Benutzer ein ODBC-Eintrag existiert, müssen wir beim Zugriff auf eine Datenbank im Rahmen der Vorlesung immer den Provider und den Pfad der Datenbank angeben! 4.4 Suche mehrerer Daten in Datenbanken Im letzten Abschnitt haben wir nur eine ganz bestimmte Zeile ausgelesen. Wir wollen jetzt wesentlich flexibler mit Datenbanken umgehen. Wir geben dazu einen Suchbegriff ein und erhalten kein, ein oder mehrere Resultate. Das dazugehörige Handling wollen wir jetzt kennenlernen. Bleiben wir dazu bei der Relation Personal. Wir geben einen String ein, und anschließend werden alle Namen zusammen mit Personalnummer, Wohnort, Geburtsdatum und Gehalt ausgegeben, die diesen String in ihrem Namen enthalten. Groß- und Kleinschreibung spiele dabei keine Rolle. Wir schreiben dazu eine HTML-Seite, die ein entsprechendes Eingabefeld enthält. Beim Absenden wird eine ASP-Seite aufgerufen, die die Eingabe liest, die Datenbank öffnet, die Daten abruft und dann in Tabellenform ausgibt. Betrachten wir zunächst den wesentlichen Ausschnitt aus der Datei mitarbeiter.html: <form action="mitarbeiter.asp" method="post"> <table cellpadding=10> <tr> <td align=right>Bitte geben Sie die gesuchten Zeichen ein: </td> <td> <!-- Eingabefeld: --> <input size=30 maxlength=30 name=Name> </td> </tr> <tr> <td> </td> <td align=right> <input type="Submit" value="Weiter"> <!-- Submit --> </td> </tr> </table> </form> Wir erkennen eine Formular- und eine Tabellendefinition. Die Tabelle wurde nur wegen der besseren Übersichtlichkeit gewählt. Das Formular ruft beim Absenden mit dem Submit-Button die Datei mitarMultimedia Entwicklung (Teil2) Edwin Schicker 17 beiter.asp auf, die eingegebenen Daten im Eingabefeld namens „Name“ werden mit der Methode post weitergereicht. Betrachten wir gleich die entsprechenden Daten in der Datei mitarbeiter.asp: <% // Der eingelesene Teilstring wird ermittelt: name = new String(Request.Form("Name")); // eigenes Stringobjekt name = name.replace(/\s*$/, ""); // entspricht einem RTrim! if (name.length == 0) { %> Es wurden im Formular keine Angaben gemacht. <% } else { %> Die Datenbank Radl wird jetzt nach allen Mitarbeitern durchsucht, die im Namen den Teilstring "<i><%= name%></i>" enthalten. <p> <% // Aufbau einer DB-Verbindung: conn = Server.CreateObject("ADODB.Connection"); // ADO-Verbindung conn.Provider = "Microsoft.Jet.OLEDB.4.0" // ohne ODBC conn.Open("F:\\user\\XXXXXXXX\\public_html\\Radl.mdb "); // DB oeffnen if (conn.Errors.Count==0) // kein Fehler { // Die Datenbank ist geoeffnet, jetzt wird zugegriffen: sqlstring = "Select Persnr, Name, Ort, GebDatum, Gehalt " + "From Personal " + "Where Ucase(Name) Like Ucase('%" + name + "%')"; suchliste = conn.Execute(sqlstring); // SQL-Befehl ausfuehren Unterbrechen wir das Programm an dieser Stelle. Wir finden zwar zum großen Teil Altbekanntes, doch gleichzeitig auch eine Menge neuen Code. Gleich zu Beginn haben wir die übergebenen Daten zwar wie bisher mit der Methode Request.Form ausgelesen, jedoch nicht direkt an die Variable name weitergegeben. Stattdessen wurde ein neues Stringobjekt mit dem Operator new definiert. Dieser Operator erzeugt ein neues Stringobjekt und kopiert die Daten im angegebenen Parameter in dieses Objekt. Die Variable name verweist mittels der Zuweisung auf dieses Objekt. In unserem Beispiel ist dieses Vorgehen notwendig, da wir auf das Stringobjekt die Methode Replace anwenden. Diese Methode kann nur auf ein Stringobjekt angewendet werden, Request.Form("Name") ist aber kein Stringobjekt! Die hier verwendete Methode Replace entspricht einem Right Trim (Entfernen aller Whitespaces am Ende eines Strings). Leider ist in Javascript kein Trim-Operator definiert und muss relativ komplex nachgebildet werden. Der erste Parameter von Replace enthält einen Suchstring, der mittels Schrägstriche ('/') geklammert wird. Das Zeichen '\s' steht für ein Whitespace, zusammen mit dem folgenden Zeichen '*' werden beliebig viele Whitespaces angesprochen. Das Zeichen '$' steht für das Stringende. Der Suchstring ist also eine beliebige Folge von Whitespaces am Ende eines Strings. Der Ersetzstring steht im zweiten Parameter und ist hier der Leerstring. Wir sehen, dass wir tatsächlich einen Right Trim nachgebildet haben. Der weitere Code ist einfacher. Wir überprüfen, ob das Eingabefeld tatsächlich Daten enthält. Wenn nicht, so wird eine entsprechende Meldung ausgegeben und das Programm wird beendet. Ansonsten wird eine Verbindung zur Datenbank aufgebaut. Wieder greifen wir direkt mit OLE-DB zu. Es ist zu beachten, dass innerhalb eines Javastrings ein Rückstrich ('\') als Entwertungszeichen interpretiert wird. Aus diesem Grund muss als Trenner von Verzeichnispfaden die Folge '\\' eingegeben werden. Der Select-Befehl sollte keine Schwierigkeiten bereiten. Um nicht zwischen Groß- und Kleinschreibung zu unterscheiden, werden die zu vergleichenden Daten zunächst in Großbuchstaben umgewandelt. Die entsprechende Funktion in Access heißt Ucase. Mit dem Execute-Befehl wurden alle gesuchten Daten aus der Datenbank in eine Liste eingelesen. Wir müssen diese Daten jetzt in einer Schleife auf den Browser ausgeben. Wieder empfiehlt sich die Verwendung einer Tabelle. Betrachten wir dazu den Rest des Programms: Multimedia Entwicklung (Teil2) Edwin Schicker 18 // Ausgeben der gelesenen Daten: if (suchliste.EOF) { %> Mitarbeiter mit dem Teilstring im Namen existiert nicht! } else { // Daten ausgeben: <% %> Ergebnis: <br> <table border cellpadding=10> <tr> <!-- Ueberschriftenzeile: --> <th>Persnr </th> <th>Name </th> <th>Ort </th> <th>GebDatum </th> <th>Gehalt </th> </tr> <% // In einer Schleife werden die Daten ausgelesen: while (!suchliste.EOF) { %> <tr> <!-- Die ausgelesenen Daten werden gleich in die Tabelle uebernommen: --> <td> <%= suchliste("Persnr") %> </td> <td> <%= suchliste("Name") %> </td> <td> <%= suchliste("Ort") %> </td> <td> <%= suchliste("GebDatum") %> </td> <td> <%= suchliste("Gehalt") %> </td> </tr> <% suchliste.MoveNext(); // naechsten Datensatz lesen } %> </table> <% } // Die Datenbank wird jetzt geschlossen: conn.Close(); } else // Fehler beim Oeffnen der Datenbank { Response.Write("Fehler beim Verbindungsaufbau:" + conn.Errors(0)); } } %> Wir überprüfen also zunächst, ob es Daten mit den gewünschten Eigenschaften überhaupt gibt. Wenn ja, so bauen wir eine Tabelle auf. Wird in einer Tabelle statt der Spaltenangabe <td> die Angabe <th> geschrieben, so interpretiert dies der Browser als Überschriftenzeile. Meist werden diese Spalten dann fett gedruckt und zentriert ausgegeben. In einer While-Schleife werden die Daten aus der Suchliste gelesen. Zum nächsten Datensatz in der Suchliste gelangen wir mit der Methode MoveNext. Die Eigenschaft EOF teilt uns das Ende der Daten mit. 4.5 Mehrfacher Zugriff auf Datenbank Mit dem bisherigen Wissen kommen wir in den meisten Fällen aus. Wir wollen dieses Wissen jetzt vertiefen, zusätzlich Auswahllisten generieren und weitere Zugriffsmöglichkeiten auf Datenbanken mittels ADO kennenlernen. Schließlich sollen aus Performancegründen Datenbanken auch über mehrere ASP-Seiten hinweg geöffnet bleiben. Wir wollen diese neuen Möglichkeiten wieder an einem Beispiel kennenlernen. Multimedia Entwicklung (Teil2) Edwin Schicker 19 Das Beispiel in diesem Kapitel liest einen Kunden- und einen Teilenamen ein. Anschließend werden alle Auftragsdaten, die diesen Kunden und dieses Teil betreffen, ausgegeben. Bei der Eingabe eines Kunden oder eines Teils können Fehler auftreten. Um dies zu vermeiden, werden in einer Auswahlliste alle existierenden Kunden und Teile angezeigt. Der Benutzer muss nur noch jeweils einen Eintrag auswählen. Betrachten wir dazu die fertige HTML-Seite: Wir erkennen zwei Drop-Down-Boxen. Diese enthalten alle Kundennamen und alle bestellbaren Teile. Wir betrachten nicht den gesamten HTML/ASP/ADO-Code, da viele Teile bereits bekannt sind. Stattdessen picken wir die neuen interessanten Teile heraus. Wir loggen uns wieder direkt mittels OLE-DB in die Datenbank ein. Neu ist jetzt, dass wir uns diese Verbindung in einer sogenannten Sessionvariable merken. Das in ASP definierte Session-Objekt kann beliebige Variablen über eine gesamte Sitzung global speichern, insbesondere über mehrere ASP-Seiten hinweg. Dies geschieht wie folgt: // Die Datenbank ist geoeffnet. Die Verbindungsdaten werden gemerkt: Session("_conn") = conn; // Sessionvariable _conn Der Name der Sessionvariable ist im Rahmen eines erlaubten Javascript-Namens frei wählbar. Wir haben uns hier für den Namen _conn entschieden. Auf diese Variable werden wir später zurückgreifen. Die Kunden- und Teiledaten werden wie folgt eingelesen: // Jetzt werden Kunden und Teile gelesen: sqlstring = "Select Nr, Name From Kunde;"; suchliste1 = conn.Execute(sqlstring); // Kunden einlesen sqlstring = "Select Teilnr, Bezeichnung From Teilestamm;"; suchliste2 = conn.Execute(sqlstring); // Teiledaten einlesen Zur Ausgabe im Browser haben wir eine Tabelle gewählt, die in der rechten Spalte je eine Auswahlliste enthält. Betrachten wir nur die erste der beiden Auswahllisten. Die zweite ist analog aufgebaut: <td align=right>Bitte wählen Sie einen Kunden aus: </td> <td><!-- Select Box: --> <select name="Kundnr" size=1> <!-- size=1: Drop Down Box --> <% // Auslesen der Daten fuer die Select-Box in einer Schleife: while (!suchliste1.EOF) { %> <option value="<%=suchliste1("Nr")%>"><%=suchliste1("Name")%> <% suchliste1.MoveNext(); // naechsten Datensatz lesen } %> Multimedia Entwicklung (Teil2) Edwin Schicker 20 </select> </td> Die Auswahlliste erhalten wir mit dem Select-Tag. Der Parameter size=1 liefert eine Drop-Down-Box, wie wir sie in obigem Bild erkennen können. In einer Schleife lesen wir nun die erhaltenen Daten aus der Liste und geben diese gleich an die Auswahlliste weiter. Der Option-Tag besitzt den Parameter value. Hier kann man einen Wert angeben, der statt der Auswahl weitergegeben wird. Es ist in der Praxis meist nützlich, nicht den Kundennamen, sondern die Kundennummer weiterzureichen. Dies haben wir hier auch angewendet. Im Browserfenster erscheint also der Kundenname, weitergereicht wird jedoch die Kundennummer, genauer: der Wert, der im Value-Parameter angegeben wird. Mit der Auflistung der Teile gehen wir analog vor. Wir zeigen die Teile an, reichen jedoch die Teilenummer weiter. Ein Submit-Button mit dem Namen Weiter rundet unsere Ausgabe ab. Beim Klick auf diesen Button wird die Datei auswahlfeld2.asp aufgerufen. In dieser Datei werden die beiden übergebenen Parameter und die Sessionvariable ausgelesen. Es ist zu beachten, dass wir in der vorhergehenden Datei die Datenbank nicht geschlossen hatten. Wir brauchen diese daher nicht nochmals zu öffnen, sondern können sofort zugreifen: <% // Die Kundnr Teilnr // Die conn = Angaben in den Select Boxen werden ermittelt: = Request.Form("Kundnr"); = Request.Form("Teilnr"); Datenbankverbindungsdaten werden aus der Sessionvariable geholt: Session("_conn"); // Die Datenbank ist noch geoeffnet: sqlstring = "Select A.Auftrnr As Auftrag, Datum, Persnr, " + "Anzahl, Gesamtpreis " + "From Auftrag A, Auftragsposten AP " + "Where A.Auftrnr = AP.Auftrnr " + " And Kundnr = " + Kundnr + " And Teilenr = " + Teilnr + ";" // Wir verwenden jetzt einen Recordset zum Zugriff auf Datenbanken: recSet = Server.CreateObject ("ADODB.Recordset") // RecordSet recSet.Open(sqlstring, conn) Im Select-Befehl zum Auslesen der gewünschten Daten müssen wir beachten, dass wir Aliasnamen verwenden müssen, wenn wir im Select-Teil Ausdrücke verwenden. Auch die Qualifizierung einer Variable, hier A.Auftrnr, fällt darunter. Wir können nämlich aus der Recordliste nur Variablen auslesen, die der Syntax erlaubter Bezeichner genügen. Weiter greifen wir jetzt nicht mehr direkt mit dem Connection-Objekt auf die Datenbank zu, sondern erzeugen einen sogenannten Recordset. Das Öffnen dieses Recordsets mittels der Methode Open unter Angabe der Verbindung conn als zweiten Parameter führt jetzt zum Zugriff auf die Datenbank und entspricht der Execute-Methode des Connection-Objekts. Der Zugriff mittels Recordsets ist dahingehend aufwendiger, dass zunächst ein Recordset-Objekt mittels der Servermethode CreateObject erzeugt werden muss. Der Vorteil eines Recordsets gegenüber dem direkten Arbeiten mit dem Connection-Objekt liegt aber darin, dass wir den Cursor, der die Daten in die Recordliste ausliest, entsprechend unseren Wünschen einstellen können. Wir wollen hier keinen Gebrauch davon machen, einige Möglichkeiten aber aufzählen: Methode Update: Erlaubt den Update an der aktuellen Cursorposition. Dies entspricht in SQL dem Befehl Update … Where Current of … Property Cachesize: Die Cachegröße beeinflusst die Performance erheblich. Angegeben wird die Anzahl der Records, die im Cache gehalten werden sollen. Diese Zahl muss größer als Null sein. Property CursorLocation: Der Cursor kann auf dem Server (adUseServer) oder auf dem Client (adUseClient) gehalten werden. Ein Cursor auf dem Client erfordert, dass zunächst alle Daten vom Server auf den Client übertragen werden müssen. Beispielsweise liefert die Property Multimedia Entwicklung (Teil2) Edwin Schicker 21 RecordCount nur im Falle eines Client-Cursors die Anzahl der Records zurück (ein ServerCursor liefert hier den Wert –1). Property CursorType: Hier kann angegeben werden, wie sich der Cursor durch die Recordliste bewegen kann. Beispielsweise erlaubt der Typ adOpenForwardOnly nur ein Vorwärtsbewegen durch die Liste. Dies ist auch die Standardeinstellung. Property LockType: Beim Durchsuchen und eventuellem Update sind Lockmechanismen erforderlich, um einen korrekten Parallelbetrieb zu ermöglichen. Standardmäßig ist der Wert adLockReadOnly eingestellt. Es darf nur gelesen werden. Diese Beispiele sollten hinreichend erklären, warum Recordlisten den einfachen Connectionlisten vorzuziehen sind. Das Objekt Connect erlaubt aus der obigen Aufzählung nur die Property CursorLocation. Um obige Properties zu setzen, muss die entsprechende Zuweisung vor der Open-Methode erfolgen! Das Auslesen der Recordlisten entspricht in unseren Fällen dem Auslesen der Connectionlisten. Doch sehen wir selbst: // Ausgeben der gelesenen Daten: if (recSet.EOF) { %> Der Kunde <%=Kundnr%> hat das Teil <%=Teilnr%> noch nicht bestellt. <p> } else { <% %> Der Kunde <%=Kundnr%> hat Teil <%=Teilnr%> wie folgt bestellt: <p> <table border cellpadding=10> <tr> <!-- Kopfzeile definieren (mit <th> statt <td>: --> <th>Auftragsnr </th> <th>Auftragsdatum </th> <th>Verkäufernr </th> <th>Anzahl </th> <th>Gesamtpreist </th> </tr> <% while (!recSet.EOF) { // Auslesen der Daten: %> <tr> <!-- Die ausgelesenen Daten kommen gleich in die Tabelle: --> <td> <%= recSet("Auftrag") %> </td> <td> <%= recSet("Datum") %> </td> <td> <%= recSet("Persnr") %> </td> <td> <%= recSet("Anzahl") %> </td> <td> <%= recSet("Gesamtpreis") %> </td> </tr> <% recSet.MoveNext(); // naechsten Datensatz lesen } %> </table> <% } // Die Datenbank wird jetzt geschlossen: Session("_conn") = null; // Schliessen der Sessionvariable conn.Close(); } %> Multimedia Entwicklung (Teil2) Edwin Schicker 22 Aus programmtechnischen Gründen sollte mit dem Schließen der Datenbank das entsprechende Session-Objekt _conn auf Null gesetzt werden. Somit wird nicht versehentlich versucht, auf eine geschlossene Datenbank zuzugreifen. 4.6 Transaktionen Bisher haben wir nur lesend auf die Radl-Datenbank zugegriffen. Das Schreiben in eine Datenbank ist aber genauso einfach. Wir müssen dazu lediglich im SQL-String einen Schreibbefehl einfügen. In der Regel erfordert aber ein Arbeitsvorgang, etwa eine Buchung, mehrere Schreibzugriffe. Es muss hier sichergestellt werden, dass alle diese Schreibzugriffe auch vollständig ausgeführt werden. Sollte ein Schreibzugriff misslingen, so ist der gesamte Arbeitsvorgang zurückzusetzen. Einen solchen Arbeitsvorgang nennt man eine Transaktion. In einer Datenbank wird sichergestellt, dass Transaktionen immer vollständig ausgeführt werden. Im Fehlerfall werden alle bereits ausgeführten Änderungen wieder zurückgenommen. Gerade im ECommerce-Bereich und im Internet-Banking-Bereich ist dieser Transaktionsmechanismus zwingend erforderlich. Auch unser Zugriff auf Datenbanken über das Internet muss also Transaktionen voll unterstützen. Hier scheidet sich jetzt das Spreu vom Weizen. Einfache CGI-Programme können Transaktionen vom Prinzip her nicht unterstützen, da jede Aktion von der anderen unabhängig erfolgt. Nur durch komplexe Programmierung können hier einfache Transaktionen nachgebildet werden. Aus Sicherheitsgründen sollten wir aber keine Risiken eingehen und nur mit WWW-Servern arbeiten, die Datenbanktransaktionen mit eigenen Mechanismen zusätzlich unterstützen. Zusätzlich zu ASP und ADO bietet hier Microsoft den Microsoft Transaction Server (MTS) an. Die Möglichkeiten sind enorm. Wir wollen hier nur den Grundmechanismus im Zusammenspiel mit ASP und ADO kennenlernen. Wir benötigen genaugenommen zwei Transaktionsmechanismen, die Datenbanktransaktion und die Programmtransaktion. Beide müssen korrekt zusammenarbeiten. Datenbanktransaktionen unterstützt ADO, die entsprechenden Methoden des Connection-Objekts lauten: BeginTrans( ) CommitTrans( ) RollbackTrans( ) Die Methode BeginTrans ist nur in Datenbanken erforderlich, die nicht automatisch nach einem Commit die nächste Transaktion starten, wie etwa in MS-Access. Die Methode CommitTrans beendet eine Transaktion, die Methode RollbackTrans setzt die gesamte Transaktion zurück. Insbesondere bei einem auftretenden Fehler beim Datenbankzugriff oder im Programm muss die Methode RollbackTrans aufgerufen werden. Dies muss aber jetzt programmtechnisch gelöst werden. Hier kommt MTS ins Spiel. Wir müssen dazu nur die erste Zeile unseres ASP-Programms erweitern: <%@ TRANSACTION=Required LANGUAGE = "JScript" %> Zusätzlich zu unserer Sprachangabe schalten wir auch den Transaktionsmechanismus ein. Im ASPProgramm ändert sich erfreulicherweise sehr wenig. Zum Einen benutzen wir jetzt unsere obigen Transaktionsbefehle beim Zugriff auf die Datenbank. Zum Anderen ruft MTS nach dem Ablauf des ASP-Programms eine der folgenden Funktionen auf (falls definiert): OnTransactionCommit() OnTransactionAbort() // Erfolgreiches Beenden des ASP-Programms // Fehlerhaftes Beenden des ASP-Programms Wurde das ASP-Programm korrekt ausgeführt, so wird am Ende des Programms automatisch die Funktion OnTransactionCommit aufgerufen. Wir definieren daher diese Funktion und schreiben hier auch den Datenbank-Commit-Befehl. Stößt das ASP-Programm während des Arbeitens auf einen Fehler, so wird das Programm verlassen und die Funktion OnTransactionAbort aufgerufen. Hier ist jetzt der ideale Platz, um den DatenbankRollback-Befehl auszuführen. Multimedia Entwicklung (Teil2) Edwin Schicker 23 Es sei dringend darauf hingewiesen, dass MTS genau darauf achtet, dass sich der Programmierer an diesen Mechanismus hält. Aus diesem Grund sind in MTS die Methoden CommitTrans und RollbackTrans im normalen Programmcode nicht erlaubt. Sie dürfen nur innerhalb der beiden oben erwähnten Funktionen geschrieben werden! Wieder wollen wir an Hand eines Beispieles diesen Mechanismus verstehen lernen. Wir wollen dazu einen neuen Auftrag in die Radl-Datenbank aufnehmen. Einfachheitshalber gehen wir davon aus, dass höchstens zwei Auftragsposten je Auftrag existieren. Im Programm transaktion1.asp sind die notwendigen Daten einzugeben. Eine neue Auftragsnummer und das aktuelle Datum werden automatisch vorgegeben. Die Namen der Kunden und Mitarbeiter werden in einer Drop-Down-Box angeboten. Bei den beiden Auftragsposten wird die Positionsnummer ebenfalls vorgegeben, ebenso die Teilenamen in einer Drop-Down-Box. Betrachten wir die Oberfläche: Diese Oberfläche enthält fast keine neuen Elemente. Wir haben Drop-Down-Boxen und Eingabefelder bereits kennengelernt. Die neue Auftragsnummer wird ermittelt, indem die bisherige maximale Auftragsnummer aus der Datenbank gelesen und um eins erhöht wird. Neu ist das Ermitteln des aktuellen Datums und das Weiterreichen der Auftragsnummer und des Datums an die nächste ASP-Seite. Das aktuelle Datum wird in ASP direkt mit der Methode new geliefert. Ein Problem ist, dass dieses Datum auch die Uhrzeit enthält. Das Datum kann nicht direkt von der Uhrzeit getrennt werden. Wir müssen stattdessen mit den entsprechenden Methoden den Tag, das Monat und das Jahr auslesen. Leider wird das Monat von 0 bis 11 gezählt. Sind diese Fallen aber alle bekannt, so löst folgender Code unsere Probleme: <% // Heutiges Datum ermitteln: datum = new Date(); Tag = datum.getDate(); Monat = datum.getMonth() + 1; Jahr = datum.getYear(); datum = Tag + "." + Monat + "." // enthaelt auch die Uhrzeit! // aktueller Tag // Monatsangabe beginnt bei 0! // aktuelles Jahr + Jahr; %> Multimedia Entwicklung (Teil2) Edwin Schicker 24 Wir haben nun das Datum und mit einem Datenbankzugriff auch die neue Auftragsnummer ermittelt und ausgegeben. Da diese Daten in keinem Formularfeld stehen, werden diese jedoch nicht an die nächste Seite weitergereicht. Um dort diese Daten nicht noch einmal berechnen zu müssen, wurden sogenannte versteckte Eingabefelder verwendet: <td><%=suchliste3("Nr")%> <!-- enthaelt neue Auftragsnummer --> <!-- Die Auftragsnummer muss weitergegeben werden. Daher wird ein verstecktes Eingabefeld verwendet! --> <input type="hidden" NAME="AuftrNr" VALUE="<%= suchliste3("Nr") %>" > </td> Wir wenden uns jetzt der Datei transaktion2.asp zu, die vom obigen Programm aufgerufen wird. In der ersten Zeile ist der Transaktionsbetrieb vermerkt. Weiter wird eine Variable ta eingeführt, die sich merkt, ob eine Datenbanktransaktion gestartet wurde. Betrachten wir den Beginn des Programms, wo alle notwendigen Daten eingelesen werden, die Datenbankverbindung überprüft und die Transaktion begonnen wird. <% ta = 0; // Transaktions-Variable // Die übergebenen Angaben werden ermittelt: Auftrnr = Request.Form("AuftrNr"); Datum = Request.Form("Datum"); Kundnr = Request.Form("Kundnr"); Persnr = Request.Form("Persnr"); Teilnr1 = Request.Form("Teilnr1"); Anz1 = Request.Form("Anzahl1"); Teilnr2 = Request.Form("Teilnr2"); Anz2 = Request.Form("Anzahl2"); // Die Datenbankverbindung wird ueberprueft: if (Session ("_conn") == null || (Anz1==0 && Anz2==0)) { %> Fehler! Die DB-Verbindung wurde unterbrochen oder die Variable Anzahl wurde nicht gesetzt. Bitte geben Sie die Daten nochmals ein. <p><center><a href="transaktion1.asp">Zurück</a></center></p> <% } else { conn = Session("_conn"); // Die Datenbank ist noch geoeffnet. //In Access muss der TA-Beginn explizit gesetzt werden: conn.BeginTrans(); ta = 1; // zeigt an, dass Transaktion gestartet wurde // Neuen Auftrag einfuegen: sqlstring = "Insert Into Auftrag(Auftrnr, Datum, Kundnr, Persnr) " + "Values(" + Auftrnr + ",'" + Datum +"'," + Kundnr + "," + Persnr + ");"; conn.Execute(sqlstring); // Befehl ausführen Wir erkennen, dass wieder die Sessionvariable _conn überprüft wird, die natürlich in transaktion1.asp gesetzt wurde. Die Transaktion wird gestartet, die Variable ta wird auf den Wert 1 gesetzt. In die Variable sqlstring schreiben wir den Insert-Befehl und führen ihn mit der Execute-Methode aus. Die anderen Schreibbefehle in die Relation Auftragsposten sind analog. Allerdings taucht dort eine kleine Falle auf. Die Positionsnummer setzt sich nämlich zusammen aus der Auftragsnummer plus einem fortlaufenden Zähler. Wollen wir mehr als 10 Auftragsposten pro Auftrag unterstützen, so wäre folgender Code innerhalb des Insert-Befehls für den ersten Auftragsposten denkbar: "Values(" + Auftrnr + "," + (100*Auftrnr+1) +"," + Teilnr1 + ... Multimedia Entwicklung (Teil2) Edwin Schicker 25 Wir multiplizieren also die Auftragsnummer mit dem Wert 100 und addieren dazu die Zahl 1. Die Falle besteht jetzt darin, dass die Klammern zwingend erforderlich sind, da Javascript den Plus-Operator sonst als Konkatenierungsoperator interpretiert! Wurden die Schreibbefehle nun alle korrekt ausgeführt, so wird am Ende des Programms die Funktion OnTransactionCommit aufgerufen. Diese Funktion haben wir am Ende des Programms (nach dem Tag </html>) hinzugefügt: <% function OnTransactionCommit() // MTS-Transaktion war erfolgreich { if (ta == 1) // Transaktion war gestartet, also beenden: { conn.CommitTrans(); // Daten werden endgueltig gespeichert Response.Write ("<p>Die Bestelldaten wurden aufgenommen."); } conn.Close(); // Verbindung schliessen Session("_conn") = null; %> <p><center><a href="default.htm">Zurück zur Startseite</a></center></p> <% } Im Wesentlichen haben wir in dieser Funktion nur die Methode CommitTrans aufgerufen und damit die Transaktion beendet. Da wir nicht mehr auf die Datenbank zugreifen müssen, haben wir auch die Verbindung geschlossen. Sollte im Programm bei einem der Schreibbefehle ein Fehler aufgetreten sein, so wird MTS das Programm sofort beenden und die Funktion OnTransactionAbort aufrufen. Natürlich haben wir am Programmende deshalb auch diese Funktion definiert: function OnTransactionAbort() // MTS-Transaktion endet mit Fehler { if (ta == 1) // Transaktion war gestartet: { conn.RollbackTrans() // Daten werden zurueckgesetzt Response.Write ("<p>Die Bestelldaten wurden nicht gespeichert."); } conn.Close(); // Verbindung schliessen Session("_conn") = null; %> <p> <p><center><a href="default.htm">Zurück zur Startseite</a></center></p> <% } Diese Funktion unterscheidet sich von der vorherigen nur dadurch, dass die Methode RollbackTrans aufgerufen wird. Eine begonnene Transaktion wird damit vollständig zurückgesetzt. 4.7 Weitere Möglichkeiten Wir können mit dem bisherigen Wissen bereits sehr komplexe Datenbankzugriffe durchführen. Mit der Zeit werden wir aber bemerken, dass einige Zusatzmöglichkeiten die Programmierung noch effizienter, die Performance noch besser werden lässt. Hier sei nur stichpunktartig auf einige Möglichkeiten verwiesen: Command-Objekt: Das Command-Objekt ist ein ADO-Objekt und ermöglicht noch mehr Möglichkeiten als das Recordset-Objekt. Insbesondere bei mehreren sehr ähnlichen Datenbankzugriffen zeigen Command-Objekte Vorteile, da hier mit Variablen gearbeitet werden kann, die vor jedem Befehl entsprechend gesetzt werden können. Diese Command-Objekte sollen bei komplexen Zugriffen performanter sein als Recordsets. Multimedia Entwicklung (Teil2) Edwin Schicker 26 MTS: Microsoft Transaction Server ermöglichen ein fast beliebiges Transaktionshandling. Natürlich können Transaktionen auch über mehrere ASP-Seiten hinweg erfolgen. Hier empfiehlt sich das Lesen der entsprechenden Online-Hilfe. Debugging: Werden die Programme komplexer, so werden wir ohne entsprechende Debug-Unterstützung kaum mehr auskommen. Auch diese Unterstützung wird in ASP geboten. 4.8 Übungsaufgaben 1. Geben Sie alle Teile der Relation Teilestamm aus, insbesondere Teilnr, Bezeichnung, Preis, Maß und Einheit. 2. Lesen Sie eine Zahl ein. Geben Sie dann alle Teile aus, deren Bestand unter diese Zahl gesunken ist. Geben Sie mindestens Teilenr, Bezeichnung und Bestand aus. 3. In einer Drop-Down-Box werden alle Teile angezeigt. Wählen Sie ein Teil aus. Alle Daten zu diesem Teil werden angezeigt (aus Relation Teilestamm und Lager). 4. Fügen Sie ein neues Teil in die Datenbank hinzu. Aktualisieren Sie die Relationen Teilestamm und Lager! 5 Überblick über weitere Möglichkeiten An dieser Stelle sollen einige weiteren Möglichkeiten in ADO/ASP aufgezeigt werden. Zum Einen wird das Application-Objekt vorgestellt, sozusagen ein Gegenstück zum Session-Objekt. Weiter wird kurz auf die Datei Global.asa eingegangen. Hier können einige zentrale Daten und Ereignisse abgelegt bzw. ausgeführt werden. Zuletzt seien noch einige weitere Möglichkeiten zum Zugriff auf Datenbanken aufgezeigt, etwa der Zugriff mittels des Command-Objekts. 5.1 Das Application-Objekt Wir haben bereits das Session-Objekt kennengelernt. Dieses ermöglicht das Anlegen von Daten über die gesamte Sitzung. Die Daten werden benutzerspezifisch verwaltet, so dass nur der augenblicklich mit dem WWW-Server verbundene eindeutige Benutzer auf diese Daten zugreifen kann. Wir haben mit dem Befehl Session("_conn") = conn; die aktuell hergestellte Verbindung in der Session-Variable _conn hinterlegt. Analog können wir auch definieren: Application("zaehler") = 0; Diese Variable zaehler ist aber jetzt nicht für einen Benutzer sichtbar, sondern für alle Benutzer, die auf diese Seite zugreifen. Eine Application-Variable ist also so lange gültig, so lange eine Seite vom WWW-Server angeboten wird und dieser Server aktiv ist. Diese Variable ist für jeden zugreifenden Benutzer sichtbar. Wollen wir also wissen, wie oft auf eine Seite zugegriffen wurde, so können wir eine entsprechende Variable definieren und diese bei jedem Seitenzugriff um den Wert 1 erhöhen. Das Problem ist jetzt allerdings, dass womöglich zwei Benutzer gleichzeitig die Seite starten. Um Synchronisationsprobleme lösen zu können, besitzt das Application-Objekt die Methoden Lock( ) und Unlock( ). Betrachten wir ein Beispiel: Wir wollen wissen, wie viele Benutzer die aktuelle Seite lesen, und wir wollen wissen, wie viele Datenbankzugriffe insgesamt von dieser Seite aus erfolgen. Wir definieren daher die beiden Application-Variablen zaehler und db_zaehler. Das Setzen auf den Wert 0 zu Beginn erfolgt dabei beispielsweise wie folgt: <% if (Application("zaehler") == null) Application("zaehler") = 0; Multimedia Entwicklung (Teil2) Edwin Schicker 27 if (Application("db_zaehler") == null) Application("db_zaehler") = 0; %> Wir überprüfen also zunächst, ob die beiden Variablen bereits existieren, wenn nicht so werden sie auf den Wert 0 gesetzt. Dies geschieht daher in einer Anwendung nur ein einziges Mal. An der gewünschten Stelle im Programm können wir jetzt einen der Zähler erhöhen. Betrachten wir dies am Beispiel der Variablen zaehler. <% Application.Lock(); Application("zaehler") = Application("zaehler") + 1; Application.Unlock(); %> Wir erkennen, dass wir das Manipulieren des Zählers in einem durch einen zentralen Lock geschützten Bereich durchführen. Dadurch wird sichergestellt, dass die Erhöhung des Zählers immer korrekt erfolgt, unabhängig von etwaigen anderen parallel zugreifenden Benutzern. Die Ausgabe eines solchen Zählers könnte wie folgt aussehen: Auf diese Seite wurde bisher <% =Application("zaehler") %>-mal zugegriffen. Diese Vorgehensweise ist so möglich, hat aber den Nachteil, dass dadurch der Code unnötigerweise vergrößert wird. In der Praxis steht sowieso meist zu viel Code in einer Seite. Weiter wird bei jedem Zugriff überprüft, ob die Application-Variablen bereits existieren. Hier kann die Datei Global.asa eine große Hilfe sein. 5.2 Die Datei Global.asa Eine komplette ADO/ASP-Anwendung kann aus vielen HTML- und ASP-Seiten bestehen, eventuell noch über mehrere Unterverzeichnisse verteilt. Jede solche Anwendung darf im Hauptverzeichnis genau eine Datei Global.asa enthalten. Diese Datei Global.asa kann globale Informationen aufnehmen, etwa wie die Anwendung beim Hochfahren und Herunterfahren des WWW-Servers reagieren soll. Weiter können hier globale Objekte und Typen definiert werden. Mit Hilfe dieser Datei können wir das im letzten Abschnitt behandelte Zählen der Benutzer aus der ASP-Datei auslagern. Betrachten wir eine komplette Datei Global.asa: <SCRIPT LANGUAGE="Jscript" RUNAT="Server"> function Application_OnStart() { Application("zaehler") = 0; Application("db_zaehler") = 0; } function Application_OnEnd() { } function Session_OnStart() { Application.Lock(); Application("zaehler") = Application("zaehler") + 1; Application.Unlock(); } function Session_OnEnd() { } </SCRIPT> Hier wurden 4 Funktionen definiert. Die ersten Beiden werden beim Hochfahren und Herunterfahren des Servers aufgerufen, die letzten beiden beim Zugriff eines neuen Benutzers und beim Beenden des Benutzers (meist nach einer abgelaufenen Zeitspanne von 20 Minuten nach dem letzten Zugriff; diese Zeitspanne ist einstellbar: Session.Timeout( ) ). Multimedia Entwicklung (Teil2) Edwin Schicker 28 Wir haben hier beim Hochfahren des Servers unsere Variablen initiiert und beim Start eines neuen Benutzers wird der Zähler um den Wert 1 erhöht. Dieses Hochsetzen erfolgt immer dann, wenn ein neuer Benutzer auf die Anwendung zugreift. Es ist dabei nicht notwendig, dass der Benutzer die Startseite öffnet, der Zugriff auf eine beliebige Seite der Anwendung genügt. Ein mehrmaliger Zugriff auf eine der Seiten der Anwendung innerhalb einer Session führt dabei nicht nochmals zum Fortschalten des Zählers! 5.3 Das Command-Objekt in ADO Es gibt zahlreiche Anwendungen, wo mehrere ähnliche SQL-Anforderungen nacheinander an den DBServer geschickt werden. Unterscheiden sich diese SQL-Befehle nur durch ein, zwei oder drei Angaben, so können wir sehr geschickt das Command-Objekt einsetzen. Betrachten wir dazu ein Beispiel: Wir wollen das Gehalt der Mitarbeiter erhöhen. Wir geben dazu die Namen der Mitarbeiter in einer Select-Box aus. Die gewünschten Mitarbeiter können nun ausgewählt werden. Bei allen angegebenen Mitarbeitern wird dann das Gehalt um 2% erhöht. Wir benötigen für diese Aufgabe zwei ASP-Dateien. In der ersten Datei namens command1.asp wird die Datenbank geöffnet und die Mitarbeiterdaten werden in einer Select-Box ausgegeben. Beim Klick auf den Button für die Gehaltserhöhung wird die zweite Datei namens command2.asp aufgerufen. Die erste Datei bereitet keine Probleme, der Stoff wurde bereits weiter oben behandelt. Als kleine Wiederholung wird der Aufbau der Select-Box trotzdem wiedergegeben: <form action="command2.asp" method="post"> <!-- Select Box: --> <select name="Persnr" size=6 multiple> <!-- 6er-Box, Mehrfachauswahl --> <% // In einer Schleife die Daten fuer die Select Box auslesen: while (!suchliste.EOF) { %> <option value="<%=suchliste("Persnr")%>"><%=suchliste("Name")%> <% suchliste.MoveNext(); // naechsten Datensatz lesen } %> </select> <p> <input type="Submit" value="Gehaltserhöhung!"> </form> Wir erkennen hier eine Select-Box mit Mehrfachauswahl, in der die Mitarbeiternamen ausgegeben, deren Personalnummern aber an die Datei command2.asp weitergereicht werden. Die Datei command.asp muss nun die übergebenen Daten verarbeiten. Optimal wäre das Abschicken eines einzigen Update-Befehls. Dies ist allerdings ohne dynamisches SQL kaum möglich, da ja von vorneherein nicht bekannt ist, welche und wie viele Mitarbeiter eine Gehaltserhöhung erhalten. Wir behelfen uns daher damit, je ausgewähltem Mitarbeiter einen Update-Befehl zu schreiben. Hier hilft uns jetzt das Command-Objekt weiter, da sich die einzelnen Befehle nur durch unterschiedliche Personalnummern unterscheiden. Betrachten wir den Aufbau dieses Update-Befehls mittels des CommandObjekts: comm = Server.CreateObject ("ADODB.Command") comm.ActiveConnection = conn // Command-Objekt erzeugen // Verbinden mit Connection // SQL-Text, Fragezeichen ist Platzhalter: comm.CommandText = "Update Personal Set Gehalt = Gehalt *1.02 " + "Where Persnr = ?;" // Platzhalter wird definiert: comm.Parameters.Append( comm.CreateParameter("Pnr", 3) ) // 3 = 4 Byte-Integer, Konstanten sind in adojavas.inc definiert Multimedia Entwicklung (Teil2) Edwin Schicker 29 // ausgewaehlten Mitarbeiter als Parameter uebernehmen: for (i=1; i <= Request.Form("Persnr").Count; i++) { comm("PNr") = Request.Form("Persnr")(i) // Wert -> Platzhalter comm.Execute() // Update ausfuehren } Wir definierten hier zunächst ein neues Command-Objekt und ordnen dieses der aktuellen DB-Verbindung zu, also der Connection-Variable conn. Wir schreiben den Update-Befehl und setzen statt einer Personalnummer ein Fragezeichen. Je Fragezeichen müssen wir jetzt einen Parameter erzeugen (mittels CreateParameter) und diesen der Parameterliste mittels der Methode Append hinzufügen. Jeder Parameter wird durch einen Namen identifiziert und besitzt einen Datentyp. Die Datentypen sind in der Datei adojavas.inc definiert. Einige Beispielwerte: Datentyp adInteger adChar adDate adSmallint adSingle adDouble Wert 3 129 7 2 4 5 Info 32Bit-Integerwert Zeichenkette Datum 16Bit-Integerwert 32Bit-Gleitpunktzahl 64Bit-Gleitpunktzahl Soll der Datentyp verwendet werden, so muss die Datei adojavas.inc inkludiert werden: <!--#include File="adojavas.inc"--> Im Falle von Zeichenketten muss in der Methode CreateParameter zusätzlich die Zeichenkettenlänge mit übergeben werden, etwa comm.CreateParameter ("Ort", 129, 1, 30); Hier wird ein Zeichenkettenparameter mit Namen Ort als Zeichenkette mit der Länge 30 definiert, oder ausführlich: comm.CreateParameter ("Ort", adChar, adParamInput, 30); adParamInput heißt, dass der Wert des Parameters eingegeben wird, besitzt den Wert 1 und ist die Standardvorgabe. In unserem obigen Beispiel wird jetzt in einer Schleife jeder Mitarbeiter ausgelesen, dem CommandParameter zugewiesen und der Update-Befehl ausgeführt. 5.4 Die Methode Redirect des Session-Objekts Arbeiten wir mit MTS, so wird am Ende einer HTML-Seite immer noch Code abgearbeitet, entweder der Code in der Funktion OnTransactionCommit oder in OnTransactionAbort. Hier können noch Aufräumungsarbeiten geleistet werden, etwa das Schließen der noch geöffneten Datenbank. In der Datei command1.asp wird ebenfalls die Datenbank geöffnet. In der Regel wird durch das Drücken des Submit-Buttons zur Datei command2.asp gewechselt, die eine geöffnete Datenbank erwartet. Es wäre also unklug, die Datenbank vorher zu schließen. Andererseits können wir aber mit einem Hyperlink auf die Startseite zurückspringen. In diesem Fall wäre aber die Datenbank noch offen, das Ausführen der Datenbankbeispiele würde dann aber scheitern, da jeweils eine bereits geschlossene Datenbank erwartet wird. Wir können uns damit behelfen, dass wir nicht direkt zu der Startseite zurückspringen, sondern eine Hilfsseite aufrufen, die die Datenbank schließt und dann von sich aus zur Startseite wechselt. Soll dies Multimedia Entwicklung (Teil2) Edwin Schicker 30 automatisch funktionieren, also ohne erneuten Klick auf einen Button oder einen Link, so ist es zweckmäßig, die Methode Redirect des Response-Objekts einzusetzen. Betrachten wir dies wieder am Beispiel. Statt zur Startseite zurückzuspringen, rufen wir die Seite ende.asp aus. Erst hier wird zur Startseite gewechselt. Betrachten wir den dazugehörigen Code: if (Session ("_conn") != null ) { conn = Session("_conn") Session("_conn") = null conn.Close() Session.Abandon () } Response.Redirect("default.htm") // Verbindung existiert noch // Explizites Schließen der DB // Sessionobjekte freigeben // Umlenken auf die Startseite Dieses Beispiel besitzt eine kleine Falle. Die Methode Redirect funktioniert nur dann einwandfrei, wenn noch keine Daten der aktuellen Seite zum Benutzer gesendet wurden, da sonst meist der HTMLAufbau gestört wird. Versuchen wir dies zu verstehen. Die hier angegebene Datei ende.asp enthält auch normalen HTML-Code, also Html- und Body-Tags. Sind diese bereits beim Browser des Benutzers und erfolgt jetzt ein Redirect-Aufruf, so werden die bisherigen Daten nicht gelöscht, vielmehr werden jetzt die Daten der angegebenen Seite noch zusätzlich übertragen. Da ja der Tag </body> von dieser Seite noch nicht übertragen wurde, kommt dann meist der Browser erheblich durcheinander, auch weil nochmals ein Html-Header übertragen wird. Für einen störungsfreien Betrieb sei daher dringend empfohlen, die Datenpufferung einzuschalten. In diesem Fall werden vom Server Daten erst dann an den Benutzer übertragen, wenn die Flush- oder End-Methode des Response-Objekts aufgerufen wurde. Wir schreiben daher am Anfang unserer Datei noch vor den Html-Tag: Response.Buffer = True Jetzt ist sichergestellt, dass die Daten dieser Seite nicht zum Benutzer gelangen. Erst durch die Methode Redirect werden dann die Daten der Datei default.htm an den Benutzer geschickt, da in dieser Datei die Pufferung nicht gesetzt ist (standardmäßig ist die Pufferung ausgeschaltet). 5.5 Providerangaben Bisher haben wir ausschließlich auf Microsoft Access-Datenbanken zugegriffen. Natürlich können wir mittels ADO und OLE-DB auch auf andere Datenbanken zugreifen, insbesondere auf SQL-Server oder Oracle. Wir können zum Einen ODBC verwenden und dort die entsprechenden Datenbanken einschließlich Treiber auswählen. Es geht aber auch ohne ODBC. Bisher haben wir folgende Providerangabe gesetzt: conn.Provider = "Microsoft.Jet.OLEDB.4.0" // Access-Treiber Wir könnten aber auch schreiben: conn.Provider = "MSDASQL" conn.Provider = "SQLOLEDB" conn.Provider = "MSDAORA" Multimedia Entwicklung (Teil2) // Verbindung über ODBC, Standardvorgabe // Verbindung zu SQL-Server // Verbindung zu Oracle Edwin Schicker