Wir haben uns entschieden, alle für die Darstellung der virtuellen 3D-Gebäude notwendigen Informationen in einer Datenbank zu speichern. Als Datenbanksoftware wählten wir MySQL. Außer der kostenlosen Verfügbarkeit unter Linux waren folgende Punkte für uns von Bedeutung: MySQL ist eine echte Multi-User, Multi-Threaded SQL Datenbank und wird von allen großen Providern oder auch Suchmaschinenbetreibern eingesetzt. MySQL ist eine CLient/Server Implementierung, die aus einem Server-Dämon mysqld und vielen Client Programmen besteht. MySQL ist äußerst schnell und flexibel genug, um sogar Bilder und Log-Dateien darin abzulegen. In der Praxis ist MySQL sehr viel schneller, als z.B. ORACLE oder INFORMIX.
Wie der Name sicher schon nahe legt, werden Anweisungen an die Datenbank mit Hilfe der Sprache SQL formuliert. SQL ist eine standardisierte Datenbanksprache, die das Speichern, Updaten und den Zugriff auf Informationen erleichtert. SQL steht für Structured Query Language und wurde Ende der 70'er Jahre bei IBM in San Jose, Kalifornien als Abfragesprache für die relationale Datenbank DB2 entworfen. Es handelte sich hier ursprünglich um eine nichtprozedurale Sprache, die also keine Schleifen, Unterprogramme, Funktionen und Funktionsübergabeparameter u.s.w. enthält.
SQL ist also in dem Sinne keine Programmiersprache und dementsprechend einfach zu erlernen. Die SQL Befehle setzen sich aus zwei Teilen zusammen, der Data Definition Language (DDL) und der Data Manipulation Laguage (DML). Die DDL dient dem Aufsetzen der Datenbankstruktur, die DML dient der Manipulation der darin enthaltenen Daten.
Die wichtigsten von uns verwendeten SQL-Befehle wollen wir kurz erklären.
CREATE TABLE tbl_name (create_definition,...)
In unserem Beispiel wird eine neue Tabelle room angelegt. Hier sollen alle relevanten
Informationen, die wir zum Erstellen eines Raumes benötigen, gespeichert werden. Wir
haben uns im Beispiel der Einfachheit halber nur auf die eindeutige ID (Index), dem Namen
des Raumes und dessen Ursprungskoordinaten beschränkt. Natürlich werden keine
Tabellen durch unser 3D-Simulationsprogramm erzeugt oder verändert. Diese Aufgabe bleibt
dem Betreuer der Datenbank überlassen.
CREATE TABLE room (ID INTEGER PRIMARY KEY,
Name VARCHAR(30),
Origin_x DOUBLE,
Origin_y DOUBLE,
Origin_z DOUBLE)
DROP TABLE tbl_name
In unserem Beispiel wird die Tabelle room gelöscht. Das Entfernen von Tabellen wird
wiederum nicht vom 3D-Simulationsprogramm durchgeführt und ist nur dem Betreuer der
Datenbank erlaubt.
DROP TABLE room
INSERT INTO tbl_name VALUES (expression,...)
In unserem Beispiel wird in die Tabelle room der Raum mit dem Namen FR0001 und den
Ursprungskoordinaten 1, 13.75 und 25.2 eingefügt.
INSERT INTO room VALUES (3, 'FR0001', 1, 13.75, 25.2)
SELECT select_expression,... FROM table_references WHERE where_definition
In unserem Beispiel wollen wir Name und Ursprungskoordinaten des Raumes in der Tabelle room
ermitteln, der die ID 3 hat.
SELECT Name, Origin_x, Origin_y, Origin_z FROM room WHERE ID = 3
UPDATE tbl_name SET col_name1=expr1,col_name2=expr2,... WHERE where_definition
Das folgende Beispiel zeigt, wie der Raumname in der Tabelle room mit der ID 3 auf FR3056
gesetzt wird.
UPDATE room SET Name = 'FR3056' WHERE ID = 3
DELETE FROM tbl_name WHERE where_definition
Hier wird in der Tabelle room die Spalte mit der ID 3 gelöscht.
DELETE FROM room WHERE ID = 3
Die Datenbankanbindung mit JDBC kann man ganz allgemein in die folgenden vier Schritte unterteilen.
Die Architektur von JDBC ähnelt sehr der von ODBC: Die zentrale Rolle spielen die angesprochenden JDBC-Treiber, die ein Treibermanager verwaltet. Es gibt vier verschiedene Typen von Treibern:
Hat man sich für einen Treiber entschieden, kann es richtig losgehen. Nun muss der ausgewählte JDBC-Treiber geladen werden, was zum Beispiel die folgende Zeile bewirkt:
Class.forName("org.gjt.mm.mysql.Driver");
Dieser Aufruf macht der Java-Umgebung Driver bekannt. Für den Fall, dass der Aufruf
fehlschlägt, sollte die Anweisung in einem Try/Catch-Block stehen, der Ausnahmesituationen
behandelt. Wenn sich der Treiber nicht auf dem Client befindet, versucht der Browser die
benötigten Klassen übers Web nachzuladen. Erst wenn auch das keinen Erfolg hat, tritt
eine Exception auf.
jdbc:<Protokoll>:<Datenbank-URL>
Der DriverManager sucht dann selbstständig nach einem geeigneten Treiber für das
angegebene Protokoll, im Beispiel also 'mysql'. Wurde vorher kein entsprechender Treiber
via Class.forName() geladen, erzeugt der Manager eine Exception.
Die Form der Datenbank-URL gibt an, wo und wie die Datenbank zu erreichen ist. Ihre Form hängt vom verwendeten Treiber ab. In unserem Fall hat die URL folgende Form:
String url = "jdbc:mysql://lava.cs.tu-berlin.de/franklin";
Der Aufruf von
Connection con = DriverManager.getConnection
(url, "username", "password");
erzeugt dann eine Verbindung zur Datenbank, über die das Programm SQL-Abfragen
absetzen kann.
String stringSelect = "SELECT ID FROM wall WHERE RoomID = " + roomID;
Auch Datenbank-spezifische Erweiterungen oder
Nicht-SQL-Anweisungen können an die Datenbank gesendet werden. Es liegt aber in der
Verantwortung des Entwicklers, sicherzustellen, dass die Datenbank auf der anderen Seite
der Verbindung damit etwas anfangen kann, beziehungsweise eventuelle Fehler abzufangen.
Um einfache SQL-Aktionen durchführen zu können, muss für die Verbindung jeweils ein Statement-Objekt erzeugt werden.
Statement stmtSelect = con.createStatement();
Für komplexere Anwendungen gibt es die
Methoden prepareStatement() und prepareCall(). Da wir diese Methoden nicht verwenden,
gehen wir hier nicht weiter darauf ein.
Das Statement-Object stellt die Methoden executeQuery() und executeUpdate() bereit. Über executeQuery() lassen sich SQL-Abfragen absetzen, die eine Menge von Datensätzen, also ein ResultSet, zurückliefern.
ResultSet rs = stmtSelect.executeQuery(stringSelect);
Auf die einzelnen Datensätze kann man anschließend etwa innerhalb einer
Schleife mit rs.next() zugreifen. Sie bewegt den Datenbank-Cursor auf die nächste
Tabellenzeile.
while (rs.next()) {
wall_IDs[i] = rs.getInt("ID");
i++;
}
Außerdem enthält ein ResultSet-Objekt get-Methoden für alle
SQL-Datentypen, die ein entsprechendes Java-Objekt zurückliefern. Die Methoden
sind nach den Java-Objekten benannt, die man durch ihren Aufruf erhält. So
liefert beispielsweise rs.getInt() ein Int-Objekt zurück.
executeUpdate() dient der Modifikation von Tabellen und kann einfache SQL-Anweisungen wie INSERT, UPDATE, DELETE, aber auch CREATE TABLE oder DROP TABLE ausführen, die sich auf die gesamte Tabelle beziehen. Die Anweisung
String stringDelete = "DELETE FROM wall WHERE ID=" + id;
Statement stmtDelete = con.createStatement();
int rowCount = stmtDelete.executeUpdate(stringDelete);
löscht die Zeile mit dem id-Feld 'id' aus der Tabelle wall. rowCount enthält die
Zahl der betroffenen Zeilen.
con.close();
explizit schließen und so die dafür reservierten Ressourcen freigeben.
Die genaue Struktur der Kapselung findet man in der mit javadoc erstellten Dokumentation.