IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java

  Chapitre 15 - Informatique distribuée

pages : 1 2 3 4 5 6 7 8 9 10 11 12 

Étape 2 : Configurer la base de données

Ici aussi, ceci est spécifique à Windows 32-bits ; vous devrez sans doute rechercher quelque peu pour savoir comment faire sur votre propre plate-forme.

Pour commencer, ouvrir le panneau de configuration. Vous devriez trouver deux icônes traitant de ODBC. « Il faut utiliser celle qui parle de ODBC 32-bits, l'autre n'étant là que pour la compatibilité ascendante avec le software ODBC 16-bits, et ne serait d'aucune utilité pour JDBC. En ouvrant l'icône ODBC 32-bits, vous allez voir une boîte de dialogue à onglets, parmi lesquels User DSN, » System DSN,[ File DSN, « etc., DSN signifiant » Data Source Name. Il apparaît que pour la passerelle JDBC-ODBC, le seul endroit important pour créer votre base de données est System DSN, mais vous devez également tester votre configuration et créer des demandes, et pour cela vous devez également créer votre base dans File DSN. Ceci permet à l'outil Microsoft Query (qui fait partie de Microsoft Office) de trouver la base. Remarquez que d'autres outils de demande sont disponibles chez d'autres vendeurs.

La base de données la plus intéressante est l'une de celles que vous utilisez déjà. Le standard ODBC supporte différents formats de fichier y compris les vénérables chevaux de labour tels que DBase. Cependant, il inclut aussi le format « ASCII, champs séparés par des virgules », que n'importe quel outil de données est capable de créer. En ce qui me concerne, j'ai simplement pris ma base de données « people » « que j'ai maintenue depuis des années au moyen de divers outils de gestion d'adresse et que j'ai exportée en tant que fichier « ASCII à champs séparés par des virgules » (ils ont généralement une extension .csv). Dans la section » System DSN « j'ai choisi [Add, « puis le driver texte pour mon fichier ASCII csv, puis dé-coché « [use current directory » « pour me permettre de spécifier le répertoire où j'avais exporté mon fichier de données.

Remarquez bien qu'en faisant cela vous ne spécifiez pas réellement un fichier, mais seulement un répertoire. Ceci parce qu'une base de données se trouve souvent sous la forme d'un ensemble de fichiers situés dans un même répertoire (bien qu'elle puisse aussi bien se trouver sous d'autres formes). Chaque fichier contient généralement une seule table, et une instruction SQL peut produire des résultats issus de plusieurs tables de la base (on appelle ceci une relation). Une base contenant une seule table (comme ma base « [people ») est généralement appelée flat-file database. La plupart des problèmes qui vont au-delà du simple stockage et déstockage de données nécessitent des tables multiples mises en relation par des relationsafin de fournir les résultats voulus, on les appelle bases de données relationnelles.

Étape 3 : Tester la configuration

Pour tester la configuration il faut trouver un moyen de savoir si la base est visible depuis un programme qui l'interrogerait. Bien entendu, on peut tout simplement lancer le programme exemple JDBC ci-dessus, en incluant l'instruction :

Connection c = DriverManager.getConnection(
  dbUrl, user, password);

Si une exception est lancée, c'est que la configuration était incorrecte.

Cependant, à ce point, il est très utile d'utiliser un outil de génération de requêtes. J'ai utilisé Microsoft Query qui est livré avec Microsoft Office, mais vous pourriez préférer un autre outil. L'outil de requête doit connaître l'emplacement de la base, et Microsoft Query exigeait que j'ouvre l'onglet administrateur ODBC » File DSN « et que j'ajoute une nouvelle entrée, en spécifiant à nouveau le driver texte et le répertoire contenant ma base de données. On peut donner n'importe quel nom à cette entrée, mais il est préférable d'utiliser le nom déjà fourni dans l'onglet » System DSN.]

Ceci fait, vous saurez si votre base est disponible en créant une nouvelle requête au moyen de votre générateur de requêtes.

Étape 4 : Générer votre requête SQL

La requête créée avec Microsoft Query m'a montré que ma base de données était là et prête à fonctionner, mais a aussi généré automatiquement le code SQL dont j'avais besoin pour l'insérer dans mon programme Java. Je voulais une requête qui recherche les enregistrements contenant le même nom que celui qui était fourni sur la ligne de commande d'appel du programme. Ainsi pour commencer, j'ai cherché un nom particulier, [Eckel. ]Je voulais également n'afficher que ceux qui avaient une adresse email associée. Voici les étapes que j'ai suivi pour créer cette requête :

  1. Ouvrir une nouvelle requête au moyen du Query Wizard. Sélectionner la base « [people » (ceci est équivalent à ouvrir la connexion avec la base au moyen de l'URL de base de données appropriée).
  2. Dans la base, sélectionner la table « people ». Dans la table, choisir les colonnes FIRST, LAST, et EMAIL.
  3. Sous « Filter Data », choisir LAST et sélectionner « equals » avec comme argument « Eckel ». Cliquer sur le bouton radio « And ».
  4. Choisir EMAIL et sélectionner « Is not Null ».]
  5. Sous « Sort By », « choisir FIRST.

Le résultat de cette requête vous montrera si vous obtenez ce que vous vouliez.

Maintenant cliquez sur le bouton SQL et sans aucun effort de votre part vous verrez apparaître le code SQL correct, prêt pour un copier-coller. Le voici pour cette requête :

SELECT people.FIRST, people.LAST, people.EMAIL
FROM people.csv people
WHERE (people.LAST='Eckel') AND
(people.EMAIL Is Not Null)
ORDER BY people.FIRST

Il serait très facile de se tromper dans le cas de requêtes plus compliquées, mais en utilisant un générateur de requêtes vous pouvez tester votre requête interactivement et générer automatiquement le code correct. Il serait difficile d'affirmer qu'il est préférable de réaliser cela manuellement.

Étape 5 : Modifier et insérer votre requête

Remarquons que le code ci-dessus ne ressemble pas à celui qui est utilisé dans le programme. Ceci est dû au fait que le générateur de requêtes utilise des noms complètement qualifiés, même lorsqu'une seule table est en jeu (lorsqu'on utilise plus d'une table, la qualification empêche les collisions entre colonnes de même nom appartenant à des tables différentes). Puisque cette requête ne concerne qu'une seule table, on peut - optionnellement - supprimer le qualificateur « people » dans la plupart des noms, de cette manière :

SELECT FIRST, LAST, EMAIL
FROM people.csv people
WHERE (LAST='Eckel') AND
(EMAIL Is Not Null)
ORDER BY FIRST

En outre, on ne va pas coder en dur le nom à rechercher. Au lieu de cela, il sera créé à partir du nom fourni en argument sur la ligne de commande d'appel du programme. Ces changements effectués l'instruction SQL générée dynamiquement dans un objet String devient :

"SELECT FIRST, LAST, EMAIL " +
"FROM people.csv people " +
"WHERE " +
"(LAST='" + args[0 « + "') " +
" AND (EMAIL Is Not Null) " +
"ORDER BY FIRST");

SQL possède un autre mécanisme d'insertion de noms dans une requête, appelé procédures stockées, stored procedures, utilisées pour leur vitesse. Mais pour la plupart de vos expérimentations sur les bases de données et pour votre apprentissage, il est bon de construire vos chaînes de requêtes en Java.

On peut voir à partir de cet exemple que l'utilisation d'outils généralement disponibles et en particulier le générateur de requêtes simplifie la programmation avec SQL et JDBC..

Une version GUI du programme de recherche

Il serait plus pratique de laisser tourner le programme en permanence et de basculer vers lui pour taper un nom et entamer une recherche lorsqu'on en a besoin. Le programme suivant crée le programme de recherche en tant qu'application/applet, et ajoute également une fonctionnalité de complétion des noms afin qu'on puisse voir les données sans être obligé de taper le nom complet :

//: c15:jdbc:VLookup.java
// version GUI de Lookup.java.
// <applet code=VLookup
// width=500 height=200></applet>
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.event.*;
import java.sql.*;
import com.bruceeckel.swing.*;

public class VLookup extends JApplet {
  String dbUrl = "jdbc:odbc:people";
  String user = "";
  String password = "";
  Statement s;
  JTextField searchFor = new JTextField(20);
  JLabel completion =
    new JLabel("                        ");
  JTextArea results = new JTextArea(40, 20);
  public void init() {
    searchFor.getDocument().addDocumentListener(
      new SearchL());
    JPanel p = new JPanel();
    p.add(new Label("Last name to search for:"));
    p.add(searchFor);
    p.add(completion);
    Container cp = getContentPane();
    cp.add(p, BorderLayout.NORTH);
    cp.add(results, BorderLayout.CENTER);
    try {
      // Charger le driver (qui s'enregistrera lui-même)
      Class.forName(
        "sun.jdbc.odbc.JdbcOdbcDriver");
      Connection c = DriverManager.getConnection(
        dbUrl, user, password);
      s = c.createStatement();
    } catch(Exception e) {
      results.setText(e.toString());
    }
  }
  class SearchL implements DocumentListener {
    public void changedUpdate(DocumentEvent e){}
    public void insertUpdate(DocumentEvent e){
      textValueChanged();
    }
    public void removeUpdate(DocumentEvent e){
      textValueChanged();
    }
  }
  public void textValueChanged() {
    ResultSet r;
    if(searchFor.getText().length() == 0) {
      completion.setText("");
      results.setText("");
      return;
    }
    try {
      // compléter automatiquement le nom:
      r = s.executeQuery(
        "SELECT LAST FROM people.csv people " +
        "WHERE (LAST Like '" +
        searchFor.getText()  +
        "%') ORDER BY LAST");
      if(r.next())
        completion.setText(
          r.getString("last"));
      r = s.executeQuery(
        "SELECT FIRST, LAST, EMAIL " +
        "FROM people.csv people " +
        "WHERE (LAST='" +
        completion.getText() +
        "') AND (EMAIL Is Not Null) " +
        "ORDER BY FIRST");
    } catch(Exception e) {
      results.setText(
        searchFor.getText() + "\n");
      results.append(e.toString());
      return;
    }
    results.setText("");
    try {
      while(r.next()) {
        results.append(
          r.getString("Last") + ", "
          + r.getString("fIRST") +
          ": " + r.getString("EMAIL") + "\n");
      }
    } catch(Exception e) {
      results.setText(e.toString());
    }
  }
  public static void main(String[ « args) {
    Console.run(new VLookup(), 500, 200);
  }
} ///:~

La logique « base de données » est en gros la même, mais on peut remarquer qu'un DocumentListener est ajouté pour écouter l'entrée JTextField (pour plus de détails, voir l'entrée javax.swing.JTextField dans la documentation HTML Java sur java.sun.com), afin qu'à chaque nouveau caractère frappé le programme tente de compléter le nom en le cherchant dans la base et en utilisant le premier qu'il trouve (en le plaçant dans le JLabel completion, et en l'utilisant comme un texte de recherche). De cette manière, on peut arrêter de frapper des caractères dès qu'on en a tapé suffisamment pour que le programme puisse identifier de manière unique le nom qu'on cherche.

Pourquoi l'API JDBC paraît si complexe

Naviguer dans la documentation en ligne de JDBC semble décourageant. En particulier, dans l'interface DatabaseMetaData qui est vraiment énorme, à l'inverse de la plupart des interfaces rencontrées jusque là en Java, on trouve des méthodes telles que dataDefinitionCausesTransactionCommit( ), getMaxColumnNameLength( ), getMaxStatementLength( ), storesMixedCaseQuotedIdentifiers( ), supportsANSI92IntermediateSQL( ), supportsLimitedOuterJoins( ), et ainsi de suite. Qu'en est-il de tout cela ?

Ainsi qu'on l'a dit précédemment, les bases de données semblent être depuis leur création dans un constant état de bouleversement, principalement à cause de la très grande demande en applications de base de données, et donc en outils de base de données. Ce n'est que récemment qu'on a vu une certaine convergence vers le langage commun SQL (et il existe beaucoup d'autres langages d'utilisation courante de base de données). Mais même le « standard » SQL possède tellement de variantes que JDBC doit fournir la grande interface DatabaseMetaData afin que le code puisse utiliser les possibilités de la base SQL « standard » particulière avec laquelle on est connecté. Bref, il est possible d'écrire du code SQL simple et portable, mais si on veut optimiser la vitesse le code va se multiplier terriblement au fur et à mesure qu'on découvre les possibilités d'une base de données d'un vendeur particulier.

Bien entendu, ce n'est pas la faute de Java. Ce dernier tente seulement de nous aider à compenser les disparités entre les divers produits de base de données . Mais gardons à l'esprit que la vie est plus facile si l'on peut aussi bien écrire des requêtes génériques sans trop se soucier des performances, ou bien, si l'on veut améliorer les performances, connaître la plate-forme pour laquelle on écrit afin de ne pas avoir à traîner trop de code générique.

Un exemple plus sophistiqué

Un exemple plus intéressant [73] est celui d'une base de données multi-tables résidant sur un serveur. Ici, la base sert de dépôt pour les activités d'une communauté et doit permettre aux gens de s'inscrire pour réaliser ces actions, c'est pourquoi on l'appelle une base de données de communauté d'intérêts, Community Interests Database (CID). Cet exemple fournira seulement une vue générale de la base et de son implémentation, il n'est en aucun cas un tutoriel complet à propos du développement des bases de données. De nombreux livres, séminaires, et packages de programmes existent pour vous aider dans la conception et le développement des bases de données.

De plus, cet exemple implique l'installation préalable d'une base SQL sur un serveur (bien qu'elle puisse aussi bien tourner sur la machine locale), ainsi que la recherche d'un driver JDBC approprié pour cette base. Il existe plusieurs bases SQL libres, et certaines sont installées automatiquement avec diverses distributions de Linux. Il est de votre responsabilité de faire le choix de la base de données et de localiser son driver JDBC ; cet exemple-ci est basé sur une base SQL nommée « Cloudscape ».

Afin de simplifier les modifications d'information de connexion, le driver de la base, son URL, le nom d'utilisateur et son mot de passe sont placés dans une classe séparée :

//: c15:jdbc:CIDConnect.java
// information de connexion à la base de données pour
// la «base de données de communauté d'intérêt» (CID).

public class CIDConnect {
  // Toutes les informations spécifiques à CloudScape:
  public static String dbDriver =
    "COM.cloudscape.core.JDBCDriver";
  public static String dbURL =    "jdbc:cloudscape:d:/docs/_work/JSapienDB";
  public static String user = "";
  public static String password = "";
} ///:~

Dans cet exemple, il n'y a pas de protection de la base par mot de passe, le nom d'utilisateur et le mot de passe sont des chaînes vides.

La base de données comprend un ensemble de tables dont voici la structure :

border="0" alt="Image">

« Members » contient les informations sur les membres de la communauté, « Events » et « Locations » des informations à propos des activités et où on peut les trouver, et « Evtmems » connecte les événements et les membres qui veulent suivre ces événements. On peut constater qu'à une donnée « membre » d'une table correspond une clef dans une autre table.

La classe suivante contient les chaînes SQL qui vont créer les tables de cette base (référez-vous à un guide SQL pour une explication du code SQL) :

//: c15:jdbc:CIDSQL.java
// chaînes SQL créant les tables pour la CID.

public class CIDSQL {
  public static String[ « sql = {
    // Créer la table MEMBERS:
    "drop table MEMBERS",
    "create table MEMBERS " +
    "(MEM_ID INTEGER primary key, " +
    "MEM_UNAME VARCHAR(12) not null unique, "+
    "MEM_LNAME VARCHAR(40), " +
    "MEM_FNAME VARCHAR(20), " +
    "ADDRESS VARCHAR(40), " +
    "CITY VARCHAR(20), " +
    "STATE CHAR(4), " +
    "ZIP CHAR(5), " +
    "PHONE CHAR(12), " +
    "EMAIL VARCHAR(30))",
    "create unique index " +
    "LNAME_IDX on MEMBERS(MEM_LNAME)",
    // Créer la table EVENTS:
    "drop table EVENTS",
    "create table EVENTS " +
    "(EVT_ID INTEGER primary key, " +
    "EVT_TITLE VARCHAR(30) not null, " +
    "EVT_TYPE VARCHAR(20), " +
    "LOC_ID INTEGER, " +
    "PRICE DECIMAL, " +
    "DATETIME TIMESTAMP)",
    "create unique index " +
    "TITLE_IDX on EVENTS(EVT_TITLE)",
    // Créer la table EVTMEMS:
    "drop table EVTMEMS",
    "create table EVTMEMS " +
    "(MEM_ID INTEGER not null, " +
    "EVT_ID INTEGER not null, " +
    "MEM_ORD INTEGER)",
    "create unique index " +
    "EVTMEM_IDX on EVTMEMS(MEM_ID, EVT_ID)",
    // Créer la table LOCATIONS:
    "drop table LOCATIONS",
    "create table LOCATIONS " +
    "(LOC_ID INTEGER primary key, " +
    "LOC_NAME VARCHAR(30) not null, " +
    "CONTACT VARCHAR(50), " +
    "ADDRESS VARCHAR(40), " +
    "CITY VARCHAR(20), " +
    "STATE VARCHAR(4), " +
    "ZIP VARCHAR(5), " +
    "PHONE CHAR(12), " +
    "DIRECTIONS VARCHAR(4096))",
    "create unique index " +
    "NAME_IDX on LOCATIONS(LOC_NAME)",
  };
} ///:~

Ce livre a été écrit par Bruce Eckel ( télécharger la version anglaise : Thinking in java )
Ce chapitre a été traduit par Jean-Pierre Vidal, Alban Peignier ( groupe de traduction )
télécharger la version francaise (PDF) | Commandez le livre en version anglaise (amazon) | télécharger la version anglaise
pages : 1 2 3 4 5 6 7 8 9 10 11 12 
Penser en Java 2nde édition - Sommaire |  Préface |  Avant-propos | Chapitre : 1  2  3  4  5  6  7  8  9  10  11  12  13  14  15 |  Annexe : A B C D  | Tables des matières - Thinking in Java