|
La technologie JDBC (Java
DataBase Connectivity) est un ensemble de classes
permettant de développer des applications capables
de se connecter à des serveurs de bases
de données (SGBD).
Dans un système
client/serveur, l'accès
aux bases de données avec JDBC peut s'effectuer selon
un modèle à deux couches ou bien un modèle
à trois couches.
Pour le modèle à
deux couches, une application Java est intimement liée
avec une base de données. A cet effet, il faut bien
évidemment disposer, pour la base de données
concernée, d'un pilote JDBC adéquat. Les instructions
SQL sont directement envoyées à la base, cette
dernière renvoyant les résultats par un biais
tout aussi direct. La base de données peut être
exécutée sur la machine locale (celle sur laquelle
l'application Java fonctionne) ou bien sur tout autre ordinateur
du réseau (Intranet ou Internet).
Dans le modèle à
3 couches, une troisième couche (le serveur d'application)
vient s'intercaler entre l'application Java et la base de
données. Les instructions SQL et les résultats
livrés en retour y transitent. Ce modèle présente
l'avantage d'intégrer dans cette couche un contrôle
d'accès.
Les pilotes actuels
sont classés en quatre catégories :
- les passerelles JDBC-ODBC, avec le pilote
ODBC appartenant à une base de données.
Le pilote convertit les appels de données Java en
appel ODBC valide, et les exécute ensuite à
l'aide du pilote ODBC
- un mélange de pilotes natifs et de
pilotes Java. Les appels JDBC sont convertis en appels natifs
pour le serveur de bases de données (Oracle, Sybase,
ou autres).
- les pilotes convertissant les appels JDBC
en un protocole indépendant du SGBD. Un serveur convertit
ensuite ceux-ci dans le protocole SGBD requis (modèle
à 3 couches)
- les pilotes JDBC, convertissant les appels
JDBC directement en un protocole réseau exploité
par le SGBD. Cette solution est à préconiser
dans le cadre d'un intranet
A l'heure actuelle, les bases de données
disposent pratiquement toutes d'un pilote ODBC, étant
donné que nombre d'entre-elles ont été
développées à partir d'interfaces Microsoft.
ODBC
signifie Open Database Connectivity. Il s'agit d'un
format propriétaire défini par Microsoft permettant
la communication entre des clients pour bases de données
fonctionnant sous Windows et les SGBD du marché.
Etant donné qu'il existe
différents types de bases de données, et que
chacune d'entre-elles possède sa propre façon
de traiter les requêtes SQL et de renvoyer les résultats,
ODBC permet d'obtenir des résultats identiques quel
que soit le type de base de données, sans avoir à
modifier le programme d'appel qui transmet la requête.
ODBC n'est toutefois pas la
solution miracle, étant donné d'une part qu'il
ne s'agit pas d'une technologie libre (Microsoft la fait évoluer
à son gré), d'autre part qu'il ne s'agit que
d'une interface "alternative" à celle fournie
par les constructeurs et compliquée à maîtriser.
Afin d'illustrer la
connexion à une base de données, nous allons
nous connecter à une base Access par l'intermédiaire
de la passerelle ODBC-JDBC, en abordant toutes les étapes
de la connexion à une base de données.
Après création
de la base sous Access, il faut dans un premier temps intégrer
la base dans l'administrateur
de source de données ODBC (Panneau de configuration
/ source de données ).
Les étapes de création de la source ODBC sont:
- la sélection du pilote pour la base
de données concernée (dans notre cas le pilote
Microsoft Access Driver)
- la définition du nom de la base et
de son chemin d'accès
- la définition du nom de la source
de données et du mot de passe
L'API (Application
Programming Interface) JDBC, c'est-à-dire la bibliothèque
de classes JDBC, se charge de trois étapes indispensables
à la connexion à une base de données:
- la céation d'une connexion à
la base
- l'envoi d'instructions
SQL
- l'exploitation des résultats provenant
de la base
Tous les objets et les
méthodes relatifs aux bases de données sont
présentes dans le package java.sql, il est donc
indispensable d'importer java.sql.* dans tout programme
se servant de la technologie JDBC.
Pour se connecter à
une base de données déclarée dans l'administrateur
ODBC, il est essentiel de charger dans un premier temps le
pilote JDBC-ODBC (appelé pont JDBC-ODBC) :
Class.forname("sun.jdbc.odbc.JdbcOdbcDriver");
Cette instruction charge le pilote et crée
une instance de cette classe. Pour se connecter à une
base de données particulière, il s'agit ensuite
de créer une instance de la classe Connection
en indiquant la base de données à charger à
l'aide de son URL
String url = "jdbc:odbc:base_de_donnees";
Connection con = DriverManager.getConnection(url);
Le nom de la base de données (ici
base_de_donnees) étant celle déclarée
dans le panneau de configuration
ODBC. La syntaxe de l'URL peut varier légèrement
selon le type de la base de données. Il s'agit généralement
d'une adresse de la forme:
jdbc:sousprotocole:nom
Pour exécuter
une requête SQL, il s'agit dans un premier temps de
créer un objet Statement, pouvant être
obtenu à partir de l'objet Connection. Un objet
ResultSet permettra de récupérer les
données en provenance de l'objet Statement.
String query = "SELECT * FROM Ma_Table;";
ResultSet results;
try {
Statement stmt = con.createStatement();
results = stmt.executeQuery(query);
}
catch(Exception(e){
System.out.println("exception du a la requete");
}
Une fois la connexion
à la base de données établie, il est
possible de demander des informations sur le nom des tables
et le contenu de chaque colonne, ainsi que d'exécuter
des requêtes SQL
afin de récupérer des informations, d'en ajouter
ou bien de les modifier.
Les objets utilisables pour
obtenir des informations sur la base sont:
- DataBaseMetaData: Un objet contenant
les informations sur la base de données en général
(des métadonnées), c'est-à-dire
le nom de la table, les index, la version de la base, ...
- ResultSet: Un objet contenant les
informations sur une table ou le résultat d'une requête.
L'accès aux données se fait colonne par colonne,
mais il est éventuellement possible d'accèder
indépendamment à chaque colonne par son nom
- ResultSetMetaData: Un objet contenant
des informations sur le nom et le type des colonnes d'une
table
A chacun de ces objets est associé
un grand nombre de méthodes permettant d'obtenir des
informations très détaillées sur les éléments
de la base de données.
Toutefois, seul un nombre réduit
des méthodes de chacun de ces objets permet d'obtenir
les informations les plus importantes sur les données.
L'objet ResultSet est
l'objet le plus important de la technologie JDBC, car il s'agit
d'une abstraction de la table ou de la réponse à
une requête (généralement un sous-ensemble
d'une table). Ainsi, presque toutes les méthodes et
requêtes retournent les données sous forme d'un
objet ResultSet. Cet objet contient un nombre donné
de colonnes repérées chacune par un nom, ainsi
qu'une ou plusieurs lignes contenant les données, et
auxquelles il est possible d'accèder séquentiellement
une à une du haut vers le bas. Ainsi, afin d'exploiter
un objet ResultSet, il est nécessaire de récupérer
le nombre de colonnes de celui-ci, à l'aide de l'objet
ResultSetMetaData.
ResultSetMetaData rsmd;
rsmd = results.getMetaData();
numcols = rsmd.getColumnCount();
Lors de l'obtention d'un objet ResultSet,
le descripteur pointe avant la première ligne.
La méthode next()
permet d'obtenir chacune des lignes suivantes, et retourne
false lorsqu'il ne reste plus aucune ligne. Etant donné
que l'extraction de données de la base peut générer
des erreurs, il est indispensable d'inclure ces manipulations
dans un bloc d'exception (try).
Les données contenues
dans le ResultSet peuvent être obtenues sous
différentes formes selon le type de données
stockées dans chaque colonne. Le contenu de chaque
colonne peut être obtenu soit par le nom de celle-ci,
soit par son numéro (sachant que les numéros
de colonne commencent à 1 et non à 0).
Les principales méthodes
de l'objet ResultSet sont les suivantes:
- getInt(int): récupère
sous forme d'entier le contenu d'une colonne désignée
par son numéro
- getInt(String): récupère
sous forme d'entier le contenu d'une colonne désignée
par son nom
- getFloat(int): récupère
sous forme de flottant le contenu d'une colonne désignée
par son numéro
- getFloat(String): récupère
sous forme de flottant le contenu d'une colonne désignée
par son nom
- next(): déplace le pointeur
de ligne sur la ligne suivante
- close(): "ferme" l'objet
- getMetaData(): retourne les métadonnées
de l'objet (l'objet ResultSetMetaData)
L'objet ResultSetMetaData
(obtenu de l'objet ResultSet) permet de connaître
le nombre, le nom et le type de chaque colonne à l'aide
des méthodes suivantes:
- getColumnCount(): récupère
le nombre de colonnes
- getColumnName(int): récupère
le nom de la colonne spécifiée
- getColumnLabel(int): récupère
le label de la colonne spécifiée
- getColumnType(int): récupère
le type de données de la colonne spécifiée
L'objet DataBaseMetaData permet d'obtenir
des informations sur la base de données entière.
Il sert notamment à récupérer le nom des
tables contenues dans la base de données. De plus, étant
donné que de nombreuses bases de données supportent
des variantes du langage SQL, il existe de nombreuses méthodes
associées à l'objet DataBaseMetaData permettant
de connaître les méthodes SQL supportées
par la base.
Puisque l'accès
à une base de données nécessite l'utilisation
conjointe de plusieurs objets, il peut être intéressant
de créer quelques objets de plus haut niveau encapsulant
la plupart des comportements cités ci-dessus.
Ainsi la création d'un
objet DataBase pour permettre d'encapsuler l'ensemble
des objets nécessaires à la connexion à
une base de données (Connection, Statement,
DataBaseMetaData), ainsi que de (re)définir
des méthodes simples permettant de rendre plus simples
certaines opérations, comme la création de la
connexion, la récupération du nom des tables,
ainsi qu'une méthode Execute rendant l'exécution
de requête triviale.
class Database
{
Connection con;
resultSet results;
ResultSetMetaData rsmd;
DatabaseMetaData dm;
String catalog;
String types[];
//----------------------------
public Database(String Driver)
{
types = new String[1];
types[0] = "TABLES";
try {
Class.forName(driver);
}
catch(Exception e){
System.out.println("Erreur lors du chargement du driver:"+ e.getMessage());
}
}
//------------------------------------------------------
public void Open(String url,String login,String password)
{
try{
con = DriverManager.getConnection(url,login,password);
dma = con.getMetaData();
results = new resultSet(dma.getCatalogs());
String s[];
while(results.hasMoreElements()) {
s = results.NetxElement();
}
}
catch(Exception e){
System.out.println("echec d'ouverture:"+e.getMessage());
}
}
//-----------------
public void Close()
{
try{
con.close();
}
catch(Exception e){
System.out.println("echec lors de la fermeture:"+e.getMessage());
}
}
//-----------------------------
public String[] getTableNames()
{
String[] tbnames = null;
Vector tname = new Vector();
try{
results = new resultSet(dma.getTables(catalog,null,"%",types));
while (results.hasMoreElements())
tname.addElement(results.getColumnValue("TABLE_NAME"));
}
catch(Exception e){
System.out.println(e.getMessage());
}
tbnames = new String[tname.size()];
for(int i=0;i<tname.size();i++)
tbnames[i] = (String)tname.elementAt(i);
return tbnames;
}
//--------------------------------
public String[] getTableMetaData()
{
results = null;
try{
results = new resultSet(dma.getTables(catalog,null,"%",types));
}
catch(Exception e){
System.out.println(e.getMessage());
}
return results.getMetaData();
}
//------------------------------------------------
public String[] getColumnMetaData(String tablename)
{
results = null;
try{
results = new resultSet(dma.getTables(catalog,null,tablename,null));
}
catch(Exception e){
System.out.println(e.getMessage());
}
return results.getMetaData();
}
//------------------------------------------------
public String[] getColumnNames(String table)
{
String[] tbnames = null;
Vector tname = new Vector();
try{
results = new resultSet(dma.getTables(catalog,null,table,null));
while (results.hasMoreElements())
tname.addElement(results.getColumnValue("COLUMN_NAME"));
}
catch(Exception e){
System.out.println(e.getMessage());
}
tbnames = new String[tname.size()];
for(int i=0;i<tname.size();i++)
tbnames[i] = (String)tname.elementAt(i);
return tbnames;
}
//------------------------------------------------
public void getColumnValue(String table, String columnName)
{
try{
if (table.length()>0)
results = Execute("Select "+columnName+" from "+table+" order by "+columnName);
}
catch(Exception e){
System.out.println("Erreur sur la valeur de la colonne "
+columnName+e.getMessage());
}
}
//------------------------------------------------
public String getNextValue(String columnName)
{
String res = "";
try{
if (results.hasMoreElements())
res = results.getColumnvalue(columnName);
}
catch(Exception e){
System.out.println("Erreur sur la valeur suivante "+columnName+e.getMessage());
}
return res;
}
//------------------------------------------------
public resultSet Execute(String sql)
{
results = null;
try{
Statement stmt = con.createStatement();
results = new resultSet(stmt.executeQuery(sql));
}
catch(Exception e){
System.out.println(e.getMessage());
}
return results;
}
}
De la même façon,
un nouvel objet resultSet (avec un r minuscule)
peut être intéressant s'il permet de façon
transparente de retourner automatiquement les résultats
sous forme d'un tableau de valeurs, ainsi qu'en encapsulant
le nombre et le nom des colonnes contenues dans l'objet resultSet...

|