Wie lässt sich in Java eine eingebettete Datenbank realisieren?

Eingebettete Datenbanken sind solche, die ohne gesonderten Server auskommen und somit ideal sind, um sie in Applikationen zu verwenden. Ein zu diesem Zweck häufig verwendetes System ist die Datenbank-Engine H2, die bei Bedarf aber auch über einen Server betrieben werden kann. Sie ist in reinem Java geschrieben, unterstützt Verschlüsselung, Volltext-Suche und vieles mehr.
Das Beispiel illustriert die grundlegenden Verfahrensweisen beim Umgang mit einer eingebetteten H2-Datenbank.

Die gesamte Routine wird von einem try-catch-Block umschlossen, um SQL-Exceptions abzufangen.
Als erstes muss der Treiber geladen werden:

Class.forName("org.h2.Driver");

Hierzu ist es notwendig, die jeweils aktuelle Jar-Datei unter http://www.h2database.com/html/download.html herunterzuladen und in den Classpath einzubinden. Vergessen Sie auch nicht, bei Verwendung von Eclipse die Datei in den Build-Path aufzunehmen. (Project-Properties → Java Build Path).
Der nächste Schritt besteht darin, eine Verbindung zur Datenbank aufzubauen. Dies geschieht durch die statische Methode DriverManager.getConnection(). Sie erwartet drei Parameter

Der Datenbank-URL muss in der Form jdbc:h2:<PfadZurDatei> angegeben werden, wobei der Pfad relativ oder absolut angegeben werden kann. Ist keine Datenbank-Datei angelegt, so wird sie gebildet. Im Beispiel befindet sie sich im versteckten Verzeichnis <UserHome>/.javabeginners und trägt den Namen h2Test.h2.db. Die Dateiendung h2.db darf nicht mit angegeben werden. Wird kein Pfad angegeben, so wird das aktuelle Arbeitsverzeichnis als Speicherort verwendet.
Nutzername und Passwort sind optional und können leer bleiben, bzw. selbstverständlich wie auch der Datenbankname über eine Benutzeroberfläche dynamisch zur Laufzeit eingegeben werden. Auf diese Weise könnten z.B. individuelle Programm-Einstellungen gespeichert werden.

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

public class H2Class {
    public static void main(String[] a) throws Exception {
        Connection conn = null;
        String tab = "TESTTABELLE";

        try {
            
            Class.forName("org.h2.Driver");
            
            conn = DriverManager.getConnection(
                    "jdbc:h2:~/.javabeginners/h2Test", "", "");

            DatabaseMetaData md = conn.getMetaData();

            String[] types = { "TABLE", "SYSTEM TABLE" };
            
            ResultSet metaRS = md.getTables(null, null, "%", types);

            while (metaRS.next()) {

                // Catalog
                String tableCatalog = metaRS.getString(1);
                System.out.println("Catalog: " + tableCatalog);

                // Schemata
                String tableSchema = metaRS.getString(2);
                System.out.println("Tabellen-Schema: " + tableSchema);

                // Tabellennamen
                String tableName = metaRS.getString(3);
                System.out.println("Tabellen-Name: " + tableName);

                // Tabellentyp
                String tableType = metaRS.getString(4);
                System.out.println("Tabellen-Typ: " + tableType + "\n");
            }

            Statement stmt = conn.createStatement();

            String dropQ = "DROP TABLE IF EXISTS " + tab;
            stmt.executeUpdate(dropQ);

            String createQ = "CREATE TABLE IF NOT EXISTS "
                    + tab
                    + "(ID INT PRIMARY KEY AUTO_INCREMENT(1,1) NOT NULL, NAME VARCHAR(255))";
            stmt.executeUpdate(createQ);

            String insertQ = "INSERT INTO " + tab
                    + " VALUES(TRANSACTION_ID(),'Hello World!')";
            stmt.executeUpdate(insertQ);

            ResultSet selectRS = stmt.executeQuery("SELECT * FROM " + tab);
            while (selectRS.next()) {
                System.out.printf("%s, %s\n", selectRS.getString(1),
                        selectRS.getString(2));
            }

            System.out.println("Liste Tabellen...");
            String tablesQ = "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='PUBLIC'";
            ResultSet tablesRS = stmt.executeQuery(tablesQ);
            while (tablesRS.next()) {
                System.out.printf("Tabelle %s vorhanden \n",
                        tablesRS.getString(1));
            }

            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (conn != null)
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
        }
    }
}

Tabellen-Metadaten auslesen

Über das Connection-Objekt werden mit Hilfe der Methode getMetaData() die Meta-Daten der Datenbank abgerufen. Sie müssen nun nach den gewünschten Typen gefiltert werden. Dies geschieht durch die Übergabe eines String-Arrays, in das diese eingetragen werden, an die Methode getTables() des DatabaseMetaData-Objektes. Im Beispiel werden die Metadaten der System-Tabellen und der vom Nutzer angelegten Tabellen abgefragt. Möchte man nur die Ausgaben für die Nutzertabellen abfragen, so entfernt man den Eintrag SYSTEM TABLE aus dem Array.
Neben den genannten können hier auch die folgenden Typen gewählt werden:

Die anderen drei Parameter, catalog, schemaPattern und tableNamePattern, können verwendet werden, um die Suche weiter einzuschränken. Die Angabe null für die ersten beiden Parameter bewirkt, dass diese Einschränkungen nicht verwendet werden. Das Prozentzeichen % des dritten Parameters bewirkt das Auslesen aller eingetragenen Tabellen.
In der Folge wird über das ResultSet iteriert, um die Einträge auszugeben.

Abfragen und Updates

Das Interface java.sql.Statement liefert ein Objekt zum Absetzen von Datenbank-Anfragen und zur Ausgabe der entsprechenden Ergebnisse. Im Beispiel werden auf ihm zwei verschiedene Methoden aufgerufen:

  1. executeUpdate() liefert einen int-Wert und wird für INSERT-, UPDATE- und DELETE-Statements verwendet. Der Statement-String wird als Parameter übergeben.
  2. executeQuery() liefert ein ResultSet und wird üblicherweise für SELECT-Abfragen eingesetzt.

Die Tabelle mit dem in der Variablen tab gespeicherten Namen wird im Folgenden der Reihe nach

Den Abschluss bildet eine weitere Form der Ermittlung der existierenden User-Tabellen: Auch hier stellt eine SELECT-Abfrage die Grundlage dar, um ein ResultSet zu erhalten, das durchlaufen und ausgegeben werden kann. Mit conn.close() wird die Verbindung zur Datenbank schließlich geschlossen.