www.r-krell.de |
Webangebot für Schule und Unterricht, Software, Fotovoltaik und mehr |
Willkommen/Übersicht > Informatik >
Teil 3: Datenbanken aus und für Java-Anwendungen verwenden
Die Ausführungen über Datenbanken ergänzen meine Reihe „Informatik mit Java".
Auf der vorangegangenen Seiten "Datenbanken, Teil 1" gab es die Übersicht und sinnvolle Software und auf "Datenbanken, Teil 2": Entwurf, Normalisierung, ERD und SQL-Abfragen wurden theoretische Konzepte sowie praktische Übungen mit MySQL und dem MySQL-QueryBrowser vorgestellt.
Auf dieser vorerst letzten Datenbank-Seite "Datenbanken, Teil 3" finden Sie:
zum Seitenanfang / zum Seitenende
Datenbanken und Java
-- getrennte Welten und vorteilhafte Symbiose
1) MySQL als Server -- Bedienung durch Clients
Die Datenbank-Software MySQL arbeitet als Server, d.h. MySQL stellt die Datenbankdienste zur Verfügung und übernimmt das Speichern und Verwalten der Daten. Allerdings arbeitet diese Software auch nach dem Start servergemäß im Hintergrund und hat keine eigene Oberfläche. Um MySQL zu nutzen und zu bedienen, muss deswegen ein so genanntes Client-Programm verwendet werden, das Befehle vom Menschen entgegen nimmt und an MySQL übermittelt, dort die zur Verfügung gestellten Dienste nachfragt bzw. ausführen lässt und schließlich die von MySQL gelieferten Antworten und Ausgaben in eigenen Fenstern sinnvoll anzeigt. Oft wird der im XAMPP-Paket vorhandene PHP-Client dazu genutzt. Auf meinen beiden voran gegangen Datenbank-Seiten wurde aber der MySQL-QueryBrowser aus den MySQL-GUI-Tools verwendet, der auch hier nochmal zum Einsatz kommt. Grundsätzlich ist es egal, welcher Client verwendet wird und in welcher Sprache der Client geschrieben ist, sofern einige elementare Kommunikationsregeln eingehalten werden. Insofern ist es auch leicht möglich, selbst einen Client in Java zu schreiben und so den Datenbankzugriff und die Bedienung von MySQL aus einem eigenen Java-Programm heraus zu realisieren.
Im Folgenden soll ein solches Programm entwickelt und vorgestellt werden.
zum Seitenanfang / zum Seitenende
2) Datenbankformate als gute Wahl bei allen komplexen Speicherungen
Beherrscht man einmal den Datenbankzugriff aus Java heraus, bietet sich allerdings auch an, bei vielen anderen Programmen und Anwendungen -- etwa der im Teil d) meiner Seiten "Informatik mit Java" beschriebenen Adress- und Fuhrpark-Verwaltung -- die Daten nicht per writeObject-Anweisungen in einem javaeigenen Format auf der Festplatte zu speichern, sondern direkt in einer (MySQL-)Datenbank abzulegen. Die Datenbank-Formate sind nämlich deutlich langlebiger bzw. zukunftssicherer und Daten können notfalls auch mit anderen Programmen gelesen und bearbeitet werden. Bei Java reicht hingegen oft schon das Neukompilieren der Anwendung in einer neuen Java-Version, dass sich die Serialisierung der Objekte ändert und alte Daten nicht mehr gelesen werden können und praktisch verloren sind. Insofern bringt das Speichern in einer Datenbank dauerhafte Datensicherheit und Unabhängigkeit; künftige Weiterentwicklungen könnten auch in anderen Sprachen geschehen und trotzdem noch den vorhandenen Datenbestand nutzen. Außerdem brauchen Methoden etwa zum Sortieren oder Filtern der Daten nicht mehr mühsam und fehleranfällig selbst geschrieben und programmiert werden, sondern es kann bequem auf die weithin bekannten, vielfach erklärten und längst ausgetesteten SQL-Befehle zurück gegriffen werden. Deshalb sollten Daten eigentlich bei allen vernünftigen Programmen entweder in reiner Textform oder -- bei strukturierten Daten -- in einer Datenbank gespeichert werden! Die Verwendung eigener, 'generischer' Formate, die früher vielleicht einmal Platz- oder Zeitvorteile boten, ist angesichts heutiger Rechnerausstattungen nicht mehr zeitgemäß und einfach auch zu risikoreich.
zum Seitenanfang / zum Seitenende
3) Voraussetzungen MySQL und Java-MySQL-Connector/J
Weil das Java-Programm 'nur' als Client arbeitet, muss für den Datenbankzugriff immer gleichzeitig MySQL als Server im Hintergrund laufen. Um die richtige Kommunikation zwischen Server und Client braucht sich der Java-Programmierer bzw. die Anwendungs-Programmiererin nicht kümmern, dies übernimmt der Java-MySQL-Connector, der einmal ins System eingebunden wird (der Download und die einmalige Installation des MySQL-Connectors wurden im Teil 1 meiner Datenbank-Seiten bereits beschrieben). Fertige, von Sun/Oracle bzw. mit der Java-JDK mitgelieferte Bibliotheksbefehle (aus dem Paket java.sql) erlauben die Weitergabe und das Auswerten von Datenbank-Befehlen. Der Programmierer kann sich daher auf seine übliche Aufgabe, die Konstruktion einer ansprechenden und zweckdienlichen Benutzeroberfläche und das Hinterlegen der Schaltflächen mit geeigneten Funktionen kümmern.
zum Seitenanfang / zum Seitenende
Ein Java-Programm
mit Datenbankzugriff auf MySQL
Um grundlegende Möglichkeiten zu demonstrieren, wurde ein Java-Programm entwickelt, das eine Datenbank namens TestJDB einrichtet und darin beispielhaft eine kleine Tabelle Kunde anlegt und mit einigen Datensätzen füllt. Ist die Datenbank TestJDB schon vorhanden, kann das Programm damit verbinden und sie weiter nutzen. Vom Benutzer in übliche Textfelder eingegebene Daten können zu Datensätzen verbunden und in der Datenbank bzw. dort in der Tabelle Kunde gespeichert werden; natürlich können die Daten auch wieder geholt und angezeigt werden. Außerdem soll die Eingabe beliebiger SQL-Befehle durch den Benutzer möglich sein, womit weitere Datenbanken erstellt und verändert, Tabellen angelegt, manipuliert und abgefragt werden können.
Auf dieser Webseite stelle ich den vollständige Programmtext vor. Das Programm umfasst zwei Klassen, beschrieben in den Dateien DBJ_JOberflaeche.java [Quelltext hier zusammenhängend auf türkisfarbenem Hintergrund dargestellt] und DBJ_JFunktion.java [weißer Hintergrund; verteilt auf die Abschnitte 1) bis 8) im nächsten Kapitel: der Quelltext wird jeweils von Kommentaren unterbrochen]. Ein Reiter im folgenden Bild kündet noch von der Existenz der Datei DBJ_JOberflaeche.jfm -- hierin speichert der Javaeditor das Formular, auf dem die Oberfläche mit der Maus hergestellt und verändert werden kann. Dieses Formular ist aber nicht nötig, um das Programm laufen zu lassen. Alle Informationen aus der .jfm-Datei finden automatisch ihren Niederschlag im Programmtext in DBJ_JOberflaeche. Wer also den folgenden Programmtext kopiert, kann ihn in jeder beliebigen Java-Entwicklungsumgebung neu kompilieren und das Programm laufen lassen. Noch bequemer ist allerdings der Test durch Download der fertigen jar-Datei weiter unten im Kapitel "Der Programmstart".
Die Oberfläche wurde mit dem Javaeditor (hier mit der Version 9.14p) im Wesentlichen per Drag-and-Drop erzeugt. Bezugsmöglichkeiten und Einrichtung des Javaeditors sind auf meiner Seite "Informatik mit Java, Teil a): Grundlegendes zu Java, benötigte Software und deren Installation" genannt -- wie gerade gesagt, kann aber auch jede andere Javaentwicklungsumgebung oder notfalls sogar ein einfacher Texteditor genutzt werden. Das folgende Bild zeigt aber die Arbeit an der Benutzer-Oberfläche im Javaeditor. Während der Javaeditor die in die Oberfläche gezogenen Komponenten automatisch etwa mit jLabel1, jLabel2 usw. bezeichnet, habe ich im Objekt-Inspektor jeweils sprechende Namen gegeben (und den Typ als Kürzel vorangestellt): so ist jLbKundenNr das Label, das den Text "KundenNr:" auf der Oberfläche anzeigt, jTfKundenNr das Textfeld, in das der Benutzer die Kundennummer eingeben kann und so wäre jBtKundenNr der Button bzw. die Schaltfläche, mit der etwa die Kundennummer bestätigt werden müsste, falls dafür eine Schaltfläche vorgesehen wäre. Das kleine j im Präfix macht deutlich, dass hier nicht AWT-, sondern Swing-Komponenten in eine von JFrame abgeleitete Oberfläche eingebunden werden.
Die Benutzer-Oberfläche DBJ_JOberflaeche enthält zwar alle Bedienelemente und zugehörige Methoden, soweit sie sich auf die Darstellung beziehen, ruft aber zur Erledigung der Aufgaben Methoden des eingebundenen Objekts db der Klasse DBJ_JFunktion auf. Denn in der letzten Klasse habe ich die eigentlichen Methoden zur Datenbankmanipulation und -abfrage zusammen gefasst, die dort unabhängig von der Oberfläche stehen.
zum Seitenanfang / zum Seitenende
Die grafische Benutzer-Oberfläche DBJ_JOberflaeche
1) Der Quelltext der Oberfläche
Wie im vorstehenden Abschnitt schon erläutert, wurde der Quelltext der Oberfläche weitgehend automatisch vom Javaeditor erzeugt, während ich die Bedienelemente per Mausklick bzw. Ziehen und Ablegen auf dem Formular angeordnet und im Objekt-Inspektor benannt bzw. beschriftet habe.
Von Hand eingefügt wurde die Zeile 040 (Einbinden des Objekts db mit der Funktionalität) sowie die Java-Texte in die Methoden ab Zeile 191. Etwas länger ist der Text in den Zeilen 209 bis 245, weil beim Betätigen des "O.K."-Buttons für das Hinzufügen einer weiteren Person in die Kunden-Tabelle eine Überprüfung der Eingaben vorgenommen wird und der Benutzer ggf. auf Fehler hingewiesen wird. Im Prinzip hätte der Text Zeilen 209 bis 245 schon an Stelle der Zeile 133 eingesetzt werden können, aber der Javaeditor trägt automatisch in die ActionPerformed-Methode jeder Schaltfläche einen Methodenaufruf auf eine später leer erstellte Methode wie etwa jBtOK_ActionPerformed ein, damit die vom Programmierer auszufüllenden Teile übersichtlich ab Zeile 191 hinter dem Konstruktor stehen.
Soweit der Quelltext der Oberfläche. Einleitend gab es ein paar kurze Erläuterungen [direkt vor Beginn des Quelltextes in diesem Abschnitt 1)].
zum Seitenanfang / zum Seitenende
2) Das Aussehen der Oberfläche
Die vorstehend im Quelltext beschriebene Oberfläche präsentiert sich nach dem Start bzw. beim Ausführen des Java-Programms in der folgenden Form, wobei das untere große Ausgabefenster die Reaktionen auf die betätigten Schaltflächen protokolliert und zeigt, dass hier schon einige Tasten gedrückt wurden (nämlich "TestJDB neu erstellen", "Tabelle 'Kunde' zeigen", " O.K. " und nochmal "Tabelle 'Kunde' zeigen").
Die Programmierung für das, was beim Betätigen der Schaltfläche passiert, folgt in der nachfolgend ebenfalls vollständig vorgestellten Klasse DBJ_JFunktion.
zum Seitenanfang / zum Seitenende
Die Arbeitsklasse DBJ_JFunktion im Java-Quelltext
1) Die Methoden zeigeVoraussetzungen und sucheKontakt
Hier folgt der eigentlich spannende Teil, nämlich die Java-Befehle zum Arbeiten mit einer Datenbank. Alle nummerierten, weiß
unterlegten Zeilen bilden zusammen die Klasse DBJ_JFunktion.
Zunächst folgt der Kopf der Klasse. Die Methode zeigeVoraussetzungen (folgende Zeilen 022 bis 033) wird aus der Oberfläche
heraus aufgerufen, wenn dort die Taste
gedrückt wird (siehe auch oben, Oberflächen-Zeile 193).
001 // Demo: Datenbankzugriff aus/mit Java 002 // 12.6.2010, R. Krell für IF12M 003 004 import java.sql.*; 005 006 public class DBJ_JFunktion 007 // Voraussetzungen: 008 // 1. 009 // MySQLConnector/J von http://dev.mysql.com/downloads herunter geladen 010 // und aus der heruntergeladenen Datei mysql-connector-java-5.1.12.zip 011 // das dort enthaltene Verzeichnis mysql-connector-java-5.1.12 irgendwohin 012 // gespeichert und im JavaEditor unter Fenster > Konfiguration > Interpreter > 013 // > Classpath-User den Pfad zu ;X:\Irgendwo\mysqlconnector\ 014 // \mysql-connector-java-5.1.12-bin.jar eingetragen. 015 // 2. 016 // Außerdem muss Xampp gestartet sein und MySQL running! 017 018 { 019 Connection con = null; 020 String Fehler = ""; 021 022 public String zeigeVoraussetzungen() 023 { 024 String v = "Dieses Programm funktioniert nur, wenn:\n" 025 +"1. auf Ihrem Computer MySQL installiert ist und läuft\n" 026 +" (weil Sie z.B. MySQL mit dem Xampp-Control-Panel gestartet haben)\n" 027 +"2. zusätzlich zu Java der MySQL-Connector vorhanden ist und gefunden wird\n" 028 +" (mysql-connector-java-5.1.12-bin.jar wurde der jar-Datei dieses Programms\n" 029 +" beigefügt. Wenn Sie selbst programmieren bzw. den Quelltext kompilieren,\n" 030 +" müssen Sie den Connector in Ihre Java-Entwicklungsumgebung einbinden!)\n" 031 +"(Mehr Info im Internet unter http://www.r-krell.de/if-db1.htm und ..db3.htm)\n"; 032 return (v); 033 } 034 035 private String sucheKontakt() 036 { 037 try 038 { 039 Class.forName("com.mysql.jdbc.Driver"); 040 con = DriverManager.getConnection 041 ("jdbc:mysql://localhost/?user=root&password="); 042 return ("Verbindung von Java zu MySQL erfolgreich erstellt.\n"); 043 } 044 catch (Exception ex) 045 { 046 con = null; 047 Fehler = "** Fehler beim Verbindungsaufbau von Java zu MySql:\n"+ex+"\n" 048 +"Die Voraussetzungen für den Betrieb dieses Programms sind nicht erfüllt!\n\n"; 049 return (Fehler); 050 } 051 } 052 ... |
Während hier in den Zeilen 007 bis 016 und 022 bis 033 nur die Voraussetzungen für den Programmierer bzw. den Nutzer genannt werden, sind die Zeilen 039 ff beachtenswert:
Tatsächlich ist die Methode sucheKontakt (Zeilen 035 bis 051) hier als private deklariert und wird nur in den folgenden Methoden benutzt bzw. aus den folgenden Methoden heraus aufgerufen.
zum Seitenanfang / zum Seitenende
2) Die Methode erzeugeDatenbank
Die folgende Methode wird aufgerufen, wenn in der Oberfläche die Schaltfläche gedrückt wird (vgl. Oberflächen-Zeile 197), um eine kleine Beispieldatenbank erstmals zu erstellen:
... 053 public String erzeugeDatenbank () 054 { 055 String a = ""; // Ausgabe-String, der an die aufrufende Stelle (Oberfläche) zurück gegeben wird 056 if (con == null) { 057 a = sucheKontakt(); 058 } 059 String ausgabe = "Datenbank wurde eingerichtet, Kundentabelle mit 3 Kunden gefüllt.\n"; 060 try 061 { 062 Statement stmt = con.createStatement(); // ein Statement (=Java-Behälter für das 063 // Verschicken eines SQL-Befehls) für die/über die Datenbank-Verbindung con wird erzeugt 064 String sqlBefehl = "create database TestJDB;"; // String mit einer SQL-Anweisung 065 int betroffeneZeilen = stmt.executeUpdate(sqlBefehl); // schickt den String mit 066 // der SQL-Anweisung zur Datenbank und erhält die Anzahl der betroffenen Zeilen zurück. 067 // "executeUpdate" für DCL-, DDL- und DML-Befehle [Datenbankkontrolle, Datenbankdefinition 068 // und Datenbankmanipulation] -- im Prinzip für alles außer für select-Abfragen] 069 a = a + " > "+sqlBefehl+" ["+betroffeneZeilen+"]\n"; // vom Befehl zurückgegebene Zahl in Ausgabe-String 070 071 sqlBefehl = "use TestJDB;"; // String mit einer SQL-Anweisung 072 betroffeneZeilen = stmt.executeUpdate(sqlBefehl); 073 a = a + " > " + sqlBefehl+" ["+betroffeneZeilen + "]\n"; 074 075 sqlBefehl = "create table kunde (kundennr int primary key auto_increment, name varchar(20), jahre int);"; 076 betroffeneZeilen = stmt.executeUpdate(sqlBefehl); 077 a = a + " > " + sqlBefehl+" ["+betroffeneZeilen+"]\n"; 078 079 sqlBefehl = "insert into kunde (name, jahre)" 080 +" values ('Meier', 17), ('Lehmann', 19), ('Schulze', 18);"; 081 betroffeneZeilen = stmt.executeUpdate(sqlBefehl); 082 a = a + " > " + sqlBefehl+" ["+betroffeneZeilen+"]\n"; 083 ausgabe = a + ausgabe; 084 } 085 catch (Exception ex) 086 { 087 ausgabe = ("** Fehler beim Versuch, die Datenbank erstmals einzurichten:\n"+ex+"\n"); // Fehlermeldung 088 } 089 return (ausgabe); 090 } 091 ... |
In der vorstehenden Methode erzeugeDatenbank werden aus Java heraus alle SQL-Befehle an den MySQL-Server geschickt, die nötig sind, um dort die Datenbank TestJDB zu erzeugen (Zeile 064), sie für künftige Befehle auszuwählen (Zeile 071), darin die Tabelle Kunde mit drei Attributen/Spalten und (ggf. automatisch vergebenem) Primärschlüssel KundenNr zu erzeugen (Zeile 075) und diese Tabelle dann mit drei Datensätzen zu füllen (Zeilen 079/080), wobei die Kundennr automatisch vergeben wird.
Damit das klappt, wird aber zunächst -- bei noch nicht vorhandene Verbindung con == null -- die in die private Methode sucheKontakt ausgelagerte Datenbankverbindung hergestellt (Zeilen 056 bis 058 bzw. vorangehender Abschnitt 1) für den Text von sucheKontakt).
Im vorstehenden Quelltext bin ich extra etwas umständlich vorgegangen, um die Übersichtlichkeit zu fördern. Zunächst wird einmal im Rahmen der Datenbankverbindung con ein Statement-Behälter stmt erzeugt (Zeile 062), der anschließend verschiedene SQL-Befehle zur Ausführung übergeben bekommt (Zeilen 065, 072, 076 und 081) und dabei jeweils die von MySQL gelieferte Antwort an die (Ganzzahl-)Variable betroffeneZeilen als Funktionswert bzw. Ergebnis zurück gibt. executeUpdate eignet sich nämlich nur für alle diejenigen SQL-Befehle, bei denen MySQL nur eine einzige Ganzzahl, nämlich die Anzahl der von der Befehlsausführung betroffenen Zeilen zurück gibt. Das sind aber nicht nur Update-, sondern (wie man hier sieht) auch Create-, Use- und viele andere Manipulationsbefehle! Den jeweiligen SQL-Befehl habe ich immer erst in die Text- bzw. String-Variable sqlBefehl geschrieben und dann diese Variable als Parameter an executeUpdate übergeben. Das gelieferte Ergebnis habe ich immer erst der Variable betroffeneZeilen zuweisen lassen, und dann diese Variable zusammen mit dem Text des ausgeführten Befehls (aus sqlBefehl) meinem Ausgabestring a hinzugefügt, der alle Meldungen und Ergebnisse für die Ausgabe in der Oberfläche sammelt (Anzeige dort im Textfeld jTaAusgabe).
Kürzer hätten jeweils drei Zeilen immer zu einer zusammengefasst werden können, z.B.
statt 071 bis 073 kurz (aber weniger übersichtlich):
a = a + " > use TestJDB;" + stmt.executeUpdate ("use
TestJDB");
In Zeile 075 wurde als Attributname übrigens extra jahre (und nicht etwa alter) verwendet: Sonst wären fast immer Anführungszeichen um den Attributnamen nötig, damit MySQL den Spaltennamen "Alter" vom SQL-Befehl alter für Änderungen in der Tabellenstruktur unterscheiden kann.
zum Seitenanfang / zum Seitenende
3) Die Methode verwendeDatenbank
Wurde die Datenbank mit der Kundentabelle schon in einer früheren Sitzung erzeugt, kann sie
weiter verwendet werden -- dazu muss in der Oberfläche nur der Knopf gedrückt werden (siehe Oberflächen-Zeile 201):
... 092 public String verwendeDatenbank() 093 { 094 String a = ""; 095 if (con == null) { 096 a = sucheKontakt(); 097 } 098 try 099 { 100 Statement stmt = con.createStatement(); // ein Statement (=Java-Behälter für SQL) 101 String sqlBefehl = "use TestJDB"; // String mit einer SQL-Anweisung 102 int betroffeneZeilen = stmt.executeUpdate(sqlBefehl); 103 a = a + " > " + sqlBefehl+" ["+betroffeneZeilen+"]\n"; 104 } 105 catch (Exception ex) 106 { 107 a = a + "** Fehler -- use-Befehl gelang nicht: "+ex; 108 } 109 return (a); 110 } 111 ... |
Im Wesentlichen wird in der vorstehenden Methode nur der Kontakt zur Datenbank aufgebaut (sofern noch nicht geschehen; Zeilen 095 bis 097) und dann -- wie in 062 und 071 bis 073 -- der use-Befehl ausgeführt und die Zahl der betroffenen Zeilen zur Kontrolle über den Ausgabestring a an die aufrufende Stelle (nämlich die Oberfläche, dort Oberflächen-Zeile 201) gemeldet.
zum Seitenanfang / zum Seitenende
4) Die Methoden neuerKundeAuto und neuerKunde
Jetzt folgt die Methode, die die Arbeit erledigt, falls in die Eingabefelder (Textfelder) der Oberfläche die Daten eines neuen Kunden eingegeben wurden und dort die Schaltfläche zum Aufnehmen des neuen Kunden gedrückt wird. Der Aufruf erfolgt aus der Oberfläche (s.o.), Oberflächen-Zeilen 208 bis 245, bzw. insbesondere in Oberflächen-Zeile 224.
... 112 public String neuerKundeAuto (String name, int alter) // Kunde ohne Nr. erhält automatisch eine! 113 { 114 String a; 115 String ausgabe = "Kunde wurde aufgenommen.\n"; 116 try 117 { 118 Statement stmt = con.createStatement(); 119 int betroffeneZeilen; 120 String sqlBefehl; 121 sqlBefehl = "insert into kunde (name, jahre)" 122 +" values ('"+name+"',"+alter+")"; 123 betroffeneZeilen = stmt.executeUpdate(sqlBefehl); 124 a = " > "+sqlBefehl+" ["+betroffeneZeilen+"]\n"; 125 ausgabe = a + ausgabe; 126 } 127 catch (Exception ex) 128 { 129 ausgabe = ("** Fehler bei der Aufnahme von ('"+name+"', "+alter+"):\n"+ex+"\n"); // Fehlermeldung 130 } 131 return (ausgabe); 132 } 133 ... |
Die vorstehenden Methode wird benutzt, wenn das Eingabefeld für die Kundennummer frei gelassen wurde und deshalb die Kundennummer von MySQL automatisch vergeben werden muss (dies geht, da in Zeile 075 auto_increment angegeben war). Das Einfügen von Werten erfolgt dann nur für die Spalten/Attribute name und jahre wie man in Zeile 121 sieht. Auch der insert-Befehl gibt wie alle bisher verwendeten SQL-Anweisungen nur die Anzahl der betroffenen Zeilen zurück, wird also mit executeUpdate von Java aus an die Datenbank übermittelt. Der Quelltext in den Zeilen 120 bis 124 könnte -- wie schon oben bei 071 bis 073 beschrieben -- zu einer Zeile verkürzt werden.
Hat der Benutzer hingegen eine Kundennummer ins entsprechende Eingabefeld eingetippt und wurde diese
von der Oberfläche als Ganzzahl erkannt und verwandelt, dann wird die folgende Variante der Methode aufgerufen (aus der Oberflächen-Zeile
242 heraus):
... 134 public String neuerKunde (int kdnr, String name, int alter) // Kunde mit kdnr 135 { 136 String a; 137 String ausgabe = "Kunde wurde aufgenommen.\n"; 138 try 139 { 140 Statement stmt = con.createStatement(); // ein Statement (=Java-Behälter für SQL) 141 int betroffeneZeilen; 142 String sqlBefehl; 143 sqlBefehl = "insert into kunde (kundennr, name, jahre)" 144 +" values ("+kdnr+",'"+name+"',"+alter+")"; 145 betroffeneZeilen = stmt.executeUpdate(sqlBefehl); 146 a = " > "+sqlBefehl+" ["+betroffeneZeilen+"]\n"; 147 ausgabe = a + ausgabe; 148 } 149 catch (Exception ex) 150 { 151 ausgabe = ("** Fehler bei der Aufnahme von ("+kdnr+", '"+name+"', "+alter+"):\n"+ex+"\n"); // Fehlermeldung 152 } 153 return (ausgabe); 154 } 155 ... |
Bei der Ausführung kommt es natürlich zu einem Fehler (der als Ausgabe im Oberflächenfenster erscheint und nicht zum Programmabbruch führt), wenn der Benutzer eine bereits verwendete Kundennummer eingegeben hat, da in Zeile 075 die Kundennummer als Primärschlüssel gekennzeichnet wurde und daher eindeutig ein muss.
zum Seitenanfang / zum Seitenende
5) Die Methoden aufgefüllt, abfrageMit und abfrageOhne
Bisher wurden nur SQL-Befehle (mit executeUpdate) an die Datenbank weiter gegeben, die als Ergebnis bzw. Funktionswert nur die Anzahl der betroffenen Zeilen liefern. Will man hingegen eine Select-Abfrage ausführen, erwartet man als Rückgabe eine Ergebnistabelle. Leider ist die Datenbank-Anbindung in Java nicht ganz so komfortabel wie in Delphi: Dort kann man mit TTable, TDataSource und DBGrid Komponenten verwenden, die Datenbanktabellen praktisch automatisch anzeigen und sogar eine Änderung der Datenbanktabelle durch Änderungen in der Oberfläche ermöglichen. Natürlich ist in Java beispielsweise die Verwendung eines jTable-Objekts möglich (in diesem Fall würde sich empfehlen, das jTable-Objekt der Oberfläche als Parameter an die folgenden Methoden zu übergeben, damit es dort eingerichtet und gefüllt werden kann). Allerdings muss der Programmierer durch eigenen Programmtext aus der Rückgabe des executeQuery-Befehls die Zahl der Spalten und Zeilen der Tabelle ermitteln, das jTable-Objekt entsprechend erzeugen bzw. abändern und den einzelnen Tabellenfeldern dann auch einzeln (bzw. in geeigneten Schleifen) die entsprechenden Werte der Antworttabelle zuweisen. Dann stehen dem Benutzer aber auch automatisch viele übliche Tabellendarstellungsfunktionen zur Verfügung, beispielsweise werden die Zeilen beim Klick auf eine Spaltenüberschrift nach den Werten in dieser Spalte sortiert. Wenn man aber will, dass Änderungen des Benutzers an den dargestellten Tabellenwerten umgekehrt auch wieder in die Datenbank übernommen werden, ist das mühsame Schreiben von viel weiterem Programmtext nötig. Im Internet findet man entsprechende Foren-Beiträge. Mag sein, dass manche Entwicklungsumgebungen die Schwächen Javas ausgleichen und vorbereiteten Quelltext für entsprechende Methoden liefern -- der hier verwendete Javaeditor tut es jedenfalls nicht. Und da dieser Beitrag eher die grundsätzlichen Möglichkeiten aufzeigen will, als wichtige Befehle durch Oberflächenkosmetik zu verschleiern, habe ich mich entschlossen, die Antworttabellen mit ein paar zusätzlichen Strichen im TextArea-Feld auszugeben, in dem schon die normale Programmausgabe erfolgt -- und gar kein jTable-Objekt zu verwenden. Neue Spalten könnten durch Tabulatorsprünge angesteuert werden; unterscheiden sich aber die Werte so stark, dass ihre Längen mehrere Tabulatormarken (im Abstand von 8 Zeichen) Unterschied haben, ist das Verwenden einer passenden Anzahl von Leerzeichen (bei Schrift mit fester gleicher Buchstabenbreite) sinnvoller. Die konstante Buchstabenbreite wurde oben in der Oberfläche in der Oberflächen-Zeile 180 eingestellt ("monospaced"), eine Methode zum Abschneiden des Texts nach der bzw. Auffüllen des Texts mit anschließenden Leerzeichen bis zur angegebenen Zahl von Zeichen ist nachstehend genannt:
... 156 private String aufgefüllt (String text, int zahl) 157 { 158 text = (text+" ") 159 .substring(0, zahl-1); 160 return (text+"|"); 161 } 162 ... |
Die Methode aufgefüllt fügt außerdem am Ende des Ausgabetexts einen senkrechten Strich als Spaltenbegrenzung an. Sie wird bei der Ergebnisausgabe in der nun folgenden Methode abfrageMit verwendet. Auch die folgende Methode ist private deklariert, da sie nicht direkt aus der Oberfläche, sondern nur weiter unten aus der Funktions-Methode führeSQLBefehlAus (Zeilen 226 ff) aufgerufen wird. Sie übernimmt aber die Weiterleitung eines Select-Befehls an die Datenbank und interpretiert die Rückgabe bzw. bereitet die Rückgabe so auf, dass ein mehrzeiliger Text mit der Darstellung einer Tabelle entsteht. Um die Möglichkeit zu zeigen, wurde als Kopf jeder Spalte immer die Spaltenüberschrift (Attribut-Name), der Attributtyp von MySQL und die Spaltenbreite (als maximale Anzahl der Zeichen) 3-zeilig notiert, bevor -- getrennt durch eine gestrichelte Linie -- darunter die Tabelleneinträge folgen (wie auch im Bild weiter oben auf dieser Seite [Kapitel Oberfläche, Abschnitt 2) "Das Aussehen der Oberfläche"] zu sehen ist).
163 private String abfrageMit (String sql) // für Select-Abfrage o.ä. mit komplexem
Tabellen-Ergebnis 164 { 165 String ausgabe = " > "+sql+":\n"; 166 try 167 { 168 Statement stmt = con.createStatement(); // ein Statement 169 ResultSet result = stmt.executeQuery(sql); 170 ResultSetMetaData rsmd = result.getMetaData(); 171 int spaltenzahl = rsmd.getColumnCount(); 172 173 for (int i=1; i<=spaltenzahl; i++) 174 { 175 ausgabe = ausgabe + aufgefüllt(rsmd.getColumnName(i), rsmd.getColumnDisplaySize(i)); 176 } 177 ausgabe = ausgabe+"\n"; 178 for (int i=1; i<=spaltenzahl; i++) 179 { 180 ausgabe = ausgabe + aufgefüllt(rsmd.getColumnTypeName(i), rsmd.getColumnDisplaySize(i)); 181 } 182 ausgabe = ausgabe+"\n"; 183 for (int i=1; i<=spaltenzahl; i++) 184 { 185 ausgabe = ausgabe + aufgefüllt("("+rsmd.getColumnDisplaySize(i)+")", rsmd.getColumnDisplaySize(i)); 186 } // Spaltenbreite 187 ausgabe = ausgabe+"\n"; 188 189 for (int i=1; i<=spaltenzahl; i++) 190 { 191 ausgabe = ausgabe + aufgefüllt("------------------------------------------------------------", rsmd.getColumnDisplaySize(i)); 192 } 193 ausgabe = ausgabe+"\n"; 194 while(result.next()) // solange das resultSet noch weitere Zeilen enthält 195 { 196 for (int i=1; i<=spaltenzahl; i++) 197 { 198 ausgabe = ausgabe + aufgefüllt(result.getString(i), rsmd.getColumnDisplaySize(i)); 199 } 200 ausgabe = ausgabe+"\n"; 201 } 202 } 203 catch (Exception ex) 204 { 205 ausgabe = ("** Fehler bei der Abfrage '"+sql+"':\n"+ex+"\n"); // Fehlermeldung 206 } 207 return (ausgabe); 208 } 209 ... |
Neu ist in der vorstehenden Methode:
Natürlich hilft -- wie immer bei Java -- ein Blick in die API der Java-Dokumentation, um die Möglichkeiten der Bibliotheksklassen und ihre Attribute und Methoden kennen zu lernen (s.u.).
Während die vorstehende Methode Select-Befehle mit umfangreicher Ausgabe ausführt, ist die folgende Methode für SQL-Befehle ohne Tabellen-Rückgabe, sondern mit einfachem Zahlergebnis gedacht:
... 210 private String abfrageOhne (String sql) // für SQL-Befehle ohne umfangreiche Rückgabe, sondern mit einfachem Ganzzahl-Ergebnis 211 { 212 String ausgabe = ""; 213 try 214 { 215 Statement stmt = con.createStatement(); // ein Statement 216 int betroffeneZeilen = stmt.executeUpdate(sql); 217 ausgabe = " > "+sql+" ["+betroffeneZeilen+"]\n"; 218 } 219 catch (Exception ex) 220 { 221 ausgabe = ("** Fehler bei der Abfrage '"+sql+"':\n"+ex+"\n"); // Fehlermeldung 222 } 223 return (ausgabe); 224 } 225 ... |
Wenn das Ergebnis nur aus einer Zahl besteht, und nicht aus vielen Werten erst ein formatierter Text erstellt werden muss, ist die vorstehende Methode abfrageOhne natürlich viel einfacher als die davor aufgeführte Methode abfrageMit und funktioniert in Zeile 216 mit der aus den Abschnitten 1) bis 4) schon wohl bekannten executeUpdate-Methode des Statements stmt.
zum Seitenanfang / zum Seitenende
6) Die Methode führeSQLBefehlAus
Je nachdem, ob der Benutzer in das für SQL-Befehle vorgesehene Eingabefeld der Oberfläche nun einen Select- oder einen anderen SQL-Befehl eingegebenen hat, entscheidet nach dem Druck auf die folgende Methode führeSQLBefehlAus in Zeile 228, welche der beiden vorstehenden privaten Methoden sie nutzt. Die Oberfläche ruft diese Methode in der Oberflächen-Zeile 261 auf:
... 226 public String führeSQLBefehlAus (String sqlBefehl) 227 { 228 if (sqlBefehl.substring(0,6).compareToIgnoreCase("select")==0) 229 { 230 return (abfrageMit (sqlBefehl)); 231 } 232 else 233 { 234 return (abfrageOhne (sqlBefehl)); 235 } 236 } 237 ... |
Weil evtl. führende Leerzeichen nicht vor der Kontrolle entfernt werden, kann damit versucht werden, einen Select-Befehl durch den Java-executeUpdate-Befehl in abfrageOhne ausführen zu lassen, was natürlich zu einer Java-Exception führt.
zum Seitenanfang / zum Seitenende
7) Die Methode selectAll
Die vorletzte Methode in der Klasse wird schließlich angestoßen, wenn sich der Benutzer mit die Tabelle Kunde zeigen lässt (vgl. Oberflächen-Zeile 205). Sie startet einfach eine entsprechende Select-Abfrage über die im Abschnitt 5) genannte Methode abfrageMit (Zeilen 163 bis 208):
... 238 public String selectAll() 239 { 240 return (abfrageMit ("select * from kunde;")); 241 } 242 ... |
zum Seitenanfang / zum Seitenende
8) Die Methode beendeKontakt
Und schließlich noch die Methode beendeKontakt, die das grundsätzliche
Gegenstück zu sucheKontakt (Zeilen 035 bis 051) ist, aber hier nie wirklich aufgerufen/verwendet wird:
... 243 public void beendeKontakt() 244 { 245 if(con != null) 246 { 247 try 248 { 249 con.close(); // Schließt Verbindung 250 } 251 catch (Exception ex) 252 { 253 System.out.println("Verbindung kann nicht getrennt werden: "+ex); 254 } 255 } 256 } 257 } // Ende der Klasse DBJ_JFunktion |
zum Seitenanfang / zum Seitenende
Der Programmstart
Beim Start des Programms zeigt sich die Oberfläche (da die in der Oberflächen-Klasse enthaltene main-Methode die Oberfläche startet, die ihrerseits ein Objekt der Klasse DBJ_JFunktion erzeugt und verwendet -- siehe Quelltext der Oberfläche, Oberflächen-Zeilen 271 bis 273 sowie Oberflächen-Zeile 040).
Bevor irgendwelche Knöpfe (außer der Schaltfläche "Voraussetzungen") gedrückt werden, muss MySQL z.B. über das XAMPP-Control-Panel (hier in den Vordergrund geholt) gestartet sein. Nur wenn der Server läuft, kann der in Java geschriebene Datenbank-Client Kontakt zu MySQL aufnehmen! Wird dann "TestJDB neu erstellen" oder "TestJDB benutzen" gedrückt, also die Verbindung zur Datenbank aufgenommen, merkt dies eine eventuell installierte Firewall und der Vorgang muss erlaubt werden.
Eine Verwendung des Programms als (J)Applet ist übrigens nicht möglich: Die Sicherheitseinstellungen von Java verbieten, dass ein Applet aus dem Internet Dateien auf dem lokalen Rechner liest oder Kontakt zur lokalen Datenbank aufnimmt. Aber der Webstart (mit erweiterten Rechten) oder der Download sind möglich:
Wenn die Java-JRE installiert ist und MySQL läuft, können Sie das Programm jetzt sofort ausführen:
Hier klicken zum Webstart des Datenbank-Programms |
Danach Öffnen von
Datenbank_J.jnlp [mit Java(TM) Webstart Launcher (Standard)] wählen/bestätigen
und bei evtl. Sicherheitswarnung vor der Ausführung von DBJ_JStart das Risiko akzeptieren und OK
drücken.
Wenn Sie außerdem gefragt werden, ob Sie eine Desktop- und eine
Startmenüverknüpfung anlegen wollen,
sollten Sie zustimmen, wenn Sie das Programm auch nach Verlassen dieser Webseite nochmal ausprobieren wollen.
Wird nach dem Klick Datenbank_J.jnlp herunter geladen oder werden Sie
gefragt, womit Sie Datenbank_J.jnlp öffnen wollen
oder ob/wohin Sie die Datei speichern wollen, haben Sie entweder kein Java installiert oder ihr Browser (z.B. Opera) unterstützt keinen
Webstart.
Im letzten Fall empfiehlt sich nachfolgend der Download der .jar-Datei:
Sie können das Datenbankprogramm auch als ausführbare Java-Datei herunter laden und später lokal auf Ihrem Rechner starten (Java-Umgebung JRE und laufender MySQL-Server vorausgesetzt):
Datenbank_per_Java(r-krell.de).jar (756 kB)
Die Datei muss auf dem eigenen Rechner gespeichert (nicht geöffnet und nicht entpackt) werden
und lässt sich dann dort ausführen. Bei installiertem Java 1.6 oder höher reicht zum Start ein Klick auf den Namen der gespeicherten
Datei. Enthalten ist das hier im Quelltext gezeigte Programm in kompilierter Form für Anschauungs-, Lehr- oder private Übungszwecke, ohne
Haftung und ohne Gewährleistung. Die Verwendung meiner jar-Datei zu kommerziellen Zwecken, der Einbau herunter geladener Teile in eigene
Programme usw. ist entsprechend dem Urheberrecht verboten!
Ändert man im Quelltext von DBJ_JFunktion in der Zeile 041 die Adresse der Datenbank von localhost in die IP-Adresse eines
anderen Rechners, auf dem MySQL läuft, so sollte damit -- wie oben im Abschnitt 1) bei den Anmerkungen zur Zeile 040 bereits erwähnt --
auch der Kontakt zu einer Datenbank im Netz möglich sein; allerdings habe ich das noch nicht ausprobiert. Gleiches müsste übrigens
auch für den MySQL Query Browser gelten, wenn im folgenden Kapitel dort im Feld 'Server Host:' statt localhost eine IP-Adresse
eingetragen wird!
zum Seitenanfang / zum Seitenende
Alternativer Zugriff auf die gleiche
Datenbank
zum Beispiel mit dem MySQL Query
Browser
Wie oben schon im Eingangskapitel "Datenbanken und Java.." im Abschnitt 2) "Datenbankformate als gute Wahl bei allen komplexen Speicherungen" erwähnt, besteht ein großer Vorteil bei der Speicherung von Daten in Datenbanken darin, dass man nicht nur mit einem bestimmten Programm, sondern aus vielen Anwendungen auf die Daten zugreifen kann.
Beispielsweise kann zusätzlich zum vorgestellten Java-Programm der von meinen ersten beiden Datenbank-Seiten bekannte "MySQL Query Browser" gestartet werden und ebenfalls auf die durch das hier vorgestellte Java-Programm in MySQL erzeugte Beispieldatenbank TestJDB zugreifen und sie auch verändern!
Zunächst muss man sich auch mit diesem Client beim MySQL-Server (der natürlich weiterhin laufen muss) anmelden:
So kann man schön die vielfache und damit zukunftssichere Verwendbarkeit des eigenen Datenbestands sehen!
zum Seitenanfang / zum Seitenende
Die API-Spezifikation der Java-Dokumentation
Wer weitere Informationen sucht, wie man aus Java mit Datenbanken kommunizieren kann, findet Hilfe und Tipps in Büchern, im Internet, aber nicht zuletzt auch in der Java-Dokumentation (API = Application Programming Interface = Schnittstelle für die Programmierung von Anwendungen). Wurde die getrennt herunter zu ladende Java-Dokumentation installiert [vgl. meine Seite "Informatik mit Java, Teil a)"] und der Pfad dem Javaeditor mitgeteilt [ebenfalls Teil a), "Installation und Einrichten des Javaeditors", dann dort weiter zu (3)], kann die Hilfe bequem angesehen werden. Befindet sich der Kursor in einem Klassennnamen (im Beispiel stand die Schreibmarke des Javaeditors im Wort "ResultSetMetaData" aus Zeile 170 von DBJ_JFunktion), so reicht der Klick auf F1, um die entsprechende Seite der Dokumentation zu öffnen. Die Java-Dokumentation ist auch online auf http://java.sun.com/javase/6/docs/api/ zu finden (Stand Anfang August 2010; wieweit sich durch die Übernahme von Sun durch Oracle auch hier bald URLs ändern, bleibt abzuwarten. Einige Leitseiten sind bereits verändert, so findet sich z.B. die Übersicht über die Dokumentationen der verschiedenen Java-Versionen schon jetzt nicht mehr unter www.java.sun..., sondern auf http://www.oracle.com/technetwork/java/javase/documentation/api-jsp-136079.html).
Insofern ist eine Erweiterung der hier gezeigten Grundlagen bzw. eine Anpassung des vorgestellten Programms an eigene Wünsche sicher leicht möglich...
zurück zur Informatik-Hauptseite
zur vorangehenden Seite: Datenbanken, Teil 1
zur vorangehenden Seite: Datenbanken, Teil 2