Datenbanken
xml2html mit MySQL und Postgres einsetzen

Einleitung

Die dbc-Erweiterung des xml2html-Paketes stellt die Verknüpfung zwischen xml-Daten und relationalen Datenbanken auf eine einfache Art und Weise zur Verfügung.

Der Zugriff auf die relationale Datenbank wird mittels einiger einfacher xml-Tags realisiert, mit denen wiederum xml-Code generiert wird. Mit dieser Methode lassen sich einfache und komplexe Datenbankabfragen realiseren und sogar Operationen zum Einfügen, Ändern und Löschen einzelner Datensätze sind so möglich.

Im folgenden wird eine gewisse Grundkenntniss von relationalen Datenbanken, SQL und xml2html vorausgesetzt

Funktionsweise

Architektur

Das DBC Modul besteht aus zwei Schichten. Einer abstrakten Schicht, die alle Tags definiert, welche im xml benutzt werden, und einer datenbank- bzw. datenbanktreiberspezifischen Schicht, die von der abstrakten Schicht genutzt werden, um die Funktionalität der Tags in die Datenbank zu übertragen.

Installation

Um dbc in einer xml-Seite nutzen zu können, wird eine style-Datei angelegt, welche die für den Zugriff benötigten Informationen enthält. Diese style-Datei besteht aus zwei Teilen. Der erste Teil sorgt für das Einbinden der Module die für den Datenbankzugriff benötigt werden.

<tcl source="dbc.tcl">         <-- Das allgemeine dbc-Modul -->
<tcl source="dbc-pgsql.tcl">   <-- Das datenbankspezifische Treibermodul -->

Der zweite Teil setzt die für den Datenbankzugriff benötigeten Variablen:

<set var="DBUSER" value="dbc">
<set var="DBNAME" value="dbc">
<set var="DBHOST" value="localhost">
<set var="DBPASS" value="">

Sie setzen den Datenbank-Host, den Namen der Datenbank sowie Usernamen und Passwort.

Auf jeder xml-Seite, die auf diese Datenbank zugreift muß nurnoch diese style-Datei importiert werden, und die Datenbankverbindung kann hergestellt werden.

Die Datenbankverbindung

Um die Daten aus der Datenbank nutzen zu können, muß erst eine Verbindung zu Datenbank geöffnet werden. Dazu wird der Tag <dbcopen>benutzt.

dbcopen

Dieser Tag öffnet eine Verbindung zu der Datenbank, die mit den Variablen DBHOST, DBNAME, DBUSER und DBPASS spezifiziert worden ist. Datenbankuser ist dabei DBUSER mit dem Passwort DBPASS.

Tag-Typ : einfacher Tag
Attribute : - keine -
Sub-Tags : - keine -

dbcclose

Dieser Tag schließt eine bestehende Datenbankverbindung.

Tag-Typ : einfacher Tag
Attribute : - keine -
Sub-Tags : - keine -

Arbeiten mit der Datenbank

Für das Arbeiten mit der Datenbank werden folgende Tags benötigt.

dbcselect

Dieser Tag ermöglicht es, eine Datenbankabfrage durchzuführen und deren Inhalt in Zeilenform in das xml-Dokuement zu schreiben.

Der Aufbau des resultierenden xml-Fragmentes hat folgenden Aufbau: [<DBC_RESULT_BEGIN>-Inhalt][<DBC_ROW_CONTENT>-Inhalt][<DBC_RESULT_END>-Inhalt]

Tag-Typ : umschließender Tag
Attribute :
Name Pflicht Zweck
COLS : Ja Aufzählung der zu selektierenden Spalten.
TABLES : Ja Aufzählung der zu selektierenden Tablellen.
CONDITION : Nein Bedingung für den Datenbank-Select.
ERROR : Nein Name des Tag, das bei einer Fehlersituation erzeugt werden soll.
Sub-Tags :
Name Pflicht Zweck
dbc_result_begin : Nein Content, der vor das Ergebnis des DB-Selectes gestellt wird.
dbc_result_end : Nein Content, der an das Ergebnis des DB-Selectes gehängt wird.
dbc_row_content : Ja Eine Schablone für den Text, der aus einer Ergebniszeile erzeugt werden soll.
dbc_condition : Nein Bedingung für den Datenbank-Select.

dbcinsert

Mit diesem Tag werden Daten in die Datenbank eingefügt. Die dazu notwendigen Daten werden per dbc_column-Tag spezifiziert.

Tag-Typ : umschließender Tag
Attribute :
Name Pflicht Zweck
TABLE : Ja Aufzählung der zu selektierenden Tablellen.
RESULT : Nein Name des Tag, der bei erfolgreichem einfügen erzeugt werden soll.
ERROR : Nein Name des Tag, welches bei einer Fehlersituation erzeugt werden soll.
Sub-Tags :
Name Pflicht Zweck
dbc_column : Nein Daten für eine Spalte des Datensatzes, welcher in die Datenbank eingefügt werden soll.

dbcupdate

Mit diesem Tag werden Daten in die Datenbank eingefügt. Die dazu notwendigen Daten werden per dbc_column-Tag spezifiziert.

Tag-Typ : umschließender Tag
Attribute :
Name Pflicht Zweck
TABLE : Ja Aufzählung der zu selektierenden Tablellen.
CONDITION : Nein Bedingung welche die zu ändernden Datensätze spezifiziert.
RESULT : Nein Name des Tag, der bei erfolgreichem einfügen erzeugt werden soll.
ERROR : Nein Name des Tag, das bei einer Fehlersituation erzeugt werden soll.
Sub-Tags :
Name Pflicht Zweck
dbc_column : Nein Daten für eine Spalte des Datensatzes, der in die Datenbank eingefügt werden soll.
dbc_condition : Nein Bedingung, die die zu ändernden Datensätze spezifiziert.

dbcdelete

Dieses Tag realisiert eine Datenbank delete-Operation. Der auf die Bedingung passende Datensatz wird gelöscht.

Tag-Typ : umschließender Tag
Attribute :
Name Pflicht Zweck
TABLES : Ja Aufzählung der zu selektierenden Tablellen.
CONDITION : Nein Bedingung für den Datenbank-Select.
ERROR : Nein Name des Tag, welches bei einer Fehlersituation erzeugt werden soll.
Sub-Tags :
Name Pflicht Zweck
dbc_condition : Nein Bedingung für den Datenbank-Select.

Zusätzliche Tags

Um die Arbeit mit den Tags, die für die Datenmanipulation benutzt werden, zu erleichtern oder überhaupt erst zu ermöglichen, gibt es einige Spezial-Tags. Diese Spezial-Tags beeinfulßen in einigen Fällen Tags, von denen sie umschlossen werden.

dbc_condition

Dieser Tag legt eine Bedingung für die Datenbankoperation fest. Dies kann einer klassischen SQL whereoder order by-Bedingung entsprechen.

Tag-Typ : umschließender Tag
Beinflusst : dbc_select, dbc_update, dbc_delete

dbc_result_begin

Dieses Subtag enthät ein xml-Fragment, das dem Ergebnis aus der Datenbankabfrage voran gestellt wird.

Tag-Typ : umschließender Tag
Beinflusst : dbc_select

dbc_result_end

Dieses Subtag enthät ein xml-Fragment, das an die Daten aus der Datenbankabfrage angehängt wird.

Tag-Typ : umschließender Tag
Beinflusst : dbc_select

dbc_row_content

Dieses Subtag enthät eine xml-Vorlage, aus der dann ein xml-Fragment generiert wird, das mit den Werten der aktuellen Zeile der Datenbankabfrage gefüllt wird. Um den aktuellen Wert einer bestimmten Datenbankspalte in das xml zu transportieren, werden folgende Muster ersetzt

    $COLUMN(<Spalten-Name>)
oder
    $COLUMN(<Spalten-Nummer>)

So wird zum Beispiel: Postleitzahl : $CONTENT(plz)zu Postleitzahl : 53628(Beipiel!). Wenn die Postleitzahl an erster Stelle selektiert wurde, dann tut Postleitzahl : $CONTENT(1)dasselbe.

Tag-Typ : umschließender Tag
Beinflusst : dbc_select

dbc_column

Dieses Subtag Daten für eine Spalte des Datensatzes, der in eine Tabelle eingfügt werden soll.

Als Typ des Datums kann zur Zeit zwischen Text und Date oder keiner Angabe gewählt werden. Wird keine Angabe gemacht, wird davon ausgegangen, daß es sich im einen numerischen Datentyp handelt und dementsprechend in die Datenbank eingefügt. Wird Text angegeben, werden automatisch alle nötigen Zeichen maskiert und das Datum als Text in die Datenbank eingefügt. Wird Date als Typ gewählt, so wird versucht, ein TCL-Datum aus der Angabe zu erstellen. Dieses wird gemäß den SQL Datums-Normen in die Datenbank eingefügt.

Tag-Typ : umschließender Tag
Attribute :
Name Pflicht Zweck
NAME : Ja Name der Spalte, in die das Datum eingefügt werden soll.
TYPE : Nein Typ des Datums.
Beinflusst : dbc_insert, dbc_update

Beispiele

Ein paar einfache selects

Zu Beginn werden wir uns mit den einfachen, aber häufig genutzen Datenbankfunktionen vertraut machen. Also beginnen wir mit ein paar select-statements...

Eine erste Zusammenfassung von Studenten

 <DBCSELECT cols="name,fachbereich" tables="praktika">
   <DBC_RESULT_BEGIN><table border=0 cellpadding=5 cellspacing=0></DBC_RESULT_BEGIN>
   <DBC_ROW_CONTENT><tr><td>$COLUMN(name)</td><td>$COLUMN(2)</td></tr></DBC_ROW_CONTENT>
   <DBC_CONDITION>order by fachbereich</DBC_CONDITION>
   <DBC_RESULT_END></table></DBC_RESULT_END>
  </DBCSELECT>

Das Ergebnis wird in etwa so aussehen

 <table border=0 cellpadding=5 cellspacing=0>
 <tr><td>Meier, Niclas</td><td>MI</td></tr>

 [...]

 </table>

Nun Versuchen wir es mal mit der Suche nach einem Studenten mit einem bestimmten Namen ...

 <DBCSELECT cols="name,fachbereich" tables="praktika">
   <DBC_RESULT_BEGIN><table border=0 cellpadding=5 cellspacing=0></DBC_RESULT_BEGIN>
   <DBC_ROW_CONTENT><tr><td>$COLUMN(name)</td><td>$COLUMN(2)</td></tr></DBC_ROW_CONTENT>
   <DBC_CONDITION>where name like '%Niclas%'</DBC_CONDITION>
   <DBC_RESULT_END></table></DBC_RESULT_END>
  </DBCSELECT>

Um alle Nicoläuse zu finden ... das Ergebnis:

 <table border=0 cellpadding=5 cellspacing=0>
 <tr><td>Meier, Niclas</td><td>MI</td></tr>
 </table>

Absehbar, bei dem Namen ...

Auch Datenbank-Joins sind möglich. Nehmen wir an, wir haben zusätzlich eine Tabelle mit den Bezeichnungen zu den Fachbereichskürzeln

 <DBCSELECT cols="p. name,p.fachbereich, f.bezeichnung" tables="praktika p, fachbereich f">
   <DBC_RESULT_BEGIN><table border=0 cellpadding=5 cellspacing=0></DBC_RESULT_BEGIN>
   <DBC_ROW_CONTENT><tr><td>$COLUMN(1)</td><td>$COLUMN(3)</td></tr></DBC_ROW_CONTENT>
   <DBC_CONDITION>where p.fachbereich = f.fachbereich order by name</DBC_CONDITION>
   <DBC_RESULT_END></table></DBC_RESULT_END>
  </DBCSELECT>

Einfach den Tabellen Aliasnamen geben und wie gewohnt das Join-Kriterium formulieren.

 <table border=0 cellpadding=5 cellspacing=0>
 <tr><td>Meier, Niclas</td><td>Medieninformatik</td></tr>

 [...]

 </table>

Wer Joins nicht mag, kann auch verschachtelte selects machen (wenn die Datenbank das kann)...

 <DBCSELECT COLS="person_id, name, surname" TABLES="person">
   <DBC_CONDITION>where person_id not in (select distinct teacher_id from course)
     order by surname</DBC_CONDITION>
   <DBC_RESULT_BEGIN><table></DBC_RESULT_BEGIN>
   <DBC_ROW_CONTENT><tr><td><input type=radio name=person value=$COLUMN(1)></td><td>$COLUMN(2)
     $COLUMN(3)</td></tr></DBC_ROW_CONTENT>
   <DBC_RESULT_END></table></DBC_RESULT_END>
 </DBCSELECT>

Und auch komplexe HTML-Strukturen lassen sich aufbauen, z.B. eine Tabelle mit Radio-Buttons

 <table>
 <tr><td><input type=radio name=person value=4711></td><td> Niclas Meier</td></tr>
 </table>

Das Erstellen von neuen Datensätzen

Um neue Datensätze zu erstellen, nutzen wir das Tag dbcinsert. Dieses Tag bildet ein SQL-INSERT-Statement nach.

Ein erstes Beispiel, das einfach ein paar Werte in die Datenbank einfügt ...

<dbcinsert table="praktika" result="praktikasuccess">
  <dbc_column name="fachbereich" type=text>MI</dbc_column>
  <dbc_column name="name" type=text>Max Mustermann</dbc_column>
  <dbc_column name="semester">1</dbc_column>
</dbcinsert>

Zugegebenermaßen ein konstruiertes Beispiel, da wir ja meist dynamische Werte einfügen wollen.

<dbcinsert table="praktika" result="praktikasuccess">
  <dbc_column name="fachbereich" type=text><get var=FACHBEREICH></dbc_column>
  <dbc_column name="name" type=text><get var=FULLNAME></dbc_column>
  <dbc_column name="semester"><get var=SEMESTER></dbc_column>
</dbcinsert>

Wir gehen davon aus, daß die Variablen mit den entsprechenden Werten gefüllt sind - etwa mit

<setfromcgi var=FACHBEREICH>

Dem geübten Beobachter werden die unterschiedlichen Typen der dbc_column-Tags aufgefallen sein. Dies führt dazu, daß Fachbereich und Name als Text und Semester als numerischer Typ eingefügt werden. Das INSERT-Statement würde etwa so aussehen:

    insert into praktika (fachbereich, name, semerster) values ('MI', 'Max Mustermann', 1);

Sollten in dem Content eines dbc_column-Tag etwas sein, das das Einfügen in die Datenbank behindern könnte, so wird dies automatisch maskiert. Als Beispiel sei hier "Käpt'n Blaubär" genannt. Dies würde im Normalfall folgendes INSERT-Statement erzeugen:

    insert into praktika (fachbereich, name, semerster) values ('MI', 'Käpt'n Blaubär', 1);

So kann das INSERT-Statement nicht ausgeführt werden. Der Typ Text sorgt für eine Maskierung (nach SQL-Norm) in etwa der Art:

    insert into praktika (fachbereich, name, semerster) values ('MI', 'Käpt''n Blaubär', 1);

Ein weiterer zulässiger Typ ist der Typ date. DBC versucht dann aus dem Content des dbc_column-Tags ein TCL Datum zu erzeugen. Dieses wird dann als SQL-Date (JJJJ-MM-DD) in die Datenbank geschrieben.

<dbcinsert table="praktika" result="praktikasuccess">
  <dbc_column name="fachbereich" type=text><get var=FACHBEREICH></dbc_column>
  <dbc_column name="name" type=text><get var=FULLNAME></dbc_column>
  <dbc_column name="semester"><get var=SEMESTER></dbc_column>
  <dbc_column name="datum" type="date"><eval-tcl>return [clock seconds]</eval-tcl></dbc_column>
</dbcinsert>

Schreibt das akuelle Datum in die Datenbank. Ein weiterer wichtiger Bestandteil sind die Attribute result und error. Hier können Tags genannte werden, die ausgegeben werden, wenn die Datenbankoperation erfolgreich war bzw. fehlgeschlagen ist. Dies kann sehr gut dazu benutzt werden, um eine Rückmeldung an den Benutzer zu erzeugen.

Die aktuelle Zeit in Millisekunden läßt sich auch sehr gut als technischer Primärschlüssel für den Datenbank-Insert benutzen. Vor allem bei Systemen, wo nicht damit zu rechnen ist, daß mehr als ein Insert pro Millisekunde stattfindet.

<dbcinsert table="praktika" result="praktikasuccess">
  <dbc_column name="praktikaId" type=text><eval-tcl>clock seconds</eval-tcl></dbc_column>
  <dbc_column name="fachbereich" type=text><get var=FACHBEREICH></dbc_column>
  <dbc_column name="name" type=text><get var=FULLNAME></dbc_column>
  <dbc_column name="semester"><get var=SEMESTER></dbc_column>
  <dbc_column name="datum" type="text">
    
    <eval-tcl>
	  scan $global_options(DATUM) "%d.%d.%d" day month year
	  return [string trim [clock format [clock scan "$month/$day/$year"]
        -format "%d. %m. %Y"]]
    </eval-tcl>
  </dbc_column>
</dbcinsert>

Hier wird mit der praktikaId ein Primärschlüssel aus der aktuellen Systemzeit in Millisekunden erzeugt. Dann wird auch das Datum als Text in Die Datenbank geschrieben. Ein gutes Beispiel, daß auch in den Datenbank bezogenen Tags eine gewisse Verarbeitung stattfinden kann.

Das Ändern von Datensätzen

Das Ändern mit dbcupdate ist von der Funktion her nicht viel anders als das Einfügen mit dbcinsert

<dbcupdate table="praktika" result="praktikasuccess">
  <dbc_column name="fachbereich" type=text><get var=FACHBEREICH></dbc_column>
  <dbc_column name="name" type=text><get var=FULLNAME></dbc_column>
  <dbc_column name="semester"><get var=SEMESTER></dbc_column>
  <dbc_condition>where praktikaId = <get var=PRAKIKAID></dbc_condition>
</dbcupdate>

Ändert die Daten eines Nutzers mit einer bestimmten PrakikaId (Primärschlüssel). Die Funktionsweise ist äquivalent zu der bei dbcinsert.

Das Löschen von Datensätzen

DBC bietet auch die Möglichkeit Datensätze zu löschen. Dies wird mit dem dbcdelete-Tag vorgenommen.

<dbcdelete table="prakika">
  <dbc_condition>where praktikaId = <get var=PRAKIKAID></dbc_condition>
</dbcdelete>

Löscht einen Praktikanten aus der Datenbank.