Verknüpfung mit Java

Die Abiturklassen

Einführung

Wir lernen nun, wie wir von Java aus auf eine Datenbank zugreifen können. Dabei verwenden wir die beiden Abiturklassen QueryResult und DatabaseConnector. Ihre offiziellen Dokumentationen sowie die Klassen selbst sind hier zu finden.

Betrachten wir ein Diagramm, das die beiden Klassen beschreibt:

Klassendiagramm der beiden relevanten Abiturklassen
Klassendiagramm der beiden relevanten Abiturklassen

Die Klasse QueryResult modelliert das Ergebnis einer SQL-Abfrage in Form eines zweidimensionalen String Arrays – also letztlich als Tabelle. Gehen wir kurz auf ihre Methoden ein – das folgende Beispiel wird danach sicherlich noch für mehr Klarheit sorgen:

String [][] getData() Diese Methode liefert das zweidimensionale String-Array zurück, das das Ergebnis der letzten SQL-Abfrage darstellt. Der erste Index gibt dabei die Zeile und der zweite Index die Spalte an.
String[] getColumnNames() Hiermit erhalten wir die Überschriften der Spalten unserer Ergebnistabelle wie sie in der gegebenen Datenbank verwendet werden.
String[] getColumnTypes() Dies liefert uns die Datentypen der Spalten. Die Datentypen werden dabei einfach als Strings (also als Text) angegeben. Die Bezeichnungen richten sich nach der Datenbank.
int getRowCount() Liefert die Anzahl der Zeilen der Ergebnistabelle.
int getColumnCount() Liefert die Anzahl der Spalten der Ergebnistabelle.

Es fällt vielleicht auf, dass diese Klasse keinen öffentlichen Konstruktor besitzt. Das ist sinnvoll, denn schließlich wollen wir die Ergebnistabellen nicht selbst erstellen. Dies erledigt für uns ein Objekt der Klasse DatabaseConnector.

Hier eine Übersicht über ihre Methoden:

DatabaseConnector(String pIP, int pPort, String pDatabase, String pUsername, String pPassword) Ein neues Objekt dieser Klasse wird erstellt und baut eine Verbindung zur angegeben Datenbank auf. Arbeiten wir mit SQLite ist einzig der Parameter pDatabase relevant.
void executeStatement(String pSQLStatement) Die SQL-Anweisung pSQLStatement wird ausgeführt.
QueryResult getCurrentQueryResult() Liefert das Ergebnis der zuletzt ausgeführten SQL-Anweisung. Handelte es sich um einen INSERT- oder DELETE-Befehl, wird hier null geliefert.
String getErrorMessage() Falls es bei der letzten Ausführung eines Anweisung einen Fehler gab, wird dieser als String geliefert. Gab es keinen, so erhalten wir null.
void close() Schließt die Verbindung zur Datenbank.

Beispiel für einen Probedurchlauf

Wir wenden die beiden Abiturklassen nun in einem einfachen Beispiel an.

Zur Vorbereitung sind 3 Schritte notwendig, die wir gleich noch genauer betrachten:

  1. Die Datenbank muss bereitgestellt werden.
  2. Die Abiturklassen müssen im Projekt vorliegen.
  3. Die Treiberdatei für SQLite muss im Projekt eingebunden werden.

Schritt 1:

Wir verwenden die Datenbank Schuelerdaten.db, die hier als zip-Datei heruntergeladen werden kann. Bei den Daten handelt es sich um zufällig erzeugte Kombinationen von Namen, Vornamen etc..
Nach dem Download muss sie entpackt und in den Ordner verschoben werden, in dem sich das Java-Projekt befindet, von wo aus wir auf die Daten zugreifen wollen. In Eclipse ist dies der Ordner, der den Namen unseres Projekts trägt. Sie muss nicht in den Ordner src oder bin!
Wenn beim Testen etwas schief geht, liegt es übrigens sehr häufig daran, dass sich die Datei an einer falschen Stelle befindet!

Schritt 2:

In unserem Projekt benötigen wir die beiden oben beschriebenen Abiturklassen und die Abiturklasse Queue. Diese müssen also dort eingefügt werden.

Schritt 3:

Außerdem benötigen wir noch eine Treiberdatei, damit wir auf eine SQLite-Datenbank zugreifen können. Diese kann hier heruntergeladen werden. Auch diese Datei muss entpackt werden. Danach muss sie ins Projekt eingebunden werden. In Eclipse geschieht dies über die Projekteigenschaften unter Java Build Path, indem man die Datei als „External JAR“ hinzufügt:

Einbinden der Treiber in Eclipse
Einbinden der Treiber in Eclipse

In anderen Umgebungen (BlueJ, Netbeans,…) gibt es entsprechende Möglichkeiten.

Haben wir alles vorbereitet, können wir dieses Beispiel erstellen:

import databases.DatabaseConnector;

public class Testlauf {
 public static void main(String[] args) {
  /* Ein Objekt der Klasse DatabaseConnector wird erstellt.
  SQLite verlangt als einzige Angabe den Namen der Datei!
  Die anderen Angaben sind irrelevant.
  */  
  DatabaseConnector meinConnector = new DatabaseConnector("", 0, "Schuelerdaten.db", "", "");
  
  /*
  Eine SQL-Anweisung wird ausgeführt.
  */
  meinConnector.executeStatement("select * from Schueler");
  /*
  Zur Sicherheit sollten wir sehr häufig die Fehlermeldungen prüfen.
  */
  System.out.println(meinConnector.getErrorMessage());
  
  /*
  Das Ergebnis wird in der Konsole angezeigt.
  */
  for (int i=0; i<meinConnector.getCurrentQueryResult().getRowCount(); i=i+1) {
   
   for (int j=0; j<meinConnector.getCurrentQueryResult().getColumnCount(); j=j+1) {
    System.out.print(meinConnector.getCurrentQueryResult().getData()[i][j]+" ");
   }
   
   System.out.println();
  }

 }

}

Hat alles funktioniert, werden die Daten der gespeicherten Schüler ausgegeben. Andernfalls ist leider eine Fehlersuche nötig. Fast immer geht beim ersten Versuch einer der drei oben beschriebenen Schritte schief.

Sehen wir uns noch eine Modifizierung des Beispiels an:

import java.util.Scanner;

public class Testlauf {

 public static void main(String[] args) {

  DatabaseConnector meinConnector = new DatabaseConnector("", 0, "Schuelerdaten.db", "", "");

  Scanner meinScanner = new Scanner(System.in);

  System.out.println("Welcher Nachname ist gesucht?");
  String nachname = meinScanner.next();

  meinConnector.executeStatement("select * from Schueler where name = '" + nachname + "'");

  if (meinConnector.getCurrentQueryResult().getRowCount() == 0) {
   System.out.println("Der Name ist nicht vorhanden!");
  } else {
   for (int i = 0; i < meinConnector.getCurrentQueryResult().getRowCount(); i = i + 1) {

    for (int j = 0; j < meinConnector.getCurrentQueryResult().getColumnCount(); j = j + 1) {
     System.out.print(meinConnector.getCurrentQueryResult().getData()[i][j] + " ");
    }

    System.out.println();
   }
  }
  meinScanner.close();
 }
}

Hier wird man beim Start nach einem Nachnamen gefragt. Dieser wird dann eingelesen und passende Schüler werden gesucht. Gibt keinen Schüler mit dem gesuchten Nachnamen, erscheint eine entsprechende Meldung.
Dass es keinen passenden Datensatz gibt, kann man, wie man hier sieht, sehr schön mit der Bedingung  meinConnector.getCurrentQueryResult().getRowCount() == 0überprüfen, denn diese sagt aus, dass keine passenden Zeilen gefunden wurden, d.h., dass die Ergebnistabelle keine Zeilen enthält.

Beispielanwendung

Hier möchte ich an das (kleine) Trainingsprojekt Kiosk zur Einführung von Queues anknüpfen. Es empfiehlt sich, dieses vor dem Weiterlesen anzusehen!

Die folgende Klasse ist eine Unterklasse von Kiosk, die an einem einfachen Beispiel zeigen soll, wie die Abiturklassen zur Verknüpfung mit einer Datenbank eine Anwendung finden könnten:

public class DatenbankKiosk extends Kiosk {

 DatabaseConnector meinConnector;

 public DatenbankKiosk(String pDatenbank) {
  super();
  meinConnector = new DatabaseConnector("", 0, pDatenbank, "", "");
 }

 public boolean istInDatenbank(int pId) {
  meinConnector.executeStatement("select * from Schueler where SID=" + pId);

  return (meinConnector.getCurrentQueryResult().getRowCount() > 0);
 }

 public Schueler gibSchueler(int pId) {
  meinConnector.executeStatement("select Vorname, Name, Bezeichnung from Schueler where SID=" + pId);

  if (meinConnector.getCurrentQueryResult().getRowCount() > 0) {
   Schueler ergebnis;

   if (meinConnector.getCurrentQueryResult().getData()[0][2].equals("Q2")
     || meinConnector.getCurrentQueryResult().getData()[0][2].equals("Q1")
     || meinConnector.getCurrentQueryResult().getData()[0][2].equals("EF")) {
    // Oberstufenschüler anstellen:
    ergebnis = new Schueler(meinConnector.getCurrentQueryResult().getData()[0][0]
      + " " + meinConnector.getCurrentQueryResult().getData()[0][1], true);
   } else {
    // Unter- / Mitteltufenschüler anstellen:
    ergebnis = new Schueler(meinConnector.getCurrentQueryResult().getData()[0][0]
      + " " + meinConnector.getCurrentQueryResult().getData()[0][1], false);
   }

   return ergebnis;
  } else {
   return null;
  }
 }

 public void stelleAn(int pId) {
  Schueler neuer = gibSchueler(pId);

  if (neuer != null) {
   super.stelleAn(neuer);
  }
 }
}

Betrachten wir die Methoden dieser Klasse:

boolean istInDatenbank(int pId) Diese Methode liefert true, falls es einen Schüler mit der gegebenen ID in der Datenbank gibt und sonst false. Um dies festzustellen, wird geprüft, ob die Suche nach der ID mehr als 0 Zeilen enthält.
Schueler gibSchueler(int pId) Diese Methode liefert ein Objekt der Klasse Schueler, das zu der gegeben ID gehört. D.h., anhand der Datenbank werden Vor- und Nachname ermittelt und ob der Schüler in der Oberstufe ist. Gibt es die ID nicht in der Datenbank, wird null geliefert.
void stelleAn(int pId) Hiermit wird der Schüler mit der gegebenen ID am Kiosk angestellt, sofern die ID existiert. Andernfalls passiert nicht.

Das Herzstück bildet hier ganz klar die zweite Methode. Daher würde ich empfehlen, diese für sich mit Geduld ganz in Ruhe nachzuvollziehen.

So kann ein Testlauf aussehen:

public class KioskTest {

    public static void main(String[] args) {
        DatenbankKiosk meinDBKiosk = new DatenbankKiosk("Schuelerdaten.db");


        meinDBKiosk.stelleAn(0);
        System.out.println(meinDBKiosk.getMessage());
        meinDBKiosk.stelleAn(1);
        System.out.println(meinDBKiosk.getMessage());
        meinDBKiosk.stelleAn(5);
        System.out.println(meinDBKiosk.getMessage());
        meinDBKiosk.stelleAn(3);
        System.out.println(meinDBKiosk.getMessage());

        meinDBKiosk.bedieneErsten();
        System.out.println(meinDBKiosk.getMessage());
        meinDBKiosk.bedieneErsten();
        System.out.println(meinDBKiosk.getMessage());
        meinDBKiosk.bedieneErsten();
        System.out.println(meinDBKiosk.getMessage());
    }

}