HSQLDB als lokale Datenbank

Die HyperSQL-Datenbank, HSQLDB, ist eine Datenbank, die verschiedene Speicheroptionen anbietet. Die Daten kann sie direkt im Speicher, in Dateien oder auf Servern ablegen. Die Flexibilität prädestiniert sie u.a. für die Verwendung innerhalb von Test-Szenarien.

Die HyperSQL Datenbank

HSQLDB ist vollständig in Java geschrieben. Die gesamte Funktionalität ist im Archiv hsqldb.jar gekapselt. Die Datei befindet sich in einem ZIP-Archiv, das unter https://sourceforge.net/projects/hsqldb/files/ herumtergeladen werden muss und je nach Verwendung bereitgestellt wird. Das ZIP-Archiv enthält zsätzlich eine ausführliche englischsprachige Dokumentation.

Soll die Datenbank innerhalb einer Java-Standalone-Application genutzt werden, so bietet sich hierzu die Integration in den Classpath an.
Das Archiv enthält drei Elemente:

Um das Manager-Tool aufzurufen, wechselt man auf der Konsole in das Verzeichnis, in dem hsqldb.jar gespeichert ist und tätigt dann, je nach gewünschtem GUI, einen der beiden folgenden Aufrufe:

java -cp hsqldb.jar org.hsqldb.util.DatabaseManager  // AWT
java -cp hsqldb.jar org.hsqldb.util.DatabaseManagerSwing  // Swing

Die Datenbank selber muss nicht gesondert erzeugt werden. Sie wird, falls sie noch nicht existiert, automatisch gebildet, wenn ein Nutzer durch Aufruf der geeigneten URL die Verbindung herstellt. Die hierzu notwendige URL besitzt die folgende Form

jdbc:hsqldb:<datenbank>

<datenbank> muss hierbei durch eine Angabe ersetzt werden, die je nach Typ der gewünschten Datenbank variiert.
Um die Verbindung herzustellen, wird als Standard der Nutzer SA mit einem leeren Passwort verwendet. Hier kann ein beliebiges Passort, auch ein leeres, eingesetzt werden. Selbstverständlich jedoch müssen beim anschließenden Verbinden mit der dann existierenden Datenbank diese Angaben wieder verwendet werden.

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;

import java.sql.Statement;

public class HSQLDB {

    public static void main(String[] args) {
        new HSQLDB().runMem();
    }

    private void runMem() {
        System.out.println("Database in Memory:");
        ResultSet rs = null;
        Statement stmt = null;
        Connection c = null;
        try {
            c = DriverManager.getConnection(
                    "jdbc:hsqldb:mem:mymemdb;shutdown=true", "jb", "123");
            String query = "CREATE TABLE PUBLIC.USERS (name CHAR(25), age INTEGER NOT NULL, PRIMARY KEY(name));";
            stmt = c.createStatement();
            stmt.executeQuery(query);
            query = "INSERT INTO USERS (name, age) VALUES ('Donals Duck', 83)";
            rs = stmt.executeQuery(query);
            rs.close();
            query = "SELECT * FROM USERS;";
            rs = stmt.executeQuery(query);
            while (rs.next()) {
                System.out.println(rs.getString("name"));
                System.out.println(rs.getInt("age"));
            }
            rs.close();
            stmt.close();
            c.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            try {
                if (rs != null && !rs.isClosed())
                    rs.close();
                if (stmt != null && !stmt.isClosed())
                    stmt.close();
                if (c != null && !c.isClosed())
                    c.close();
            } catch (SQLException e) {
            }
        }
    }
}

Datenbank im Speicher

Im einfachsten Fall wird eine Datenbank, HSQLDB spricht von einem Catalog, direkt im Speicher angelegt. Auf diese Weise wird sie jedes Mal neu erzeugt, ist unabhängig von speichernden Dateien und kann so gut zu Testzwecken oder zum Speichern von temporären Programmdaten verwendet werden.

Das o.a. Beispiel stellt ein lauffähiges Programm dar, das eine Datenbank im Speicher erzeugt, eine Tabelle USERS mit zwei Spalten name und age anlegt und dort anschließend einen Nutzer Donald Duck mit dem Alter 83 einträgt. Anschließend wird der Eintrag wieder ausgelesen und ausgegeben.
Die gesamte Routine befindet sich in der Methode runMem(), die in main() auf einem Objekt der Klasse aufgerufen wird.

Innerhalb eines try-catch-Blockes, der dem Abfangen von SQL-Fehlern dient, wird als erstes eine Verbindung zur Datenbank erzeugt.

DriverManager.getConnection("jdbc:hsqldb:mem:mymemdb;shutdown=true", "jb", "123");

Wie oben erläutert, wird hierbei die Datenbank automatisch neu erzeugt. Der Methode getConnection() werden drei Parameter übergeben. Die letzten beiden sind der Benutzername jb und das Passwort 123. Der erste Parameter stellt die URL zur Datenbank dar. Hierbei erzeugt der Ausdruck mem:mymemdb eine Datenbank mit Namen mymemdb im Speicher. Der durch Semikolon abgegrenzte String shutdown=true stellt eine Verbindungseigenschaft dar und sorgt dafür, dass die Datenbank nach dem Schließen der letzten Verbindung ebenfalls geschlossen wird.
Der dann folgende Quelltext entspricht der in Java gängigen Datenbank-Handhabung, bei der Querys erzeugt, diese durch ein Statement ausgeführt und die Ergebnisse schließlich ggf. in einem ResultSet gespeichert und ausgegeben werden.

Datenbank in Datei

Möchte man die Datenbank statt im Speicher persistent in einer Datei erzeugen, reicht prinzipiell der Austausch der Zeile, in der die Verbindung hergestellt wird.

DriverManager.getConnection("jdbc:hsqldb:file:~/Desktop/testdb", "jb", "123");

Im Vergleich zum vorhergehenden Beispiel erkennt man, dass lediglich der Ausdruck mem:mymemdb gegen file:~/Desktop/testdb getauscht wurde. Es liegt auf der Hand, dass hier angegeben wird, dass eine Datenbank mit Namen testdb in einer Datei unter dem angegebenen Pfad zu erzeugen ist. Die Pfadangabe kann an dieser Stelle sowohl absolut, als auch relativ erfolgen.

Führt man die Datei mit dieser Verbindungsangabe aus, so stellt man fest, dass unter dem angegebenen Pfad ein Verzeichnis und drei Dateien neu erzeugt werden.

testdb.tmp
Ein temporäres Verzeichnis, das darauf hinweist, dass die Datenbank nicht korrekt geschlossen wurde.
testdb.log
Eine Text-Datei, die die kürzlich vorgenommenen Datenänderungen enthält.
testdb.properties
Eine Text-Datei, die Angaben zu den Datenbank-Eigenschaften enthält.
testdb.script
Eine Text-Datei, die die Tabellen-Definitionen und die Daten enthält.

Das Vorhandensein des *.tmp-Verzeichnisses und der *.log-Datei weist darauf hin, dass die Datenbank nach der Ausführung des Programms nicht vollständig geschlossen wurde. Dies lässt sich verhindern, indem die Eigenschafts-Angabe shutdown=true an den Datenbankpfad durch Semikolon getrennt angehängt wird.

DriverManager.getConnection("jdbc:hsqldb:file:~/Desktop/testdb;shutdown=true", "jb", "123");

Führt man das Programm in dieser Form zwei Mal hintereinander aus, so erhält man die folgende Ausgabe:

Database in File:
Donald Duck              
83
Donald Duck              
83

Ein Blick in testdb.script zeigt am Ende den Eintrag

...
SET SCHEMA PUBLIC
INSERT INTO USERS VALUES(1,'Donald Duck              ',83)
INSERT INTO USERS VALUES(2,'Donald Duck              ',83)

Man erkennt hier, dass die bisherigen Einträge in Form der SQL-Querys gespeichert werden. Sie stellen den eigentlichen Datenspeicher dar. Aus anderen DBMS bekannte Binärdateien sucht man bei diesem System vergebens.

Datenbank als *.csv-Datei

HSQLDB erlaubt auch die Verwendung einer *.csv-Datei als Datenbankspeicher. Bei Nichtexistenz wird sie neu erstellt. Es können aber auch bereits bestehende Dateien verwendet werden, sofern die Datenstruktur der geforderten Tabellenstruktur entspricht.
Die Verbindung wird auf die gleiche Weise hergestellt, wie beim obigen Beispiel, allerdings muss zusätzlich die *.csv-Datei als Quelle des Tabelleninhaltes eingetragen werden.

DriverManager.getConnection("jdbc:hsqldb:file:~/Desktop/testdb;shutdown=true", "jb", "123");
//...
query = "SET TABLE USERS SOURCE 'test.csv;fs=\\semi'";
stmt.executeQuery(query);

Die Eigenschaft fs=\\semi sorgt dafür, dass in diesem Fall das Semikolon als Feldtrenner verwendet wird. Weitere Trenner können unter der Überschrift Configuration im Kapitel 5, Text Tables, in der HSQLDB-Dokumentation nachgeschlagen werden. Bitte beachten: Die dort angegebenen Backslashes '\' müssen wie im Beispiel ihrerseits jeweils durch einen Backslash maskiert werden.

Datenbank auf Server

Meist wird eine HSQL-Datenbank wohl lokal betrieben werden. Sie kann jedoch auch auf einem Server eingerichtet und so über ein Netzwerk bereitgestellt werden. Hierzu muss die Datenbank mit Hilfe des folgenden Befehls zunächst auf dem Server erzeugt und gestartet werden:

java -cp <pfad>/hsqldb.jar org.hsqldb.server.Server --database.0 file:mydb --dbname.0 dbtest

Die Angabe <pfad> bezeichnet hierbei den relativen oder absoluten Pfad zum Verzeichnis, in dem hsqldb.jar abgelegt ist. Die Konfigurationsdateien der erzeugten Datenbank mit den Bezeichnern mydb.* werden dann in dem Verzeichnis erstellt, von dem aus der Kommandozeilenbefehl abgesetzt wurde. Der am Ende angegebene Name dbtest wird im Client zur Ansprache der DB verwendet und verschleiert so den Datenbanknamen mydb.
Sollen zwei Datenbanken erzeugt werden, müssen die Angaben zu den letzten beiden Parametern verdoppelt werden:

java -cp <pfad>/hsqldb.jar org.hsqldb.server.Server --database.0 file:mydb --database.1 file:yourdb --dbname.0 mdb --dbname.1 ydb

Eine Übersicht über die verwendbaren Parameter findet sich nach Eingabe von

java -cp <pfad>/hsqldb.jar org.hsqldb.server.Server --help

Nach dem Start der Datenbank wird neben den oben bereits angesprochenen Dateien für jede Datenbank noch eine *.lck-Datei angelegt, die die laufende Datenbank sperrt. Beenden lässt sie sich auf der Konsole hart durch Ctrl + C oder, besser, durch ein SQL-Statement SHUTDOWN.

Die Verbindung zur Datenbank erfolgt idealerweise durch ein proprietäres hsql-Protokoll, kann jedoch auch per http erfolgen, wenn der Zugriff zwingend per http erfolgen muss. Hierzu muss dann statt org.hsqldb.server.Server im obigen Befehl die Klasse org.hsqldb.server.WebServer aufgerufen werden. Zudem sollte der Start als root erfolgen und möglichst einen eigenen Port < 1023 verwenden.

sudo java -cp <pfad>/hsqldb.jar org.hsqldb.server.WebServer --database.0 file:mydb --dbname.0 mdb --port 999