Différents types d'applications existent; examinons un classement.
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.
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.
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 |
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: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).
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.
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.
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.
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);} }
public static void main(String[] args) { . . . }
try { Class pil = Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); . . . } catch(ClassNotFoundException e) { e.printStackTrace(); } catch(SQLException e) { while( e != null ) { System.out.println(e.getMessage()); System.out.println(e.getSQLState()); System.out.println(e.getErrorCode()); } e.printStackTrace(); }
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.
Une exception (SQLException) est levée si la connexion ne peut pas être ouverte.
Une connexion est par défaut en mode 'autocommit', ce qui entraîne la validation de chaque requête; ce mode peut être changé afin de pouvoir valider ( con.commit() ) ou annuler (con.rollback() ) une ou plusieurs requêtes consécutives.
Une connexion doit être fermée dès que la base n'est plus sollicitée: con.close();
Ces méthodes déclenchent une 'SQLException' si la base ou une table n'est pas accessible.
while (res.next ()) { for (i=1; i<=nCols; i++) { System.out.print(res.getString(i)); } }
ResultSetMetaData md = res.getMetaData (); int nCols = md.getColumnCount (); md.getColumnType(i) md.getColumnName(i) md.getColumnLabel(i) md.getColumnDisplaySize(i)
Driver | pilote de connexion, appelé par le d.manager | |
Connection | ouverture d'une session sur un sgbd | |
DataBaseMetaData | méta-information (connexion, tables,colonnes ...) | |
Statement | exécution d'une requette SQL | |
PreparedStatement | requête SQL avec paramètres | |
CallableStatement | appel de procédure stockée | |
ResultSet | ensemble de lignes, résultat d'un 'Select' | |
ResultSetMetaData | attributs d'un ResultSet | |
en plus: Array, Blob, Clob |
DriverManager | envoie les demandes au pilote adéquat | |
DriverPropertyInfo | accès aux propriétés du pilote | |
Date | extension de la classe java.util.Date au format SQL | |
Time | extension de la classe java.util.Date | |
Timestamp | extension de la classe java.util.Date | |
Types | constantes désignant les types SQL |
DataTruncation | avertissement quand une donnée est tronquée par conversion | |
SQLException | Lien avec erreur SQL (normalisée par XOPEN) | |
SQLWarning | voir Connection.getWarnings Statement.getWarnings ResultSet.getWarnings |
/** 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); }
/** 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); }
/** 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