Java et accès BD

I. Environnement

  1. Applications multi niveaux

    De plus en plus souvent une application utilise plusieurs machines. Par exemple pour réserver une place de train on utilise un navigateur, situé sur une machine 'cliente', pour dialoguer avec un site de réservations, situé, lui, sur une machine 'serveur' utilisée pour traiter la demande (contenant au minimum un système de gestion de base de données et un serveur Web).

    Différents types d'applications existent; examinons un classement.

    • Une application standard peut être divisée en 3 parties:
      • une interface utilisateur: saisie d'information, choix de traitement, présentation des résultats
      • une partie de lecture ou d'écriture des données
      • et une partie de traitement des données

      On parle d'applications deux niveaux quand l'interface utilisateur et le traitement des données sont sur une machine, alors que les données résident sur une autre machine.
      L'application a trois niveaux quand interface utilisateur, traitement des données et données sont respectivement sur des machines différentes.

    • application internet / intranet: deux machines (au moins) interviennent:
      • machine 1: contient le navigateur, qui sert d'interface utilisateur
      • machine 2: contient au moins le serveur HTTP qui reçoit une requête, l'évalue et renvoie le résultat en langage HTML.

        La requête peut se réduire à l'examen d'une page existant sur la machine; elle est immédiatement gérée par le serveur Http.
        Mais la requête peut être plus complexe et le serveur délègue alors son traitement à un module auxiliaire (technologies cgi, asp, isapi, nsapi, php, servlet) pouvant lui même s'adresser à une troisième machine.

  2. Java

    Java s'utilise de trois façons: application, applet ou servlet; dans chaque cas une 'machine virtuelle' interprète (sur une machine réelle) un p-code, obtenu après une étape de compilation à partir de classes écrites en langage Java. Cette machine est, dans chaque cas: Le p-code peut circuler sur le réseau et s'interprète sur n'importe quelle machine réelle équipée d'un des éléments précités.
    Utilisations du pseudo-code java
    contexte
    d'utilisation
    classe Java méthode interprétation du p-code
    application   main exécutable java: mac.vituelle (m.v.J )
    applet dérive de Applet init navigateur +  m.v.J
    servlet dérive de HttpServlet init serveur Http + mod. JServ +  m.v.J

  3. Sql et Bd

    Pour les concepteurs de Java, en 1997 le problème se pose d'ajouter au langage des fonctionnalités d'accès à des bases de données en respectant des conditions de sécurité, et en préservant l'indépendance du code écrit vis à vis des divers SGBD.

    A cette date la situation, à propos d'accès à une base, est la suivante:
    • les bases de données relationnelles sont très largement utilisées
    • le langage SQL est relativement normalisé
    • les protocoles réseaux entre machines sont 'propriétaires'
    • un effort de standardisation existe sur les systèmes Windows, avec ODBC (open data base connectivity), technologie permettant de séparer les exécutables traitant les données issues d'un SGBG, des pilotes permettant l'accès au SGBD.

    Pour les concepteurs du langage, il faut définir des classes et des interfaces permettant d'écrire du code Java, qui ne doit pas être modifié , ou le moins possible, quand on change de SGBD, et respecte la normalisation de l'X/Open SQL CLI.

    Les classes à concevoir doivent permettre d'établir une connexion, d'effectuer des requêtes simples (exprimées en langage SQL-92), paramétrées ou faisant appel à des procédures stockées, d'extraire les résultats d'une requête, et d'obtenir un certain nombre de services (méta données, conversion de types Java/SQL, gérer une transaction).

  4. Liaison BD

    Envisageons une application accédant à un SGBD relationnel, en utilisant le langage SQL; le SGBD est souvent situé sur une autre machine, donc il faut y accéder par un réseau. Comment cette application dépend elle du SGBD ? son code va contenir:
    • un partie de préparation des requêtes SQL presque indépendante du SGBD
    • une partie d'échange de données par le réseau, qui dépend fortement du protocole utilisé par le gestionnaire de la base;
    • des traitements qui sont indépendants du SGBD.

    Pour avoir un maximum d'indépendance entre l'application et le Sgbd, La technologie actuelle isole les accès réseau du module principal de l'application. Ces accès sont exécutés par des pilotes (drivers), fournis par les éditeurs de SGBD. Cela évite la modification du code principal quand le Sgbd change, car on ne change alors que le pilote.

II. JDBC

  1. Paquetages JDBC

    La solution proposée est une Api (application programming interface) de bas niveau, composée de classes et d'interfaces se trouvant dans le paquetage java.sql. Il existe en fait 2 Api:
    • API  Jdbc: utilisée par le programmeur, répondant aux fonctionnalités SQL,
    • API  JdbcDriver: utilisée par concepteur de pilote, dialogue entre la machine virtuelle Java et le SGBD.

  2. Pilotes

    Un pilote JDBC va implanter les interfaces qui composent JDBC pour un SGBD particulier; ce pilote a un nom qui doit être connu du DriverManager à l'exécution, et qui sert à son chargement par la machine virtuelle, avant d'établir la première connexion à une base.

    Chaque éditeur de base a ses propres pilotes JDBC. Il existe aussi un type de pilote qui n'accède pas directement à une base mais dialogue avec des pilotes ODBC (implantés sur PC-Windows); cela permet, sur PC avec Windows, d'utiliser des SGBD accessibles par ODBC; ce pilote, développé par Sun, s'appelle: sun.jdbc.odbc.JdbcOdbcDriver. Grace à lui un exécutable sous Windows, faisant des accès Sgbd par ODBC, peut immédiatement être remplacé par un programme Java.

    Voyons plusieurs cas suivant que la base est locale (colonnes 1 et 4) ou distante(colonnes 2 et 3) et qu'on dispose (colonnes 1 et 2) ou non (colonnes 3 et 4) d'un pilote ODBC.

    Architectures possibles à l'exécution
    1 2 3 4
    le Driver Manager
    charge
    le Driver Manager
    charge
    le Driver Manager
    charge
    le Driver Manager
    charge
    JDBC/ODBC Driver
    qui dialogue avec
    JDBC/ODBC Driver
    qui dialogue avec
    JDBC Driver3
    qui dialogue avec
    JDBC Driver4
    qui dialogue avec
    ODBC Driver1
    qui dialogue avec
    ODBC Driver2
    qui dialogue avec
    couche réseau Sgbd local
    Sgbd local couche réseau Sgbd distant  
      Sgbd distant    
    Driver1, Driver2, Driver3 et Driver4 sont spécifiques au SGBD utilisé

    Les classes qui composent JDBC ne dépendent pas du Sgbd; elles sont regroupées dans le paquetage java.sql.

  3. Sécurité

    Sécurité des applets: l'accès à une base ne peut se faire que si la base est sur la machine d'où provient l'applet; en fait le pilote JDBC ne peut se connecter qu'aux bases situées sur la machine où il est chargé; le pilote doit aussi vérifier que l'applet qui le sollicite est autorisée à le faire. Ces vérifications ne concernent pas l'utilisation de la base. Une applet ne peut pas accéder à des données locales.

III. Programmation

  1. Premier exemple

    Voyons un exemple d'interrogation simple du contenu d'une table, qui montre comment établir la connexion, interroger la table et récupérer son contenu pour affichage, par l'intermédiaire d'un objet  ResultSet.

     /**  AvecJdbc.java */
    
    import java.sql.*;
    import sun.jdbc.odbc.JdbcOdbcDriver;
    
    public class AvecJdbc {
       public static void main(String[] args) {
       // Connexion, requête et résultats affichés à la console
       try {
          // Charger le pilote  jdbc-odbc
          Class pil = Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
          // Connexion avec choix du pilote
          Connection con = DriverManager.getConnection (
                                  "jdbc:odbc:Base Voile", "U", "mdp");
          // Création d'un objet 'requête'
          Statement req = con.createStatement();
    
          // Définir, envoyer la reqête, et récupérer le résultat
          ResultSet res = req.executeQuery("Select * From Voilier");
    
          // Afficher le résultat, ligne à ligne
          int i;
          while( res.next()) {
             // afficher les cinq valeurs d'une ligne
             for(i=1; i<=5; i++)  a(res.getString(i)+'\t');
             a("\n");
             }
    
          // /Fermer la connexion
          con.close();
          }
    
       catch(Exception e) { e.printStackTrace(); }
       }
    
    /** Utilitaire d'affichage */
    static void a(String txt) {System.out.print(txt);}
    }
    
  2. Définir la base

    L'instruction pour établir la connexion contient la désignation de la base, l'utilisateur autorisé à se connecter, et son mot de passe, comme ci-dessous: La 'ressource' base de données est indiquée avec une syntaxe analogue à celle d'une URL, comme par exemple:
        "jdbc:interbase://localhost/c:\mesdonnees\mabase.gdb"
    avec le 'protocole' jdbc indiqué en premier comme dans les URL: "http:..." ou "mail:...".

    Le format d'écriture de cette pseudo-URL est:   jdbc:<sous-protocole>:< chaîne> Exemples:
    jdbc:odbc:Voile
    base enregistrée par ODBC sous le nom Voile; l'emplacement des fichiers est précisé à l'enregistrement de cette base auprès de l'administrateur ODBC
    jdbc:odbc:Voile;CacheSize=20;ExtensionCase=LOWER
    base enregistrée comme précédemment et paramètres supplémentaires utilisés par odbc
    odbc:dbnet://wombat.com:356/Voile
    pilote dbnet  qui interprète les paramètres   //wombat.com:356/Voile
    jdbc:interbase://localhost/c:\mesdonnees\mabase.gdb
    pilote interbase qui interprète:  //localhost/c:\mesdonnees\mabase.gdb

  3. Rappel des opérations à effectuer

    pour accéder à un SGDB.

  4. Niveaux supérieurs

    Dans la programmation d'applications accédant à un SGDB, JDBC ne traite que des aspects bas niveaux. Il existe des classes, développées au-dessus de JDBC, fournissant des objets très utiles pour connaître et exploiter les informations d'une base; nous les trouvons dans les outils RAD (de développements rapides). Citons:
    • Objets d'interfaces graphiques:
      • construction de schémas de base
      • construction de requêtes
      • présentation des données (table, barre de navigation, barre d'état ... gestion d'événements)
      • gestion du transactionnel
    • Classes pour aider le programmeur
      • contenu d'une table, du résultat d'un 'Select'
    • Utilisation d'un objet d'interface utilisateur pour visualiser les données.
      • dans la base: des tables avec des colonnes ayant un nom, un type SQL défini et un ordre (non significatif)
      • un ResultSet avec des colonnes ayant un nom, un alias, un type Java, un statut (modifiable ou non), et un ordre (lié à l'écriture de la requête d'interrogation).
        Cet objet est parcouru par ligne afin d'avoir l'ensemble des résultats.
      • un JTable, par exemple, avec:
        • un titre, des barres de défilement,...
        • un comportement événementiel (gestion du défilement ...)
        • des colonnes ayant des attributs d'affichage (couleurs, nom ou icône, largeur, hauteur d'une cellule, police de caractères ...); on peut avoir plus de colonnes que dans la requête si on désire montrer un résultat de calcul (colonne calculée).
        • chaque colonne a un comportement événementiel lié à l'autorisation ou pas d'être modifiée

IV. Le paquetage java.sql

Dans ce paragraphe, après avoir détaillé l'écriture l'utilisation des méthodes les plus courantes, nous définissons en un mot les diverses interfaces ou classes du paquetage.

  1. Les méthodes les plus importantes des classes et interfaces

  2. Résumé des classes et interfaces

    Indiquons en un mot le rôle des interfaces, des classes et des classes d'exception du paquetage JDBC.

V. Exemple avec interface utilisateur

Nous voyons enfin une application plus conséquente dans laquelle un objet d'interface utilisateur, de classe JTable est utilisé pour visualiser les résultats d'une requête. Les lignes de cet objet JTable peuvent ou non être modifiées; son contenu est  parcouru, si nécessaire, en s'aidant par des barres de défilement. Cet objet réagit à la souris et au clavier, bref il a un comportement événementiel qui est géré par la classe JTable.

Nous l'utilisons de façon simple car chaque colonne de cet objet correspond à une colonne de la table SQL, mais ce n'est pas obligatoire; on pourait afficher moins de colonnes que dans la table SQL si certaines informations doivent être cachées, ou afficher plus de colonnes si, nous voulons calculer des informations non contenues dans la table SQL pour chaque enregistrement (colonnes dites calculées).
La programmation peut être découpée en trois parties:
a)  la déclaration des paquetages utilisés et la construction de l'interface;
b) le remplissage du composant JTable par un accès à une base: méthode JTable remplirTable() . Dans cette méthode la connexion au SGBD est établie. On y voit aussi comment récupérer des méta données sur le résultat de la requête SQL, comme   le nombre de colonnes ou le nom des colonnes.
c) une utilisation des données, sur un clic souris, qui est un simple affichage, mais pourrait être une mise à jour de la table SQL dans le SGBD.
     
  1. Déclarations et construction de l'interface

    /** Dans Req2.java
      accès à une base et affichage dans une table swing
    La table Voilier:
        nvoi type prix etat base
        7     Cata 3000 1   Glenan
        8     Cata 2500 1   Nice
           . . .
    */
    import java.sql.*;
    import sun.jdbc.odbc.JdbcOdbcDriver;
    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;    //  JTable, JScrollPane, JPanel, JFrame
    import java.util.Vector;
    
    
    public class Req2  extends JFrame {
      // Construction de l'interface
      public Req2() {
         // Le composant JTable
         JTable table = remplirTable();
         table.setPreferredScrollableViewportSize(new Dimension(500, 70));
         // Un panneau, placé dans la fenêtre
         JScrollPane panneau = new JScrollPane(table);
         getContentPane().add(panneau, BorderLayout.CENTER);
         // Réflexe sur la table: Clic souris -->  affichage des valeurs
         table.addMouseListener( new RefSouris(table) );
    
         // Pour fermer la fenêtre
         addWindowListener( new WindowAdapter() {
             public void windowClosing(WindowEvent e) {System.exit(0);}
               });
         }
    
      // Méthode principale
      public static void main(String[] args) {
         Req2 fen = new Req2();
         fen.pack();
         fen.setLocation( 100, 100);
         fen.setVisible(true);
         }
    
     
  2. Connexion, requête et exploitation des résultats

    /** Requête et résultats dans une JTable */
      JTable remplirTable() {
          Vector tabLignes=new Vector(), nomCols=new Vector();
          int i;
          try {
            // Charger le pilote  jdbc-odbc
            Class pil = Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
            // Connexion avec choix du pilote
            Connection con = DriverManager.getConnection (
                                    "jdbc:odbc:Base Voile", "U", "mdp");
            // Création d'un objet 'requête'
            Statement req = con.createStatement();
            // Définir, envoyer la reqête et récupérer le résultat
            ResultSet res = req.executeQuery("Select * From Voilier");
            // Extraire noms des colonnes; les placer dans Vector nomCols
            ResultSetMetaData md = res.getMetaData();
            nomCols = new Vector();
            for( i=1; i<=md.getColumnCount (); i++)
                 nomCols.add( md.getColumnLabel(i));
            // Mémoriser le résultat dans la JTable
            while( res.next()) {
               // placer les cinq valeurs dans Vector ligne
               Vector ligne=new Vector();
               for(i=1; i<=5; i++) {
                     String ch=res.getString(i);
                     ligne.add(ch);
                     }
               tabLignes.add(ligne);
               }
            // Fermer la connexion
            con.close();
            }
         catch(Exception e) { e.printStackTrace(); }
    
         return new JTable(tabLignes,nomCols);
         }
    
     
  3. Rélexe sur clic souris

       /** Réflexe associé au clic souris (classe interne)
           Un champ mémorise la table swing
       */
       class RefSouris extends MouseAdapter {
          private JTable table;
          RefSouris(JTable t) { table=t; }
          public void mouseClicked(MouseEvent e) {
             AfficherDonnees();
             }
          private void AfficherDonnees() {
             int i,j, numLigs = table.getRowCount(),
                      numCols = table.getColumnCount();
             javax.swing.table.TableModel modele = table.getModel();
             a("\nLa table:");
             for ( i=0; i < numLigs; i++) {
                a("\n    ligne " + i + ":");
                for ( j=0; j < numCols; j++)
                    System.out.print("  " + modele.getValueAt(i, j));
                }
             a("\n");
             }
    	 } // fin class RefSouris
    
      /** Utilitaire: affichage */
      static void a( String txt) {System.out.print(txt);}
      /** Utilitaire: message lié à une exception */
      static void a( String txt, Exception e) {
         a(txt+"  ");
         if( e != null) {
               a( e.getLocalizedMessage()+"\n   --> " + e.toString());
               // e.printStackTrace();
               }
         }
      /*********************************************/
      }   // fin  class   Req2
    

Annexes

 

Des livres

Des liens