Für den Zugriff auf eine MySQL-Datenbank erstellen wir eine Klasse namens DBZugriff. Die Klasse ist in der Datenzugriffsschicht angesiedelt und unabhängig von der Fachkonzeptschicht. Sie kann daher unverändert in jeder Java-Anwendung eingesetzt werden, die auf eine MySQL-Datenbank zugreifen soll.
19.1.1 Verbindung zur Datenbank aufbauen und beenden
In einem ersten Test werden wir eine Verbindung zu einer MySQL-Datenbank aufbauen. Wenn dies erfolgreich war, beenden wir die Verbindung anschließend wieder. Auf der Konsole erhalten wir dabei entsprechende Mitteilungen, welche Schritte erfolgreich abgeschlossen worden sind beziehungsweise welcher Schritt fehlgeschlagen ist.
Hierfür müssen wir ein Objekt der Klasse DBZugriff erzeugen. Das Objekt soll uns eine Verbindung zu einem MySQL-DBMS ermöglichen, das in unserem Test auf dem gleichen Rechner läuft wie unsere Java-Testanwendung (localhost). Innerhalb des MySQL-DBMS soll mit Hilfe des Benutzers root ein Zugriff auf die Datenbank artikelverwaltung erfolgen. Ein Passwort ist für diesen Benutzer nicht hinterlegt.2
Um zu verstehen, was der Konstruktor im Einzelnen tut, untersuchen wir seinen Quellcode:
Aufgabe 19-1: Test – Verbindung zur Datenbank aufbauen und beenden
Erstellen Sie in Eclipse ein neues Java-Projekt namens 19-1_Artikelverwaltung und binden Sie den von Oracle für MySQL bereitgestellten JDBC-Datenbanktreiber ein.
JDBC (Java Database Connectivity) bezeichnet die von JAVA in den Paketen java.sql und javax.sql bereitgestellten Schnittstellen für den Zugriff auf relationale Datenbankmanagementsysteme (RDBMS). Mit ihrer Hilfe können Java-Anwendungen unabhängig vom verwendeten RDBMS entwickelt werden. In einem Java-Projekt muss lediglich ein vom Hersteller des verwendeten RDBMS bereitgestellter JDBC-Datenbanktreiber eingebunden werden.
Erzeugen Sie ein geeignetes Objekt der Klasse DBZugriff (vgl. Abbildung 19-2). Bauen Sie mit seiner Hilfe eine Verbindung zur Datenbank auf. Wenn dieser Vorgang erfolgreich war, soll die Verbindung anschließend wieder beendet werden. Die hierfür erforderlichen Methoden finden Sie in Abbildung 19-1.
Stellen Sie sicher, dass das MySQL-DBMS auf Ihrem Rechner gestartet ist, bevor Sie den Test ausführen.
In Abbildung 19-5 können wir die Funktionsweise der beiden Methoden openConnection() und closeConnection() anhand eines Quellcode-Ausschnitts der Klasse DBZugriff nachvollziehen.
19.1.2 Abfrageergebnis mit einem Datensatz
In einem zweiten Test werden wir dem DBMS eine Abfrage übergeben, die zu einem Abfrageergebnis mit einem Datensatz führt. Anschließend lesen wir aus dem Datensatz die Werte einiger Spalten aus und geben sie auf der Konsole aus.
Beispiel
Wir fragen aus der Beispieldatenbank die Daten des Artikels mit der Artikelnummer 11076 ab. Anschließend geben wir davon Artikelnummer, Verkaufspreis, Lagerbestand und Artikelbezeichnung auf der Konsole aus.
//Test: Daten des 1. Datensatzes des Abfrageergebnisses (resultSet) werden auf der Konsole ausgegeben publicstaticvoid main(String[] args) {
DBZugriff dbZugriff = new DBZugriff("localhost", "artikelverwaltung", "root", ""); ResultSet resultSet;
if(dbZugriff.openConnection()) { resultSet = dbZugriff.executeQuery("SELECT * FROM artikel WHERE ArtNr='11076';");
try { if(resultSet!=null && resultSet.next()) {
System.out.println( resultSet.getString("ArtNr") + "\t"
+ resultSet.getDouble("VkPreis") + "\t"
+ resultSet.getInt("LBestand") + "\t"
+ resultSet.getString("ArtBez") );
}
} catch (SQLException e) {
System.out.println("Fehler beim DB-Zugriff!" + e.toString());
} finally { //Falls eine Exception geworfen wird, die keine SQLException ist, werden Anweisungen, die auf das try-catch-Konstrukt folgen, nicht mehr ausgeführt. Anweisungen im finally-Block werden jedoch auch in diesem Fall ausgeführt. dbZugriff.closeConnection();
}
}
}
}
Erläuterungen
Zeile 12
Ein Objekt vom Typ1ResultSet ermöglicht den Zugriff auf ein Abfrageergebnis.
Zeile 14
Ist die Methode openConnection() erfolgreich, gibt sie den Wert true zurück und die Abfrage kann durchgeführt werden. Scheitert der Verbindungsaufbau, gibt sie den Wert false zurück und unser Testprogramm endet. Da in letzterem Fall dem Attribut dbConnection des Objekts dbZugriff kein Connection-Objekt zugewiesen wird, erübrigt sich auch ein Aufruf der Methode closeConnection().
Zeile 15
Die executeQuery-Methode führt die übergebene SQL-Anweisung aus. Ist der Vorgang erfolgreich, gibt sie ein Objekt vom Typ ResultSet zurück, andernfalls null. Das Testprogramm wird in jedem Fall fortgeführt.
Zeile 17
Alle Methoden des Objekts resultSet, die in diesem Beispiel aufgerufen werden, lösen eine SQLException aus, falls bei ihrer Ausführung etwas schief geht. Die entsprechenden Aufrufe erfolgen daher innerhalb eines try-Blocks. Eine mögliche SQLException wird durch den catch-Block (Zeile 25) aufgefangen.
Zeile 18
Zunächst wird geprüft, ob resultSet auf ein ResultSet-Objekt verweist. Ist dies der Fall wird für das Objekt die Methode next() ausgeführt.
Die Methode next() versucht den Datensatzzeiger (Cursor) auf den nächsten Datensatz des Abfrageergebnisses zu setzen. Unmittelbar nach einer Abfrage steht der Cursor zunächst vor dem ersten Datensatz. Die Methode gibt den Wert true zurück, wenn der Cursor auf den nächsten Datensatz gesetzt wurde, und den Wert false, wenn es keine weiteren Datensätze mehr gibt.
Scheiterte die Ausführung der Abfrage in Zeile 15, hat resultSet den Wert null. In diesem Fall führt der Versuch die Methode next() auszuführen zu einer Ausnahme, die in Zeile 25 aufgefangen wird. Im finally-Block (Zeile 28) wird sichergestellt, dass alle belegten Ressourcen freigegeben werden. Anschließend endet das Testprogramm.
Zeile 19
Die getString-Methode liest im Abfrageergebnis aus dem Datensatz, auf den der Datensatzzeiger aktuell zeigt, den Wert der Spalte ArtNr aus und gibt ihn als String zurück.
Zeile 20
Die getDouble-Methode liest im Abfrageergebnis aus dem Datensatz, auf den der Datensatzzeiger aktuell zeigt, den Wert der Spalte VkPreis aus und gibt ihn als double-Wert zurück.
Zeile 21
Die getInt-Methode liest im Abfrageergebnis aus dem Datensatz, auf den der Datensatzzeiger aktuell zeigt, den Wert der Spalte LBestand aus und gibt ihn als int-Wert zurück.
Zeile 28
Indem wir die closeConnection()-Methode innerhalb des finally-Blocks aufrufen, stellen wir sicher, dass sie in jedem Fall ausgeführt wird. Alternativ wäre es denkbar die Anweisung nach dem try-catch-Konstrukt aufzurufen. In diesem Fall muss jedoch sichergestellt werden, dass diese Anweisung ausgeführt wird. Das bedeutet alle denkbaren Exceptions müssen abgefangen werden, auch solche die keine Behandlung erzwingen. Das hat zur Folge, dass auch keine Ausnahmen an den Aufrufer weitergereicht werden dürfen. Um diese Einschränkungen zu vermeiden, empfiehlt sich die Verwendung von finally.
Abb. 19-6: Klasse TestDBZugriff2 (Quellcode)
In Abbildung 19-8 können wir die Funktionsweise der Methode executeQuery(pSql: String): ResultSet anhand eines Quellcode-Ausschnitts der Klasse DBZugriff nachvollziehen.
19.1.3 Abfrageergebnis mit mehreren Datensätzen
In einem dritten Test werden wir dem DBMS eine Abfrage übergeben, die zu einem Abfrageergebnis mit mehreren Datensätzen führt. Anschließend lesen wir aus allen Datensätzen des Abfrageergebnisses die Werte einiger Spalten aus und geben sie auf der Konsole aus.
Aufgabe 19-2: Test – Abfrageergebnis auf der Konsole ausgeben
Erstellen Sie nach dem Vorbild der Klasse TestDBZugriff2 die Klasse TestDBZugriff3. Diese soll aus der Beispieldatenbank die Daten aller Artikel abfragen und jeweils Artikelnummer, Verkaufspreis, Lagerbestand und Artikelbezeichnung auf der Konsole ausgeben.
In einem vierten Test werden wir dem DBMS eine SQL-Anweisung übergeben, die die Daten eines neuen Artikels in die entsprechende Tabelle schreibt. Zu Kontrollzwecken lesen wird anschließend den neuen Datensatz aus der Datenbank und geben die Werte einiger Spalten auf der Konsole aus.
Aufgabe 19-3: Test – Einfügen eines neuen Datensatzes
Erstellen Sie die Klasse TestDBZugriff4. Diese soll einen neuen Artikel (Artikelnummer 12346, Bezeichnung Testartikel, Preis 9.99, Lagerbestand 100) in der Beispieldatenbank anlegen. Anschließend soll sie diese Daten aus der Datenbank abfragen und auf der Konsole ausgeben.
Das Ändern beziehungsweise Löschen eines Artikels funktioniert analog. Es muss lediglich die INSERT-Anweisung durch eine UPDATE beziehungsweise DELETE-Anweisung ersetzt werden.