6. Datenbankverwaltung mit der JDBC-API
6.1. Übersicht
Es gibt viele Datenbanken auf dem Markt. Um den Datenbankzugriff unter MS Windows zu standardisieren, hat Microsoft eine Schnittstelle namens ODBC (Open DataBase Connectivity) entwickelt. Diese Schicht verbirgt die spezifischen Merkmale der einzelnen Datenbanken hinter einer Standardschnittstelle. Unter MS Windows stehen viele ODBC-Treiber zur Verfügung, die den Datenbankzugriff erleichtern. Hier ist zum Beispiel eine Liste der auf einem Win95-Rechner installierten ODBC-Treiber:

Eine Anwendung, die auf diese Treiber zurückgreift, kann jede der oben genannten Datenbanken ohne Änderungen nutzen.

Damit auch Java-Anwendungen die Vorteile der ODBC-Schnittstelle nutzen können, hat Sun die JDBC-Schnittstelle (Java Database Connectivity) entwickelt, die als Vermittler zwischen der Java-Anwendung und der ODBC-Schnittstelle fungiert:

6.2. Wichtige Schritte bei Datenbankoperationen
6.2.1. Einführung
In einer Java-Anwendung, die eine Datenbank mit der JDBC-Schnittstelle nutzt, sind im Allgemeinen die folgenden Schritte erforderlich:
- Verbindung zur Datenbank herstellen
- Senden von SQL-Abfragen an die Datenbank
- Empfangen und Verarbeiten der Ergebnisse dieser Abfragen
- Schließen der Verbindung
Die Schritte 2 und 3 werden wiederholt ausgeführt, wobei die Verbindung erst am Ende der Datenbankoperationen geschlossen wird. Dies ist ein relativ gängiges Muster, das Ihnen vielleicht bekannt ist, wenn Sie bereits interaktiv mit einer Datenbank gearbeitet haben. Wir werden jeden dieser Schritte anhand eines Beispiels näher erläutern. Wir betrachten eine Access-Datenbank namens ARTICLES mit folgender Struktur:
name | Typ |
Code | 4-stelliger Artikelcode |
Name | sein Name (Zeichenkette) |
Preis | sein Preis (tatsächlich) |
aktueller_Bestand | aktueller Lagerbestand (Ganzzahl) |
min_stock | der Mindestbestand (Ganzzahl), unterhalb dessen der Artikel nachbestellt werden muss |
Diese ACCESS-Datenbank ist im ODBC-Datenbankmanager als „Benutzer“-Datenquelle definiert:


Die Eigenschaften werden über die Schaltfläche „Konfigurieren“ wie folgt festgelegt:

Diese Konfiguration besteht im Wesentlichen darin, die Datenbank „ARTICLES“ mit der dieser Datenbank entsprechenden Access-Datei „articles.mdb“ zu verknüpfen. Sobald dies geschehen ist, ist die Datenbank „ARTICLES“ für Anwendungen über die ODBC-Schnittstelle zugänglich.
6.2.2. Der Verbindungsschritt
Um eine Datenbank zu nutzen, muss eine Java-Anwendung zunächst eine Verbindung herstellen. Dies geschieht mithilfe der folgenden Klassenmethode:
wobei
DriverManager: eine Java-Klasse, die die Liste der für die Anwendung verfügbaren Treiber enthält
Connection: eine Java-Klasse, die eine Verbindung zwischen der Anwendung und der Datenbank herstellt, über die die Anwendung SQL-Abfragen an die Datenbank sendet und Ergebnisse empfängt
URL: Name, der die Datenbank identifiziert. Dieser Name entspricht den Internet-URLs. Deshalb ist er Teil der URL-Klasse. Das Internet spielt hier jedoch überhaupt keine Rolle. Die URL hat folgende Form:
**jdbc:treibername:quellenname;param=val1;param2=val2**
In unseren Beispielen, in denen wir ausschließlich ODBC-Treiber verwenden, heißt der Treiber **„odbc**“. Der dritte Teil der URL besteht aus dem Quellennamen und eventuellen Parametern. In unseren Beispielen handelt es sich dabei um dem System bekannte ODBC-Quellen. Die URL für die zuvor definierte Datenquelle *„Articles“* lautet somit
**jdbc:odbc:Articles**
id: Benutzer-ID (Login)
mdp: Benutzerkennwort
Zusammenfassend lässt sich sagen, dass das Programm eine Verbindung zu einer Datenbank herstellt:
- die durch einen Namen (URL) identifiziert wird
- unter den Anmeldedaten eines Benutzers (ID, Passwort)
Wenn diese drei Parameter korrekt sind und wenn der Treiber vorhanden ist, der die Verbindung zwischen der Java-Anwendung und der angegebenen Datenbank herstellen kann, wird eine Verbindung zwischen der Java-Anwendung und der Datenbank hergestellt. Diese Verbindung wird im Programm durch das Connection-Objekt dargestellt, das von der DriverManager-Klasse zurückgegeben wird. Da diese Verbindung aus verschiedenen Gründen fehlschlagen kann, kann es zu einer Ausnahme kommen. Wir schreiben daher:
Connection connexion=null;
URL base=...;
String id=...;
String mdp=...;
try{
connexion=DriverManager.getConnection(base,id,mdp);
} catch (Exception e){
// traiter l’exception
}
Um eine Verbindung zu einer Datenbank herzustellen, benötigen Sie den entsprechenden Treiber. In unseren Beispielen ist dies der ODBC-Treiber, der die angeforderte Datenbank verarbeiten kann. Dieser Treiber muss zwar in der Liste der ODBC-Treiber auf dem Rechner verfügbar sein, Sie benötigen jedoch auch die Java-Klasse, die als Schnittstelle dazu dient. Dazu fordert die Anwendung die erforderliche Klasse wie folgt an:
Die Klasse Class** steht in keinerlei Zusammenhang mit der JDBC-Schnittstelle. Es handelt sich um eine allgemeine Klasse zur Klassenverwaltung. Ihre statische Methode forName ermöglicht das dynamische Laden einer Klasse, wodurch deren statische Attribute und Methoden verfügbar werden. Die Klasse, die als Schnittstelle zu den MS Windows ODBC-Treibern dient, heißt „sun.jdbc.odbc.JdbcOdbcDriver**“. Wir würden daher schreiben (die Methode kann eine Ausnahme auslösen):
try{
Class.forName(« sun.jdbc.odbc.JdbcOdbcDriver ») ;
} catch (Exception e){
// handle exception (non-existent class)
}
Die für die JDBC-Schnittstelle erforderlichen Klassen befinden sich im Paket „java.sql“. Daher schreiben wir zu Beginn des Programms:
Hier ist ein Programm, mit dem Sie eine Verbindung zu einer Datenbank herstellen können:
import java.sql.*;
import java.io.*;
// call: pg PILOTE URL UID MDP
// connects to the URL database using class JDBC PILOTE
// user UID is identified by password MDP
public class connexion1{
static String syntaxe="pg PILOTE URL UID MDP";
public static void main(String arg[]){
// check number of arguments
if(arg.length<2 || arg.length>4)
erreur(syntaxe,1);
// connection to base
Connection connect=null;
String uid="";
String mdp="";
if(arg.length>=3) uid=arg[2];
if(arg.length==4) mdp=arg[3];
try{
Class.forName(arg[0]);
connect=DriverManager.getConnection(arg[1],uid,mdp);
System.out.println("Connexion avec la base " + arg[1] + " établie");
} catch (Exception e){
erreur("Erreur " + e,2);
}
// closing the base
try{
connect.close();
System.out.println("Base " + arg[1] + " fermée");
} catch (Exception e){}
}// hand
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
}// class
Hier ist ein Ausführungsbeispiel:
E:\data\java\jdbc\0>java connexion1 sun.jdbc.odbc.JdbcOdbcDriver jdbc:odbc:articles
Connexion avec la base jdbc:odbc:articles établie
Base jdbc:odbc:articles fermée
6.2.3. Abfragen an die Datenbank senden
Die JDBC-Schnittstelle ermöglicht das Senden von SQL-Abfragen an die mit der Java-Anwendung verbundene Datenbank sowie die Verarbeitung der Ergebnisse dieser Abfragen. SQL (Structured Query Language) ist eine standardisierte Abfragesprache für relationale Datenbanken. Es gibt verschiedene Arten von Abfragen:
- Datenbankabfrageanweisungen (SELECT)
- Datenbank-Aktualisierungsabfragen (INSERT, DELETE, UPDATE)
- Abfragen zum Anlegen/Löschen von Tabellen (CREATE, DELETE)
Wir gehen hier davon aus, dass der Leser mit den Grundlagen der Sprache SQL vertraut ist.
6.2.3.1. Die Statement-Klasse
Um eine SQL-Abfrage an eine Datenbank zu senden, muss die Java-Anwendung über ein Objekt vom Typ Statement verfügen. Dieses Objekt speichert unter anderem den Text der Abfrage. Dieses Objekt ist zwangsläufig mit der aktuellen Verbindung verknüpft. Es handelt sich daher um eine Methode der hergestellten Verbindung, die es ermöglicht, die für die Ausführung von SQL-Abfragen erforderlichen Statement-Objekte zu erstellen. Wenn connection das Objekt ist, das die Verbindung zur Datenbank darstellt, wird ein Statement-Objekt wie folgt abgerufen:
Sobald ein Statement-Objekt erstellt wurde, können SQL-Abfragen ausgeführt werden. Dies geschieht unterschiedlich, je nachdem, ob es sich um eine Abfrage zum Abrufen von Daten oder zum Aktualisieren der Datenbank handelt.
6.2.3.2. Ausführen einer Abfrage zum Abrufen von Daten aus der Datenbank
Eine Abfrage hat in der Regel folgende Form:
Nur die Schlüsselwörter in der ersten Zeile sind erforderlich; die anderen sind optional. Es gibt weitere Schlüsselwörter, die hier nicht aufgeführt sind.
- Eine Verknüpfung wird mit allen Tabellen durchgeführt, die nach dem Schlüsselwort `FROM` aufgeführt sind
- Es werden nur die Spalten beibehalten, die auf das Schlüsselwort `select` folgen
- Es werden nur die Zeilen beibehalten, die die Bedingung des `where`-Schlüsselworts erfüllen
- Die resultierenden Zeilen, sortiert nach dem Ausdruck im Schlüsselwort `ORDER BY`, bilden das Ergebnis der Abfrage.
Das Ergebnis eines SELECT ist eine Tabelle. Wenn wir die vorherige ARTICLES-Tabelle betrachten und die Namen der Artikel abrufen möchten, deren aktueller Lagerbestand unter dem Mindestschwellenwert liegt, würden wir schreiben: SELECT name FROM articles WHERE current\_stock < min\_stock*. Wenn wir sie alphabetisch nach Namen sortiert haben möchten, würden wir schreiben: select name from articles where current_stock < minimum_stock order by name*
Um diese Art von Abfrage auszuführen, stellt die Statement-Klasse die Methode executeQuery zur Verfügung:
wobei query der Text der auszuführenden SELECT-Abfrage ist.
Wenn also
- „connection“ das Objekt ist, das die Verbindung zur Datenbank darstellt
- erstellt Statement s = connection.createStatement() das Statement-Objekt, das zur Ausführung von SQL-Abfragen benötigt wird
- ResultSet rs = s.executeQuery("select name from articles where current_stock < minimum_stock") führt eine SELECT-Abfrage aus und weist die Ergebniszeilen der Abfrage einem Objekt vom Typ ResultSet zu.
6.2.3.3. Die ResultSet-Klasse: Ergebnis einer SELECT-Abfrage
Ein ResultSet-Objekt repräsentiert eine Tabelle, d. h. eine Menge von Zeilen und Spalten. Zu jedem Zeitpunkt ist nur auf eine Zeile der Tabelle zugegriffen werden, die als aktuelle Zeile bezeichnet wird. Bei der anfänglichen Erstellung des ResultSet ist die aktuelle Zeile Zeile Nr. 1, sofern das ResultSet nicht leer ist. Um zur nächsten Zeile zu wechseln, stellt die ResultSet-Klasse die Methode next zur Verfügung:
Diese Methode versucht, zur nächsten Zeile im ResultSet zu springen, und gibt bei Erfolg „true“ zurück, andernfalls „false“. Bei Erfolg wird die nächste Zeile zur neuen aktuellen Zeile. Die vorherige Zeile geht verloren und kann nicht wiederhergestellt werden. Die ResultSet-Tabelle enthält die Spalten col1, col2, ... Um auf die verschiedenen Felder der aktuellen Zeile zuzugreifen, stehen die folgenden Methoden zur Verfügung:
, um das Feld „coli“ aus der aktuellen Zeile abzurufen. „Type“ bezieht sich auf den Typ des Feldes „coli“. Die Methode getString wird häufig für alle Felder verwendet, wodurch Sie den Inhalt des Feldes als Zeichenkette abrufen können. Diese können Sie dann bei Bedarf konvertieren. Wenn Sie den Spaltennamen nicht kennen, können Sie die Methoden
verwenden, wobei i der Index der gewünschten Spalte ist (i >= 1).
6.2.3.4. Ein erstes Beispiel
Hier ist ein Programm, das den Inhalt der zuvor erstellten Datenbank ARTICLES anzeigt:
import java.sql.*;
import java.io.*;
// displays the contents of a ARTICLES system database
public class articles1{
static final String DB="ARTICLES"; // database to exploit
public static void main(String arg[]){
Connection connect=null; // connection to base
Statement S=null; // purpose of queries
ResultSet RS=null; // query result table
try{
// base connection
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connect=DriverManager.getConnection("jdbc:odbc:"+DB,"","");
System.out.println("Connexion avec la base " + DB + " établie");
// creation of a Statement object
S=connect.createStatement();
// execute a select query
RS=S.executeQuery("select * from " + DB);
// using the results table
while(RS.next()){ // as long as there's a line to operate
// it is displayed on screen
System.out.println(RS.getString("code")+","+
RS.getString("nom")+","+
RS.getString("prix")+","+
RS.getString("stock_actu")+","+
RS.getString("stock_mini"));
}// next line
} catch (Exception e){
erreur("Erreur " + e,2);
}
// closing the base
try{
connect.close();
System.out.println("Base " + DB + " fermée");
} catch (Exception e){}
}// hand
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
}// class
Die Ergebnisse lauten wie folgt:
Connexion avec la base ARTICLES établie
a300,vélo,1202,30,2
d600,arc,5000,10,2
d800,canoé,1502,12,6
x123,fusil,3000,10,2
s345,skis nautiques,1800,3,2
f450,essai3,3,3,3
f807,cachalot,200000,0,0
z400,léopard,500000,1,1
g457,panthère,800000,1,1
Base ARTICLES fermée
6.2.3.5. Die Klasse ResultSetMetadata
Im vorherigen Beispiel kennen wir die Namen der ResultSet-Spalten. Wenn wir diese nicht kennen, können wir die Methode getType(column_name) nicht verwenden. Stattdessen verwenden wir getType(column_number). Um jedoch alle Spalten zu erhalten, müssten wir wissen, wie viele Spalten das ResultSet hat. Die ResultSet-Klasse stellt diese Information nicht bereit. Es ist die ResultSetMetaData-Klasse, die sie bereitstellt. Allgemeiner gesagt liefert diese Klasse Informationen über die Struktur der Tabelle, d. h. über die Art ihrer Spalten.
Wir greifen auf Informationen über die Struktur eines ResultSet zu, indem wir zunächst ein ResultSetMetaData-Objekt instanziieren. Wenn RS ein ResultSet ist, wird das zugehörige ResultSetMetaData wie folgt abgerufen:
Die Klasse ResultSetMetaData enthält zwei nützliche Methoden:
- int getColumnCount(), die die Anzahl der Spalten im ResultSet zurückgibt
- String getColumnLabel(int i), die den Namen der Spalte i im ResultSet zurückgibt (i >= 1)
6.2.3.6. Ein zweites Beispiel
Das vorherige Programm hat den Inhalt der Datenbank ARTICLES angezeigt. Hier schreiben wir ein Programm, das jede SQL-SELECT-Abfrage, die der Benutzer über die Tastatur eingibt, auf die Datenbank ARTICLES anwendet.
import java.sql.*;
import java.io.*;
// displays the contents of a ARTICLES system database
public class sql1{
static final String DB="ARTICLES"; // database to exploit
public static void main(String arg[]){
Connection connect=null; // connection to base
Statement S=null; // purpose of queries
ResultSet RS=null; // query result table
String select; // query text SQL select
int nbColonnes; // no. of columns in ResultSet
// creation of a keyboard input stream
BufferedReader in=null;
try{
in=new BufferedReader(new InputStreamReader(System.in));
} catch(Exception e){
erreur("erreur lors de l'ouverture du flux clavier ("+e+")",3);
}
try{
// base connection
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connect=DriverManager.getConnection("jdbc:odbc:"+DB,"","");
System.out.println("Connexion avec la base " + DB + " établie");
// creation of a Statement object
S=connect.createStatement();
// execution loop for SQL requests typed on keyboard
System.out.print("Requête : ");
select=in.readLine();
while(!select.equals("fin")){
// query execution
RS=S.executeQuery(select);
// number of columns
nbColonnes=RS.getMetaData().getColumnCount();
// using the results table
System.out.println("Résultats obtenus\n\n");
while(RS.next()){ // as long as there's a line to operate
// it is displayed on screen
for(int i=1;i<nbColonnes;i++)
System.out.print(RS.getString(i)+",");
System.out.println(RS.getString(nbColonnes));
}// next line
// following request
System.out.print("Requête : ");
select=in.readLine();
}// while
} catch (Exception e){
erreur("Erreur " + e,2);
}
// closing the base and input stream
try{
connect.close();
System.out.println("Base " + DB + " fermée");
in.close();
} catch (Exception e){}
}// hand
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
}// class
Hier sind einige der erzielten Ergebnisse:
Connexion avec la base ARTICLES établie
Requête : select * from articles order by prix desc
Résultats obtenus
g457,panthère,800000,1,1
z400,léopard,500000,1,1
f807,cachalot,200000,0,0
d600,arc,5000,10,2
x123,fusil,3000,10,2
s345,skis nautiques,1800,3,2
d800,canoé,1502,12,6
a300,vélo,1202,30,2
f450,essai3,3,3,3
Requête : select nom, prix from articles where prix >10000 order by prix desc
Résultats obtenus
panthère,800000
léopard,500000
cachalot,200000
6.2.3.7. Führen Sie eine Abfrage zur Datenbankaktualisierung aus
Ein Statement-Objekt wird zum Speichern von SQL-Abfragen verwendet. Die Methode, die dieses Objekt zum Ausführen von SQL-Aktualisierungsabfragen (INSERT, UPDATE, DELETE) verwendet, ist nicht mehr die zuvor besprochene executeQuery-Methode, sondern die executeUpdate-Methode:
Der Unterschied liegt im Ergebnis: Während executeQuery das Resultset (ResultSet) zurückgab, gibt executeUpdate die Anzahl der von der Aktualisierungsoperation betroffenen Zeilen zurück.
6.2.3.8. Ein drittes Beispiel
Wir greifen das vorherige Programm wieder auf und ändern es geringfügig: Die über die Tastatur eingegebenen Abfragen sind nun Aktualisierungsabfragen für die Datenbank ARTICLES.
import java.sql.*;
import java.io.*;
// displays the contents of a ARTICLES system database
public class sql2{
static final String DB="ARTICLES"; // database to exploit
public static void main(String arg[]){
Connection connect=null; // connection to base
Statement S=null; // purpose of queries
ResultSet RS=null; // query result table
String sqlUpdate; // text of SQL update request
int nbLignes; // no. of lines affected by an update
// creation of a keyboard input stream
BufferedReader in=null;
try{
in=new BufferedReader(new InputStreamReader(System.in));
} catch(Exception e){
erreur("erreur lors de l'ouverture du flux clavier ("+e+")",3);
}
try{
// base connection
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connect=DriverManager.getConnection("jdbc:odbc:"+DB,"","");
System.out.println("Connexion avec la base " + DB + " établie");
// creation of a Statement object
S=connect.createStatement();
// execution loop for SQL requests typed on keyboard
System.out.print("Requête : ");
sqlUpdate=in.readLine();
while(!sqlUpdate.equals("fin")){
// query execution
nbLignes=S.executeUpdate(sqlUpdate);
// follow-up
System.out.println(nbLignes + " ligne(s) ont été mises à jour");
// following request
System.out.print("Requête : ");
sqlUpdate=in.readLine();
}// while
} catch (Exception e){
erreur("Erreur " + e,2);
}
// closing the base and input stream
try{
// free up resources linked to the base
RS.close();
S.close();
connect.close();
System.out.println("Base " + DB + " fermée");
// keyboard flow closure
in.close();
} catch (Exception e){}
}// hand
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
}// class
Hier sind die Ergebnisse verschiedener Durchläufe der Programme sql1 und sql2:
Liste der Zeilen in der Datenbank ARTICLES:
E:\data\java\jdbc\0>java sql1
Connexion avec la base ARTICLES établie
Requête : select nom,stock_actu from articles
Résultats obtenus
vélo,30
arc,10
canoé,12
fusil,10
skis nautiques,3
essai3,3
cachalot,0
léopard,1
panthère,1
Wir ändern bestimmte Zeilen:
E:\data\java\jdbc\0>java sql2
Connexion avec la base ARTICLES établie
Requête : update articles set stock_actu=stock_actu+1 where stock_actu>10
2 ligne(s) ont été mises à jour
Überprüfung:
E:\data\java\jdbc\0>java sql1
Connexion avec la base ARTICLES établie
Requête : select nom,stock_actu from articles
Résultats obtenus
vélo,31
arc,10
canoé,13
fusil,10
skis nautiques,3
essai3,3
cachalot,0
léopard,1
panthère,1
Zeile hinzufügen:
E:\data\java\jdbc\0>java sql2
Connexion avec la base ARTICLES établie
Requête : insert into articles (code,nom,prix,stock_actu,stock_mini) values ('x400','nouveau',200,20,10)
1 ligne(s) ont été mises à jour
Überprüfung:
E:\data\java\jdbc\0>java sql1
Connexion avec la base ARTICLES établie
Requ_te : select nom,stock_actu from articles
Résultats obtenus
vélo,31
arc,10
canoé,13
fusil,10
skis nautiques,3
essai3,3
cachalot,0
léopard,1
panthère,1
nouveau,20
Eine Zeile löschen:
E:\data\java\jdbc\0>java sql2
Connexion avec la base ARTICLES établie
Requête : delete from articles where code='x400'
1 ligne(s) ont été mises à jour
Requête : fin
Überprüfung:
E:\data\java\jdbc\0>java sql1
Connexion avec la base ARTICLES établie
Requête : select nom,stock_actu from articles
Résultats obtenus
vélo,31
arc,10
cano_,13
fusil,10
skis nautiques,3
essai3,3
cachalot,0
léopard,1
panthère,1
6.2.3.9. Führen Sie eine beliebige SQL-Abfrage aus
Das für die Ausführung von SQL-Abfragen erforderliche Statement-Objekt verfügt über eine execute-Methode, mit der jede Art von SQL-Abfrage ausgeführt werden kann:
Der Rückgabewert ist der Boolesche Wert true, wenn die Abfrage ein ResultSet* zurückgegeben hat (executeQuery*), und false, wenn sie eine Zahl zurückgegeben hat (executeUpdate*). Das resultierende ResultSet kann mit der Methode getResultSet abgerufen werden, die Anzahl der aktualisierten Zeilen mit der Methode getUpdateCount*. Wir würden also schreiben:
Statement S=...;
ResultSet RS=...;
int nbLignes;
String requête=...;
// exécution d’une requête SQL
if (S.execute(requête)){
// on a un resultset
RS=S.getResultSet();
// exploitation du ResultSet
...
} else {
// c’était une requête de mise à jour
nbLignes=S.getUpdateCount();
...
}
6.2.3.10. Viertes Beispiel
Wir übernehmen das Konzept aus den Programmen sql1 und sql2 und wenden es auf ein Programm sql3 an, das nun jede über die Tastatur eingegebene SQL-Abfrage ausführen kann. Um das Programm allgemeiner zu gestalten, werden die Eigenschaften der zu verwendenden Datenbank als Parameter an das Programm übergeben.
import java.sql.*;
import java.io.*;
// call: pg PILOTE URL UID MDP
// connects to the URL database using class JDBC PILOTE
// user UID is identified by password MDP
public class sql3{
static String syntaxe="pg PILOTE URL UID MDP";
public static void main(String arg[]){
// check number of arguments
if(arg.length<2 || arg.length>4)
erreur(syntaxe,1);
// init connection parameters
Connection connect=null;
String uid="";
String mdp="";
if(arg.length>=3) uid=arg[2];
if(arg.length==4) mdp=arg[3];
// other data
Statement S=null; // purpose of queries
ResultSet RS=null; // result table of a query request
String sqlText; // text of query SQL to be executed
int nbLignes; // no. of lines affected by an update
int nbColonnes; // nb of columns in a ResultSet
// creation of a keyboard input stream
BufferedReader in=null;
try{
in=new BufferedReader(new InputStreamReader(System.in));
} catch(Exception e){
erreur("erreur lors de l'ouverture du flux clavier ("+e+")",3);
}
try{
// base connection
Class.forName(arg[0]);
connect=DriverManager.getConnection(arg[1],uid,mdp);
System.out.println("Connexion avec la base " + arg[1] + " établie");
// creation of a Statement object
S=connect.createStatement();
// execution loop for SQL requests typed on keyboard
System.out.print("Requête : ");
sqlText=in.readLine();
while(!sqlText.equals("fin")){
// query execution
try{
if(S.execute(sqlText)){
// we have obtained a ResultSet - we exploit it
RS=S.getResultSet();
// number of columns
nbColonnes=RS.getMetaData().getColumnCount();
// using the results table
System.out.println("\nRésultats obtenus\n-----------------\n");
while(RS.next()){ // as long as there's a line to operate
// it is displayed on screen
for(int i=1;i<nbColonnes;i++)
System.out.print(RS.getString(i)+",");
System.out.println(RS.getString(nbColonnes));
}// next line of ResultSet
} else {
// it was an update request
nbLignes=S.getUpdateCount();
// follow-up
System.out.println(nbLignes + " ligne(s) ont été mises à jour");
}//if
} catch (Exception e){
System.out.println("Erreur " +e);
}
// following request
System.out.print("\nNouvelle Requête : ");
sqlText=in.readLine();
}// while
} catch (Exception e){
erreur("Erreur " + e,2);
}
// closing the base and input stream
try{
// free up resources linked to the base
RS.close();
S.close();
connect.close();
System.out.println("Base " + arg[1] + " fermée");
// keyboard flow closure
in.close();
} catch (Exception e){}
}// hand
public static void erreur(String msg, int exitCode){
System.err.println(msg);
System.exit(exitCode);
}
}// class
Wir erstellen die folgende Abfragedatei:
select * from articles
update articles set stock_mini=stock_mini+5 where stock_mini<5
select nom,stock_mini from articles
insert into articles (code,nom,prix,stock_actu,stock_mini) values ('x400','nouveau',100,20,10)
select * from articles
delete from articles where code='x400'
select * from articles
fin
Das Programm wird wie folgt ausgeführt:
Das Programm liest seine Eingabe aus der Abfragedatei und schreibt seine Ausgabe in die Ergebnisdatei. Die erhaltenen Ergebnisse lauten wie folgt:
Connexion avec la base jdbc:odbc:articles établie
Requête : (requete 1 du fichier des requetes : select * from articles)
Résultats obtenus
-----------------
a300,vélo,1202,31,3
d600,arc,5000,10,3
d800,canoé,1502,13,7
x123,fusil,3000,10,3
s345,skis nautiques,1800,3,3
f450,essai3,3,3,4
f807,cachalot,200000,0,1
z400,léopard,500000,1,2
g457,panthère,800000,1,2
Nouvelle Requête : (requete 2 du fichier des requetes : update articles set stock_mini=stock_mini+5 where stock_mini<5)
8 ligne(s) ont été mises à jour
Nouvelle Requête : (requete 3 du fichier des requetes : select nom,stock_mini from articles)
Résultats obtenus
-----------------
vélo,8
arc,8
canoé,7
fusil,8
skis nautiques,8
essai3,9
cachalot,6
léopard,7
panthère,7
Nouvelle Requête : (requete 4 du fichier des requetes : insert into articles (code,nom,prix,stock_actu,stock_mini) values ('x400','nouveau',100,20,10))
1 ligne(s) ont été mises à jour
Nouvelle Requête : (requete 5 du fichier des requetes : select * from articles)
Résultats obtenus
-----------------
a300,vélo,1202,31,8
d600,arc,5000,10,8
d800,canoé,1502,13,7
x123,fusil,3000,10,8
s345,skis nautiques,1800,3,8
f450,essai3,3,3,9
f807,cachalot,200000,0,6
z400,léopard,500000,1,7
g457,panthère,800000,1,7
x400,nouveau,100,20,10
Nouvelle Requête : (requete 6 du fichier des requêtes : delete from articles where code='x400')
1 ligne(s) ont été mises à jour
Nouvelle Requête : (requete 7 du fichier des requêtes : select * from articles)
Résultats obtenus
-----------------
a300,vélo,1202,31,8
d600,arc,5000,10,8
d800,canoé,1502,13,7
x123,fusil,3000,10,8
s345,skis nautiques,1800,3,8
f450,essai3,3,3,9
f807,cachalot,200000,0,6
z400,léopard,500000,1,7
g457,panthère,800000,1,7
6.3. en zur Steuerberechnung mit einer Datenbank
Als wir uns das letzte Mal mit der Berechnung von Steuern befasst haben, haben wir eine grafische Benutzeroberfläche verwendet und die Daten in einer Datei gespeichert. Wir werden diese Version noch einmal aufgreifen, wobei wir nun davon ausgehen, dass sich die Daten in einer ODBC-MySQL-Datenbank befinden. MySQL ist ein Open-Source-DBMS, das auf verschiedenen Plattformen, darunter Windows und Linux, eingesetzt werden kann. Mit diesem DBMS wurde eine Datenbank namens dbimpots angelegt, die eine einzige Tabelle namens impots enthält. Der Zugriff auf die Datenbank wird durch einen Benutzernamen und ein Passwort gesteuert, in diesem Fall „admimpots“ und „mdpimpots“. Der Screenshot zeigt, wie man die Datenbank „dbimpots“ mit MySQL verwendet:
C:\Program Files\EasyPHP\mysql\bin>mysql -u admimpots -p
Enter password: *********
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 18 to server version: 3.23.49-max-nt
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql> use dbimpots;
Database changed
mysql> show tables;
+--------------------+
| Tables_in_dbimpots |
+--------------------+
| impots |
+--------------------+
1 row in set (0.00 sec)
mysql> describe impots;
+---------+--------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+--------+------+-----+---------+-------+
| limites | double | YES | | NULL | |
| coeffR | double | YES | | NULL | |
| coeffN | double | YES | | NULL | |
+---------+--------+------+-----+---------+-------+
3 rows in set (0.02 sec)
mysql> select * from impots;
+---------+--------+---------+
| limites | coeffR | coeffN |
+---------+--------+---------+
| 12620 | 0 | 0 |
| 13190 | 0.05 | 631 |
| 15640 | 0.1 | 1290.5 |
| 24740 | 0.15 | 2072.5 |
| 31810 | 0.2 | 3309.5 |
| 39970 | 0.25 | 4900 |
| 48360 | 0.3 | 6898 |
| 55790 | 0.35 | 9316.5 |
| 92970 | 0.4 | 12106 |
| 127860 | 0.45 | 16754 |
| 151250 | 0.5 | 23147.5 |
| 172040 | 0.55 | 30710 |
| 195000 | 0.6 | 39312 |
| 0 | 0.65 | 49062 |
+---------+--------+---------+
14 rows in set (0.00 sec)
mysql>quit
Die grafische Benutzeroberfläche der Anwendung sieht wie folgt aus:

Die grafische Benutzeroberfläche hat einige Änderungen erfahren:
Nr. | Typ | Name | Rolle |
1 | JTextField | txtConnection | ODBC-Datenbankverbindungszeichenfolge |
2 | JScrollPane | JScrollPane1 | Container für das Textfeld 3 |
3 | JTextArea | txtStatus | zeigt Statusmeldungen an, einschließlich Fehlermeldungen |
Die in (1) eingegebene Verbindungszeichenfolge hat das folgende Format: DSN;Benutzername;Passwort mit
der DSN-Name der ODBC-Datenquelle ist | |
die Identität eines Benutzers mit Lesezugriff auf die Datenbank | |
sein Passwort |
Die Datenbank „dbimpots“ wurde manuell mit MySQL erstellt. Sie wird wie folgt in eine ODBC-Datenquelle konvertiert:
- Starten Sie den 32-Bit-ODBC-Datenquellen-Administrator

- Verwenden Sie die Schaltfläche [Hinzufügen], um eine neue ODBC-Datenquelle hinzuzufügen

- Wählen Sie den MySQL-Treiber aus und klicken Sie auf [Fertigstellen]

- Der MySQL-Treiber fordert einige Informationen an:
1 | Der DSN-Name für die ODBC-Datenquelle – er kann beliebig gewählt werden |
2 | den Rechner, auf dem das MySQL-DBMS läuft – in diesem Fall localhost. Es ist anzumerken, dass es sich bei der Datenbank auch um eine Remote-Datenbank handeln könnte. Lokale Anwendungen, die die ODBC-Datenquelle nutzen, würden dies nicht bemerken. Dies wäre insbesondere bei unserer Java-Anwendung der Fall. |
3 | die zu verwendende MySQL-Datenbank. MySQL ist ein DBMS, das relationale Datenbanken verwaltet, also Gruppen von Tabellen, die durch Beziehungen miteinander verknüpft sind. Hier geben wir den Namen der zu verwaltenden Datenbank an. |
4 | Der Name eines Benutzers mit Zugriffsrechten auf diese Datenbank |
5 | sein Passwort |
Sobald die ODBC-Datenquelle definiert ist, können wir unser Programm testen:
![]() | ![]() |
Werfen wir einen Blick auf den Code, der im Vergleich zur grafischen Version ohne Datenbank geändert wurde. Hier ist der bisher verwendete Code für die Klasse *impots*:
// creation of an impots class
public class impots{
// data required for tax calculation
// come from an external source
private double[] limites, coeffR, coeffN;
// manufacturer
public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception{
// check that the 3 arrays have the same size
boolean OK=LIMITES.length==COEFFR.length && LIMITES.length==COEFFN.length;
if (! OK) throw new Exception ("Les 3 tableaux fournis n'ont pas la même taille("+
LIMITES.length+","+COEFFR.length+","+COEFFN.length+")");
// it's good
this.limites=LIMITES;
this.coeffR=COEFFR;
this.coeffN=COEFFN;
}//manufacturer
// tAX CALCULATION
public long calculer(boolean marié, int nbEnfants, int salaire){
// calculating the number of shares
double nbParts;
if (marié) nbParts=(double)nbEnfants/2+2;
else nbParts=(double)nbEnfants/2+1;
if (nbEnfants>=3) nbParts+=0.5;
// calculation of taxable income & family quota
double revenu=0.72*salaire;
double QF=revenu/nbParts;
// tAX CALCULATION
limites[limites.length-1]=QF+1;
int i=0;
while(QF>limites[i]) i++;
// return result
return (long)(revenu*coeffR[i]-nbParts*coeffN[i]);
}//calculate
}//class
Diese Klasse erstellt die drei Arrays „limits“, „coeffR“ und „coeffN“ aus drei Arrays, die als Parameter an ihren Konstruktor übergeben werden. Wir beschließen, einen neuen Konstruktor hinzuzufügen, der es uns ermöglicht, dieselben drei Arrays aus einer Datenbank zu erstellen:
public impots(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
// dsnIMPOTS: DSN database name
// userIMPOTS, mdpIMPOTS: database login/password
In diesem Beispiel entscheiden wir uns, diesen neuen Konstruktor nicht in der Klasse *impots*, sondern in einer abgeleiteten Klasse, *impotsJDBC*, zu implementieren:
// imported packages
import java.sql.*;
import java.util.*;
public class impotsJDBC extends impots{
// addition of a constructor for building
// limit tables, coeffr, coeffn from table
// database taxes
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
// dsnIMPOTS: DSN database name
// userIMPOTS, mdpIMPOTS: database login/password
// data tables
ArrayList aLimites=new ArrayList();
ArrayList aCoeffR=new ArrayList();
ArrayList aCoeffN=new ArrayList();
// connection to base
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection connect=DriverManager.getConnection("jdbc:odbc:"+dsnIMPOTS,userIMPOTS,mdpIMPOTS);
// creation of a Statement object
Statement S=connect.createStatement();
// select request
String select="select limites, coeffr, coeffn from impots";
// query execution
ResultSet RS=S.executeQuery(select);
while(RS.next()){
// running line operation
aLimites.add(RS.getString("limites"));
aCoeffR.add(RS.getString("coeffr"));
aCoeffN.add(RS.getString("coeffn"));
}// next line
// closing resources
RS.close();
S.close();
connect.close();
// data transfer to bounded arrays
int n=aLimites.size();
limites=new double[n];
coeffR=new double[n];
coeffN=new double[n];
for(int i=0;i<n;i++){
limites[i]=Double.parseDouble((String)aLimites.get(i));
coeffR[i]=Double.parseDouble((String)aCoeffR.get(i));
coeffN[i]=Double.parseDouble((String)aCoeffN.get(i));
}//for
}//manufacturer
}//class
Der Konstruktor liest den Inhalt der Tabelle „impots“ aus der Datenbank, die ihm als Parameter übergeben wird, und füllt die drei Arrays „limites“, „coeffR“ und „coeffN“ damit. Dabei können verschiedene Fehler auftreten. Der Konstruktor behandelt diese nicht selbst, sondern „leitet sie weiter“ an das aufrufende Programm:
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
Wenn wir uns den vorherigen Code genauer ansehen, erkennen wir, dass die Klasse „impotsJDBC“ direkt die Felder „limits“, „coeffR“ und „coeffN“ ihrer Basisklasse „impots“ verwendet. Da diese als privat deklariert sind:
hat die Klasse „importsJDBC“ keinen direkten Zugriff auf diese Felder. Wir nehmen daher zunächst eine Änderung an der Basisklasse vor, indem wir schreiben:
Das Attribut „protected“ ermöglicht es Klassen, die von der Klasse „imports“ abgeleitet sind, direkten Zugriff auf Felder zu haben, die mit diesem Attribut deklariert wurden. Wir müssen eine zweite Änderung vornehmen. Der Konstruktor der untergeordneten Klasse „importsJDBC“ ist wie folgt deklariert:
public impotsJDBC(String dsnIMPOTS, String userIMPOTS, String mdpIMPOTS)
throws SQLException,ClassNotFoundException{
Wir wissen, dass wir vor der Erstellung eines Objekts einer Unterklasse zunächst ein Objekt der Oberklasse erstellen müssen. Dazu muss der Konstruktor der Unterklasse den Konstruktor der Oberklasse explizit mit einer super(....)-Anweisung aufrufen. Dies geschieht hier nicht, da wir nicht erkennen können, welchen Konstruktor der Oberklasse wir aufrufen könnten. Derzeit gibt es nur einen, und dieser ist nicht geeignet. Der Compiler sucht dann in der übergeordneten Klasse nach einem parameterlosen Konstruktor, den er aufrufen könnte. Er findet keinen, was zu einem Kompilierungsfehler führt. Wir fügen daher unserer Klasse „imports“ einen parameterlosen Konstruktor hinzu:
Wir deklarieren ihn als „protected“, damit er nur von untergeordneten Klassen verwendet werden kann. Das Grundgerüst der Klasse „impots“ sieht nun wie folgt aus:
public class impots{
// data required for tax calculation
// come from an external source
protected double[] limites=null;
protected double[] coeffR=null;
protected double[] coeffN=null;
// empty builder
protected impots(){}
// manufacturer
public impots(double[] LIMITES, double[] COEFFR, double[] COEFFN) throws Exception{
...........
}//manufacturer
// tAX CALCULATION
public long calculer(boolean marié, int nbEnfants, int salaire){
.............
}//calculate
}//class
Die Aktion für das Menü „Initialize“ in unserer Anwendung sieht nun wie folgt aus:
void mnuInitialiser_actionPerformed(ActionEvent e) {
// retrieve the connection string
Pattern séparateur=Pattern.compile("\\s*;\\s*");
String[] champs=séparateur.split(txtConnexion.getText().trim());
// three fields are required
if(champs.length!=3){
// error
txtStatus.setText("Chaîne de connexion (DSN;uid;mdp) incorrecte");
// back to visual interface
txtConnexion.requestFocus();
return;
}//if
// load data
try{
// creation of impotsJDBC object
objImpots=new impotsJDBC(champs[0],champs[1],champs[2]);
// confirmation
txtStatus.setText("Données chargées");
// salary can be modified
txtSalaire.setEditable(true);
// no more chgt possible
mnuInitialiser.setEnabled(false);
txtConnexion.setEditable(false);
}catch(Exception ex){
// problem
txtStatus.setText("Erreur : " + ex.getMessage());
// end
return;
}//catch
}
Sobald das Objekt objImpots erstellt wurde, entspricht die Anwendung der bereits geschriebenen grafischen Anwendung. Der Leser wird gebeten, diese zu Rate zu ziehen.
6.4. Übungen
6.4.1. Übung 1
Stellen Sie eine grafische Benutzeroberfläche für das vorherige sql3-Programm bereit.
6.4.2. Übung 2
Ein Java-Applet kann nur über den Server, von dem es geladen wurde, auf eine Datenbank zugreifen. Da ein Applet keinen Zugriff auf die Festplatte des Rechners hat, auf dem es ausgeführt wird, kann sich die Datenbank nicht auf dem Client-Rechner befinden, auf dem das Applet verwendet wird. Wir befinden uns daher in folgender Situation:

Der Rechner, auf dem das Applet läuft, der Server und der Rechner, auf dem die Datenbank gehostet wird, können drei verschiedene Rechner sein. Hier gehen wir davon aus, dass sich die Datenbank auf dem Server befindet.
Problem 1
Schreiben Sie die folgende Serveranwendung in Java:
- Die Serveranwendung läuft auf einem Port, der ihr als Parameter übergeben wird
- Wenn sich ein Client verbindet, sendet die Serveranwendung die Nachricht
- Der Client sendet daraufhin die für die Verbindung zu einer Datenbank erforderlichen Parameter sowie die auszuführende Abfrage im folgenden Format:
Die Parameter werden durch einen Schrägstrich getrennt. Die ersten vier Parameter sind diejenigen des in diesem Kapitel beschriebenen Programms sql3.
- Die Serveranwendung stellt dann eine Verbindung zur angegebenen Datenbank her, die sich auf demselben Rechner wie der Server befinden muss, und führt dort die SQL-Abfrage aus. Die Ergebnisse werden im folgenden Format an den Client zurückgegeben:
...
wenn es sich um das Ergebnis einer Select-Abfrage handelt, oder
um die Anzahl der von einer Update-Abfrage betroffenen Zeilen zurückzugeben. Tritt ein Datenbankverbindungsfehler oder ein Fehler bei der Abfrageausführung auf, gibt die Anwendung
- Sobald die Abfrage ausgeführt wurde, schließt die Serveranwendung die Verbindung.
Problem 2
Erstellen Sie ein Java-Applet, das den oben beschriebenen Server abfragt. Sie können sich dabei an der grafischen Oberfläche aus Übung 1 orientieren. Da der Server-Port variieren kann, wird er über die Oberfläche des Applets eingegeben. Dasselbe gilt für alle Parameter, die zum Senden der Zeile erforderlich sind:
, die der Client an den Server senden muss.
6.4.3. Übung 3
Der folgende Text beschreibt ein Problem, das ursprünglich in Visual Basic gelöst werden sollte. Passen Sie es für die Verarbeitung in Java innerhalb eines Applets an. Dieses Applet stützt sich auf den Server aus Übung 2. Die grafische Benutzeroberfläche kann angepasst werden, um dem neuen Ausführungskontext Rechnung zu tragen.
Wir schlagen vor, eine Anwendung zu erstellen, die die verschiedenen möglichen Aktualisierungsvorgänge an einer Tabelle in einer ACCESS-Datenbank veranschaulicht. Die ACCESS-Datenbank heißt articles.mdb. Sie enthält eine einzige Tabelle namens articles, in der die von einem Unternehmen verkauften Artikel aufgelistet sind. Ihre Struktur ist wie folgt:
name | Typ |
Code | 4-stelliger Artikelcode |
Name | sein Name (Zeichenkette) |
Preis | sein Preis (tatsächlich) |
aktueller_bestand | aktueller Lagerbestand (Ganzzahl) |
min_stock | der Mindestbestand (Ganzzahl), unterhalb dessen der Artikel nachbestellt werden muss |
Wir schlagen vor, diese Tabelle über das folgende Formular anzuzeigen und zu aktualisieren:

Die Steuerelemente in diesem Formular sind wie folgt:
Nr. | Typ | Name | Funktion |
1 | Textfeld | Datensatz | Nummer des angezeigten Datensatzes aktiviert ist falsch |
2 | Textfeld | Code | Elementcode |
3 | Textfeld | Name | Artikelname |
4 | Textfeld | Preis | Artikelpreis |
5 | Textfeld | aktueller | aktueller Lagerbestand des Artikels |
6 | Textfeld | Mindest | Mindestbestand des Artikels |
7 | Daten | Daten1 | Mit der Datenbank verknüpfte Datensteuerung databasename=Pfad zur Datei articles.mdb recordsource=articles connect=access |
8 | HScrollBar | position | ermöglicht die Navigation in der Tabelle |
9 | Schaltfläche | OK | ermöglicht es Ihnen, eine Aktualisierung zu bestätigen – erscheint nur während einer Aktualisierung |
10 | Schaltfläche | Abbrechen | ermöglicht es Ihnen, ein Update abzubrechen – erscheint nur während des Updates |
11 | Rahmen | Rahmen 1 | aus ästhetischen Gründen |
12 | Textfeld | Basisname | Name der geöffneten Datenbank „enabled“ ist auf „false“ gesetzt |
13 | Textfeld | sourcename | Name der geöffneten Tabelle „enabled“ ist auf „false“ gesetzt |
Steuerelement | Besondere Merkmale |
data1 | Die Felder „databasename“ und „recordsource“ sind bereits ausgefüllt. „databasename“ muss auf die Access-Datenbank „articles.mdb“ in Ihrem Verzeichnis verweisen, und „recordsource“ auf die Tabelle „articles“. |
code | Dies ist ein Textfeld, das wir mit dem Feld „code“ des aktuellen Datensatzes in „data1“ verknüpfen möchten. Dazu füllen wir zwei Felder aus: datasource: Geben Sie „data1“ ein, um anzugeben, dass das Textfeld mit der Tabelle verknüpft ist, die mit „data1“ assoziiert ist datafield: Wählen Sie das Feld „code“ aus der Tabelle „articles“ aus Nach diesen Schritten enthält das Textfeld „code“ immer das Feld „code“ des aktuellen Datensatzes in „data1“. Umgekehrt wird durch das Ändern des Inhalts dieses Textfelds das Feld „code“ des aktuellen Datensatzes aktualisiert. Verfahren Sie bei den anderen Textfeldern genauso |
name | Datenquelle: data1 Datenfeld: name |
Preis | Datenquelle: data1 Datenfeld: price |
aktuell | Datenquelle: data1 Datenfeld: current_stock |
Minimum | Datenquelle: data1 Datenfeld: stock_min |
Die Menüstruktur sieht wie folgt aus
Bearbeiten | Durchsuchen | Beenden |
Hinzufügen | Zurück | |
Bearbeiten | Weiter | |
Löschen | Erste | |
Letzter |
Die Funktionen der verschiedenen Optionen sind wie folgt:
Menü | Name | Funktion |
mnuadd | um einen neuen Datensatz zur Tabelle „items“ hinzuzufügen | |
delete | , um den aktuell angezeigten Datensatz aus der Tabelle „items“ zu löschen | |
Bearbeiten | um den aktuell angezeigten Datensatz in der Artikeltabelle zu bearbeiten | |
mnuprecedent | um zum vorherigen Datensatz zu wechseln | |
nach unten-weiter | zum nächsten Eintrag springen | |
Zurück | zum ersten Titel springen | |
mnunlast | zum letzten Titel springen | |
mnuquit | um die Anwendung zu beenden |
Während des form_load-Ereignisses wird die mit data1 verknüpfte Tabelle geöffnet (data1.refresh). Wenn die Tabelle nicht geöffnet werden kann, wird eine Fehlermeldung angezeigt und das Programm wird beendet (end). Andernfalls wird das Formular angezeigt, wobei der erste Datensatz aus der Tabelle „articles“ sichtbar ist. In den Feldern sind keine Eingaben zulässig (Eigenschaft „enabled“ auf „false“ gesetzt). Eingaben sind nur über die Optionen „Hinzufügen“ und „Bearbeiten“ möglich. Die Schaltflächen „OK“ und „Abbrechen“ sind ausgeblendet (visible=false).
Wir schlagen vor, die Prozeduren für die verschiedenen Menüoptionen sowie für die Schaltflächen „OK“ und „Abbrechen“ zu erstellen. Die folgenden Punkte werden vorerst außer Acht gelassen:
- Aktivieren/Deaktivieren bestimmter Menüoptionen: Beispielsweise muss die Option „Weiter“ deaktiviert sein, wenn sich der Cursor auf dem letzten Datensatz in der Tabelle befindet
- Verwaltung der horizontalen Bildlaufleiste
Menü „Durchsuchen/Weiter“
- Wechselt zum nächsten Datensatz (data1.RecordSet.MoveNext), sofern wir uns nicht am Ende der Datei befinden (data1.RecordSet.EOF). Aktualisiert das Datensatz-Textfeld (data1.RecordSet.AbsolutePosition / data1.RecordSet.RecordCount).
Menü „Durchsuchen/Zurück“
- springt zum vorherigen Datensatz (data1.RecordSet.MovePrevious), sofern man sich nicht am Anfang der Datei befindet (data1.RecordSet.BOF). Aktualisiert das Datensatz-Textfeld.
Menü „Durchsuchen/Erster“
- Wechselt zum ersten Datensatz (data1.RecordSet.MoveFirst), sofern die Datei nicht leer ist (data1.RecordSet.RecordCount = 0). Aktualisiert das Datensatz-Textfeld.
Menü „Durchsuchen/Letzter“
- Wechselt zum letzten Datensatz (data1.RecordSet.MoveLast), sofern die Datei nicht leer ist (data1.RecordSet.RecordCount = 0). Aktualisiert das Datensatz-Textfeld.
Menü „Bearbeiten/Hinzufügen“
- ermöglicht es Ihnen, einen Datensatz zur Tabelle hinzuzufügen
- Wechselt in den Modus „Datensatz hinzufügen“ (data1.recordset.addnew)
- Aktiviert die Dateneingabe in den 5 Feldern: Code, Name, Preis usw. (enabled=true)
- Deaktiviert die Menüs „Bearbeiten“, „Durchsuchen“ und „Beenden“ (enabled=false)
- zeigt die Schaltflächen „OK“ und „Abbrechen“ an (visible=true)
OK-Schaltfläche
- speichert eine Datensatzaktualisierung (data1.recordset.Update)
- blendet die Schaltflächen „OK“ und „Abbrechen“ aus (visible=false)
- Aktiviert die Optionen „Bearbeiten“, „Durchsuchen“ und „Beenden“ (enabled=true)
- aktualisiert das Textfeld des Formulars
Schaltfläche „Abbrechen“
- bricht eine Datensatzaktualisierung ab (data1.recordset.CancelUpdate)
- blendet die Schaltflächen „OK“ und „Abbrechen“ aus (visible=false)
- aktiviert die Optionen „Bearbeiten“, „Durchsuchen“ und „Beenden“ (enabled=true)
- aktualisiert das Textfeld des Formulars
Menü „Bearbeiten/Ändern“
- ermöglicht es Ihnen, den im Formular angezeigten Datensatz zu bearbeiten
- ruft den Datensatzbearbeitungsmodus auf (data1.recordset.edit)
- Aktiviert die Eingabe in den 4 Feldern: Name, Preis usw. (enabled=true), jedoch nicht im Code-Feld (enabled=false)
- deaktiviert die Menüs „Bearbeiten“, „Durchsuchen“ und „Beenden“ (enabled=false)
- zeigt die Schaltflächen „OK“ und „Abbrechen“ an (visible=true)
Menü „Bearbeiten/Löschen“
- ermöglicht das Löschen (data1.recordset.delete) des angezeigten Datensatzes aus der Tabelle
- Wechselt zum nächsten Datensatz (data1.recordset.movenext)
Menü „Beenden“
- entlädt das Formular (unload me)
Ereignis „form_unload“ (cancel als Ganzzahl)
- wird durch den Befehl „unload me“ oder durch das Schließen des Formulars mit Alt-F4 oder einem Doppelklick auf das Symbol in der Taskleiste ausgelöst, also nicht unbedingt durch die Beenden-Option.
- zeigt die Frage „Möchten Sie die Anwendung wirklich beenden?“ mit zwei Ja/Nein-Schaltflächen an (msgbox mit style=vbyes+vbno)
- Wenn die Antwort „Nein“ lautet (=vbno), setze „cancel“ auf -1 und verlasse die Prozedur „form_unload“. Ein Wert von -1 für „cancel“ bedeutet, dass das Schließen des Fensters abgelehnt wird.
- Wenn die Antwort „Ja“ lautet (=vbyes), wird die Datenbank geschlossen (data1.recordset.close, data1.database.close).
Hier konzentrieren wir uns auf das Aktivieren/Deaktivieren von Menüs. Nach jedem Vorgang, der den aktuellen Datensatz ändert, rufen wir eine Prozedur auf, die wir „Oueston“ nennen können. Diese Prozedur prüft die folgenden Bedingungen:
. Wenn die Datei leer ist,
- deaktivieren die Menüs „Durchsuchen“, „Bearbeiten“ und „Löschen“ und
- und aktivieren die anderen
. Wenn der aktuelle Datensatz der erste Datensatz ist,
- deaktivieren „Durchsuchen/Zurück“
- wird den Rest ermöglichen
. Wenn der aktuelle Datensatz der letzte ist,
- deaktivieren Sie „Durchsuchen/Weiter“
- erlaubt den Rest
Ein horizontaler Schieberegler hat drei wichtige Felder:
- min: sein Minimalwert
- max: sein Maximalwert
- value: sein aktueller Wert
Initialisierung des Schiebereglers
Beim Laden (form_load) wird der Schieberegler wie folgt initialisiert:
- min=0
- max=data1.recordset.recordcount-1
- value=1
Beachten Sie, dass unmittelbar nach dem Öffnen der Datenbank (data1.refresh) die Anzahl der Datensätze in der Tabelle, die durch data1.recordset.recordcount dargestellt wird, falsch ist. Sie müssen zum Ende der Tabelle springen (MoveLast) und dann zum Anfang der Tabelle zurückkehren (MoveFirst), damit sie korrekt ist.
Direkte Aktion auf dem Laufwerk
Die Position des Schiebereglers entspricht der Position in der Tabelle.
Wenn der Benutzer den Schieberegler (hier „position“ genannt) verändert, wird das Ereignis „position_change“ ausgelöst. In diesem Ereignis ändern wir den aktuellen Datensatz in der Tabelle so, dass er die am Schieberegler vorgenommene Bewegung widerspiegelt. Dazu verwenden wir das Feld „absoluteposition“ von data1.recordset. Wenn wir diesem Feld den Wert i zuweisen, wird Datensatz Nr. i in der Tabelle zum aktuellen Datensatz. Datensätze werden beginnend mit 0 nummeriert und haben daher eine Nummer im Bereich [0,data1.recordset.recordcount-1]. In der Prozedur „position_change“ schreiben wir einfach
damit der aktuell im Formular angezeigte Datensatz die Bewegung des Joysticks widerspiegelt.
Sobald dies erledigt ist, rufen wir die Oueston-Prozedur auf, um die Menüs zu aktualisieren.
Aktualisierung des DCS
Da die Position des Cursors des Laufwerks die Position in der Tabelle widerspiegeln muss, muss der Wert des Laufwerks jedes Mal aktualisiert werden, wenn sich der aktuelle Datensatz in der Tabelle ändert, was durch eines der Menüs ausgelöst wird. Da jedes dieser Menüs die Oueston-Prozedur aufruft, ist es am besten, diese Aktualisierung ebenfalls in diese Prozedur einzufügen. Schreiben Sie hier einfach:
Wir fügen die Option „Durchsuchen/Suchen“ hinzu, mit der der Benutzer einen Artikel durch Eingabe seines Codes anzeigen kann.
Wenn diese Option aktiviert ist, erfolgen die folgenden Schritte:
- Das System wechselt in den Modus „Neu hinzufügen“ (Addnew), um zu verhindern, dass der aktuelle Datensatz, der bei Aktivierung der Option geöffnet war, verändert wird.
- Wir aktivieren die Eingabe im Codefeld und löschen den Inhalt des Datensatzfeldes,
- Die Menüs werden deaktiviert, und die Schaltflächen „OK“ und „Abbrechen“ werden angezeigt
- Wenn der Benutzer auf „OK“ klickt, müssen wir nach dem Datensatz suchen, der dem vom Benutzer eingegebenen Code entspricht. Die Prozedur „OK_click“ wird jedoch bereits für die Optionen „Durchsuchen/Hinzufügen“ und „Durchsuchen/Ändern“ verwendet. Um zwischen diesen Fällen zu unterscheiden, müssen wir eine globale Variable verwalten, die wir hier „state“ nennen und die drei mögliche Werte haben wird: „add“, „modify“ und „search“. Die mit den Schaltflächen „OK“ und „Abbrechen“ verknüpften Prozeduren verwenden diese Variable, um den Kontext zu bestimmen, in dem sie aufgerufen werden.
- Wenn „state“ den Wert „search“ hat, brechen wir in der mit „OK“ verknüpften Prozedur
- die Operation „addnew“ abbrechen (data1.recordset.cancelupdate), da wir nicht beabsichtigten, einen Datensatz hinzuzufügen. Beachten Sie, dass der aktuelle Datensatz dann auf den zurückgesetzt wird, der vor der Operation „Durchsuchen/Suchen“ auf dem Bildschirm angezeigt wurde.
- Erstellen Sie die Suchkriterien auf der Grundlage des Codes und starten Sie die Suche (data1.recordset.findfirst criteria).
- Wenn die Suche fehlschlägt (data1.recordset.nomatch=true), benachrichtigen wir den Benutzer, kehren dann in den Hinzufügen-Modus (addnew) zurück und beenden die OK-Prozedur. Der Benutzer muss einen neuen Code eingeben oder die Option „Abbrechen“ wählen.
- Ist die Suche erfolgreich, wird der gefundene Datensatz zum neuen aktiven Datensatz. Wir stellen die Menüs wieder her, blenden die Schaltflächen „OK“ und „Abbrechen“ aus, deaktivieren die Eingabe im Codefeld und beenden die Prozedur.
- . Wenn der Status „search“ lautet, brechen wir in der mit „Cancel“ verknüpften Prozedur
- den Vorgang „addnew“ abbrechen (data1.recordset.cancelupdate). Das System kehrt dann automatisch zu dem Datensatz zurück, der vor dem Durchsuchen/Suchen aktiv war.
- Stellen Sie die Menüs wieder her, blenden Sie die Schaltflächen „OK“ und „Abbrechen“ aus, deaktivieren Sie die Eingabe im Codefeld und beenden Sie die Prozedur.
Ein Artikel muss durch seinen Code eindeutig identifiziert werden. Stellen Sie sicher, dass in der Option „Durchsuchen/Hinzufügen“ das Hinzufügen abgelehnt wird, wenn der hinzuzufügende Datensatz einen Artikelcode hat, der bereits in der Tabelle vorhanden ist.
6.4.4. Übung 4
Hier stellen wir eine Webanwendung vor, die auf dem Server aus Übung 2 basiert. Es handelt sich um eine einfache E-Commerce-Anwendung.
Der Kunde bestellt Artikel über die folgende Weboberfläche:

Er kann folgende Vorgänge ausführen:
- einen Artikel aus der Dropdown-Liste auswählen
- die gewünschte Menge angeben
- ihren Kauf durch Klicken auf die Schaltfläche „Kaufen“ bestätigen
- Ihr Kauf wird in der Liste der gekauften Artikel angezeigt
- Er kann Artikel aus dieser Liste entfernen, indem er einen Artikel auswählt und auf die Schaltfläche „Entfernen“ klickt
- Wenn er auf die Schaltfläche „Zusammenfassung“ klickt, wird folgende Zusammenfassung angezeigt:

Die Zusammenfassung ermöglicht es dem Benutzer, die Details seiner Rechnung einzusehen. Der Benutzer kann Details zu einem ausgewählten Artikel in der Dropdown-Liste aufrufen, indem er auf die Schaltfläche „Informationen“ klickt:

Sobald der Benutzer die Rechnungsübersicht angefordert hat, kann er diese auf der nächsten Seite bestätigen. Dazu muss er seine E-Mail-Adresse eingeben und seine Bestellung über die entsprechende Schaltfläche bestätigen.

Vor dem Absenden der Bestellung fordert die App eine Bestätigung an:

Sobald die Bestellung bestätigt wurde, verarbeitet die App sie und zeigt eine Bestätigungsseite an:

In Wirklichkeit verarbeitet die App die Bestellung nicht. Sie sendet lediglich eine E-Mail an den Nutzer, in der dieser aufgefordert wird, seine Einkäufe zu bezahlen:
Cher client,
Vous trouverez ci-dessous le détail de votre commande au magasin SuperPrix. Elle vous sera livrée après réception de votre chèque établi à l'ordre de SuperPrix et à envoyer à l'adresse suivante :
SuperPrix
ISTIA
62 av Notre-Dame du Lac
49000 Angers
France
Nous vous remercions vivement de votre commande
----------------------------------------
Votre commande
----------------------------------------
article, quantité, prix unitaire, total
========================================
vélo, 2, 1202.00 F, 2404.00 F
skis nautiques, 3, 1800.00 F, 5400.00 F
Total à payer : 7804 F
Frage: Erstellen Sie das Äquivalent dieser Webanwendung mithilfe eines Java-Applets.

