Skip to content

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:

Image

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

Image

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:

Image

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:

  1. Verbindung zur Datenbank herstellen
  2. Senden von SQL-Abfragen an die Datenbank
  3. Empfangen und Verarbeiten der Ergebnisse dieser Abfragen
  4. 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:

Image

Image

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

Image

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:

Connection DriverManager.getConnection(String URL, String id, String mdp)

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:

    Class.forName(String nomClasse)

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:

    import java.sql.*;

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:

    Statement requete=connexion.CreateStatement();

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:

    select col1, col2,... from table1, table2,...
    where condition
    order by expression
    ...

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.

  1. Eine Verknüpfung wird mit allen Tabellen durchgeführt, die nach dem Schlüsselwort `FROM` aufgeführt sind
  2. Es werden nur die Spalten beibehalten, die auf das Schlüsselwort `select` folgen
  3. Es werden nur die Zeilen beibehalten, die die Bedingung des `where`-Schlüsselworts erfüllen
  4. 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 &lt; 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:

    ResultSet executeQuery(String requête)

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:

    boolean next()

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:

Type getType("coli") 

, 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

Type getType(i) 

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:

    RS.getMetaData()

Die Klasse ResultSetMetaData enthält zwei nützliche Methoden:

  1. int getColumnCount(), die die Anzahl der Spalten im ResultSet zurückgibt
  2. 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:

    int executeUpdate(String requête)

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:

    boolean execute(String requête)

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:

E:\data\java\jdbc\0>java sql3 sun.jdbc.odbc.JdbcOdbcDriver jdbc:odbc:articles <requetes >results

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 é 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 é 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 é 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:

Image

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

DSN
der DSN-Name der ODBC-Datenquelle ist
login
die Identität eines Benutzers mit Lesezugriff auf die Datenbank
Passwort
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

Image

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

Image

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

Image

  • 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:

    private double[] limites, coeffR, coeffN;

hat die Klasse „importsJDBC“ keinen direkten Zugriff auf diese Felder. Wir nehmen daher zunächst eine Änderung an der Basisklasse vor, indem wir schreiben:

  protected double[] limites=null;
  protected double[] coeffR=null;
  protected double[] coeffN=null;

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:

  // constructeur vide
  protected impots(){}

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:

Image

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
200 - Bienvenue - Envoyez votre requête
  • Der Client sendet daraufhin die für die Verbindung zu einer Datenbank erforderlichen Parameter sowie die auszuführende Abfrage im folgenden Format:
Pilote Java/URL base/UID/MDP/requête SQL

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:
100 - ligne1
100 - ligne2

...

wenn es sich um das Ergebnis einer Select-Abfrage handelt, oder

101 - nbLignes

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

500 - Message d’erreur
  • 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:

Pilote Java/URL base/UID/MDP/requête SQL

, 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:

Image

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

Erstellen des Arbeitsblatts in VB

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üs

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
Hinzufügen
mnuadd
um einen neuen Datensatz zur Tabelle „items“ hinzuzufügen
Löschen
delete
, um den aktuell angezeigten Datensatz aus der Tabelle „items“ zu löschen
Bearbeiten
Bearbeiten
um den aktuell angezeigten Datensatz in der Artikeltabelle zu bearbeiten
Zurück
mnuprecedent
um zum vorherigen Datensatz zu wechseln
Weiter
nach unten-weiter
zum nächsten Eintrag springen
Zurück
Zurück
zum ersten Titel springen
Letzter
mnunlast
zum letzten Titel springen
Beenden
mnuquit
um die Anwendung zu beenden

Laden des Blattes

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).


Teil 1

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).

Teil 2 – Menüverwaltung

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

Teil 3 – Verwaltung des horizontalen Schiebereglers

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

    data1.recordset.absoluteposition=position.value

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:

    position.value=data1.recordset.absoluteposition

Teil 4 – Suchoption

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.

Teil 5 – Code-Verwaltung

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:

Image

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:

Image

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:

Image

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.

Image

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

Image

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

Image

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.