Introduction a JDBC

afternoonhandsomelySoftware and s/w Development

Nov 17, 2012 (4 years and 5 months ago)

266 views

© L. Swinnen, 2009 2
Laboratoire de Base de données
Introduction à JDBC
par Louis SWINNEN

1. Introduction
Comme nous vous demandons d’explorer JDBC personnellement, ce document explique les
notions importantes et donne des références vers la documentation. Ce document détaille
également l’installation des gestionnaires JDBC sur une machine (votre portable, votre
ordinateur personnel, …).
1.1 JDBC de quoi s’agit-il ?
JDBC (
Java DataBase Connectivity
) est un ensemble logiciel qui permet à une application
Java d’établir une connexion vers une base de données. Les différents éléments de cet
ensemble logiciel sont décrits à la figure 1.

Figure 1 :
architecture JDBC
1
[1]

Au sommet, nous trouvons l’application Java qui souhaite communiquer avec une base de
données. Cette application utilise l’API JDBC (ensemble de classes et de méthodes
spécifiques) pour dialoguer avec la base de données. Ces classes et méthodes sont
identiques à toutes les bases de données. L’API JDBC utilise un driver ou gestionnaire
JDBC pour établir la connexion avec la base de données. Ce gestionnaire est spécifique
au
SGBD auquel on souhaite se connecter. Il est, par ailleurs, fournit par le constructeur de la
base de données. Il en existe de plusieurs types et aujourd’hui ils sont très souvent eux-
mêmes écrit en Java ce qui les rend portable également



1
Figure :
Copyright 1994-2007 Sun Microsystems, Inc. All Rights Reserved
© L. Swinnen, 2009 3
1.2 Les gestionnaires JDBC
Dans le cadre de nos laboratoires, deux systèmes de gestion de base de données sont
principalement utilisés, à savoir Oracle Express Edition et SQL Server 2005. Pour ces deux
SGBD, des drivers JDBC sont disponibles.
Oracle Express Edition

:

http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html


MS SQL Server 2005

:

http://m
sdn.microsoft.com/en
-
us/data/aa937724.aspx


Tableau 1 :
gestionnaires JDBC

Le tableau 1 détaille les liens à partir desquels les différents gestionnaires JDBC peuvent être
téléchargés. Pour Oracle
, veillez bien à choisir le gestionnaire
SQLJ/JDBC 10g Release 2

(version 10.2.xx). Il faut, au moins, télécharger les composants nommés
ojdbc14.jar
et
orai18n.jar
. Pour SQL Server
, vous pouvez télécharger la version 1.2 du gestionnaire JDBC
pour SQL Server 2005.
Vous trouverez également l’ensemble des gestionnaires JDBC sur chaque machine dans le
répertoire C:\Program files\JDBC.

1.3 Installation des gestionnaires JDBC
La procédure d’installation consiste à placer les gestionnaires JDBC dans un dossier de votre
choix et ensuite de référencer ces gestionnaires. Les gestionnaires JDBC prennent la forme
de fichiers
Jar
. Un fichier
Jar
est une archive Java pouvant contenir des composants (des
classes compilées directement utilisables).
Dans votre environnement de développement (par exemple sous NetBeans, Eclipse ou
IntelliJ), il faut indiquer, dans votre projet, le lien vers le gestionnaire JDBC à utiliser.
• Par exemple dans NetBeans, il faut ajouter les fichiers JAR dans votre projet. Pour
ce faire, dans la section
Libraries
(panneau de gauche), faire un clic-droit et choisir
Add Jar/Folders
et sélectionner les fichiers JAR nécessaires à votre application.
• Sous Eclipse, il est possible lors de la création du projet Java d’indiquer des libraires
particulières. Ainsi dans la fenêtre
Java Settings
, choisir l’onglet
Libraries
et ensuite
Add External JARs
et sélectionner les fichiers JAR. Si le projet est déjà créé, il est
possible d’ajouter les libraires en faisant un clic-droit sur le nom du projet et en
choisissant Properties. Ensuite, l’option
Java Build Path
fait apparaître une série
d’onglets. Choisir dans l’onglet
Librairies
l’option
Add External JARs
et sélectionner les
fichiers JARs à intégrer.

• Sous IntelliJ, il faut ajouter également les gestionnaires JDBC. Pour ce faire, dans le
projet (panneau de gauche), faire un clic-droit sur
Libraries
et choisir
Jump To
Source
. Les paramètres du projet sont alors affichés, choisir dans la section
Librairies
,
l’icône + au sommet. Ensuite, il faut donner un nom à ces librairies, comme par
exemple JDBC Oracle et enfin, dans le panneau de droite, choisir l’option
Attach
Classes
et choisir les librairies à ajouter.

En dehors de l’environnement de développement, il faut modifier la variable d’environnement
CLASSPATH afin que celle-ci pointe vers les fichiers JAR. Ainsi, sous Windows, il faut
effectuer les étapes suivantes (en administrateur) :
• Sous Windows XP : Clic-droit sur le
poste de travail
, choisir
Propriétés
puis l’onglet
Avancé
et cliquer sur
Variables d’environnement
.

© L. Swinnen, 2009 4
Sous Windows Vista : Clic-droit sur
Ordinateur
puis choisir
Propriétés
puis, dans les
tâches qui apparaissent à gauche, choisir
Paramètres système avancés
et ensuite
cliquer sur
Variables d’environnement
.

• Dans la section
Variables système
, localiser la variable
PATH
(qui indique le chemin
de recherche par défaut) et
modifiez
celle-ci pour ajouter le chemin vers les fichiers
systèmes Java. Ainsi, si votre compilateur Java est installé dans C:\Program
Files\Java\jdk1.6.0_06, ajoutez ceci à la fin de la valeur existante : ;C:\Program
Files\Java\jdk1.6.0_06\bin
• Egalement dans la section
Variables système
, vérifiez si la variable
CLASSPATH
est
définie. Si ce n’est pas le cas, il faut créer une nouvelle variable, sinon modifier la
variable existante. Il faut ici ajouter les chemins vers les différents gestionnaires
JDBC installés sur votre machine. Si, comme à l’école, les gestionnaires sont installés
dans un répertoire JDBC dans le dossier Program Files, la variable
CLASSPATH
doit
contenir notamment le chemin suivant :
C:\Program Files\JDBC\Microsoft SQL Server 2005 JDBC Driver\sqljdbc_1.1\fra\sqljdbc.jar ;
C:\Program Files\JDBC\Oracle\ojdbc14.jar; C:\program files\JDBC\Oracle\orai18n.jar

Sous les systèmes basés Unix, il faut réaliser les mêmes modifications. Par exemple, sous
Linux, si vous avez installé le compilateur Java et les gestionnaires JDBC dans le répertoire
/opt
, vous devez adapter la variable
PATH
et
CLASSPATH
comme sous Windows.

Le plus simple est d’ajouter le fichier suivant dans le répertoire
/etc/profile.d
:
Fichier

/etc/profile.d/java.sh

#! /bin/bash
JAVA_HOME=/opt/jdk1.6.0_06
PATH=$PATH:$JAVA_HOME/bin
CLASSPATH=$CLASSPATH:/opt/JDBC/Microsoft\ SQL\ Server\ 2005\ JDBC\ Driver/
sqljdbc_1.1/fra/sqljdbc.jar:/opt/JDBC/Oracle/odjbc14.jar:/opt/JDBC/Oracle/
orai18n.jar
export JAVA_HOME PATH CLASSPATH

Il reste ensuite à rendre ce script exécutable soit en ajoutant la permission
execute
par
l’interface graphique, ou en entrant dans un terminal la commande suivante :
chmod +x /etc/profile.d/java.sh

Une fois cette modification effectuée, il suffit de redémarrer la machine et l’environnement
Java est prêt à être utilisé.
© L. Swinnen, 2009 5
2. Utilisation de JDBC
Afin d’illustrer l’utilisation de JDBC, je propose un exemple qui sera décrit et détaillé par la
suite. Nous allons donc commencer par décrire la base de données considérée et puis nous
détaillerons les éléments à mettre en place au travers d’un exemple. Enfin nous reviendrons
sur quelques concepts théoriques importants dans toutes applications JDBC.

Nos exemples se concentreront autour des deux SGBD utilisés à l’école, à savoir SQL Server
et Oracle. Si vous souhaitez établir des connexions vers d’autres bases de données, il est
important, après avoir installé le gestionnaire de base de données de bien lire la
documentation afin de déterminer comment la connexion peut être établie.

2.1 La base de données considérée
Lignecom
NCOM
NPRO
QCOM
id: NPRO
NCOM
acc
ref: NPRO
ref: NCOM
acc
Produit
NPRO
LIBELLE
PRIX
QSTOCK
id: NPRO
acc
Commande
NCOM
DATECOM
NCLI
id: NCOM
acc
ref: NCLI
acc
Client
NCLI
NOM
ADRESSE
LOCALITE
CAT
COMPTE
id: NCLI
acc


2.2 Exemple d’implémentation de connexion vers Oracle
Un élément important dans la mise en place d’une application JDBC est la chaîne de
connexion. En effet, JDBC utilise une URL pour localiser le serveur de base de données.
Cette URL est propre au SGBD et mentionne généralement :
le nom (ou l’IP) du serveur de
base de données, le nom de la base de données, le nom de l’utilisateur et le mot de passe
de connexion.

Dans le cas d’Oracle XE (
version utilisée et installée à l’école
), la chaîne de connexion est la
suivante :
jdbc:oracle:thin:username/pass@//serverName:1521/XE

La chaîne de connexion doit préciser :
• serverName : Le nom / IP du serveur Oracle sur lequel on se connecte
• databaseName : Le nom de la base de donnée doit être XE
• userName : le nom de l’utilisateur pour la connexion
• pass : le mot de passe de connexion

En plus de la chaîne de connexion, il est nécessaire de mentionner à la couche JDBC le
chemin vers le pilote utilisé. Ce chemin est propre à chaque pilote JDBC. Ainsi, pour Oracle
XE, il faut mentionner comme chemin :
oracle.jdbc.OracleDriver

Ce chemin sera présent dans l’instruction « Class.forName » utilisé lors de la connexion.

2.3 Exemple d’implémentation de connexion vers SQL Server
Un élément important dans la mise en place d’une application JDBC est la chaîne de
connexion. En effet, JDBC utilise une URL pour localiser le serveur de base de données.
Cette URL est propre au SGBD et mentionne généralement :
le nom (ou l’IP) du serveur de
base de données, le nom de la base de données, le nom de l’utilisateur et le mot de passe
de connexion.
© L. Swinnen, 2009 6

Dans le cas de SQL Serveur 2005 (
version utilisée et installée à l’école
), la chaîne de
connexion est la suivante :
jdbc:sqlserver://serverName:1433;databaseName=nomBD;user=username
;password=pass

La chaîne de connexion doit préciser :
• serverName : Le nom / IP du serveur SQL Server sur lequel on se connecte
• databaseName : le nom de la base de données à utiliser
• user : le nom de connexion
• password : le mot de passe associé pour la connexion

En plus de la chaîne de connexion, il est nécessaire de mentionner à la couche JDBC le
chemin vers le pilote utilisé. Ce chemin est propre à chaque pilote JDBC. Ainsi, pour SQL
Server 2005, il faut mentionner comme chemin :
com.microsoft.sqlserver.jdbc.SQLServerDriver

Ce chemin sera présent dans l’instruction « Class.forName » utilisé lors de la connexion.

2.4 Schéma classique de connexion
Le schéma présenté ci-dessous (voir code 1) permet la connexion au SGBD en utilisant la
couche JDBC. Il faut bien sûr indiquer les éléments propres à votre environnement comme la
chaîne de connexion ou le chemin vers le pilote.
import java.sql.*;
.
.
.
try {
Class.forName("<chemin vers pilote>") ;
String connectString = "<chaine de connexion>";
Connection conn = DriverManager.getConnection(connectString);
.
.
.
} catch(ClassNotFoundException ex) {
System.out.println("Pilote JDBC non trouve");
} catch(SQLException ex) {
ex.printStackTrace();
}
code 1 :
Schéma de connexion à la base de données

Le code 1 montre le schéma de connexion. Ainsi, l’instruction
Class.forName
permet de
charger le pilote SGBD. Si celui-ci n’est pas trouvé, une erreur de type
ClassNotFoundException
est levée.

Ensuite, un objet de type
Connection
est obtenu via la méthode statique
DriverManager.getConnection()
. Grâce à l’objet
Connection
, il sera possible d’exécuter des
requêtes SQL sur le SGBD.
Deux types de requêtes sont possibles : des requêtes retournant des résultats
(principalement des requêtes de sélection) et des requêtes ne retournant aucun résultat
(principalement des requêtes d’ajout, de mise à jour ou de suppression).

© L. Swinnen, 2009 7
2.5 Requête de sélection
Dans ce paragraphe, nous allons aborder les requêtes retournant des résultats. Le schéma
d’exécution est présenté ci-dessous (voir code 2).
import java.sql.*;
.
.
.
try {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT NCLI, NOM, CAT, COMPTE
FROM CLIENT");

while(rs.next()) {
String ncli = rs.getString("NCLI");
String nom = rs.getString("NOM");
String cat = rs.getString("CAT");
float compte= rs.getFloat("COMPTE");
.
.
.
}
rs.close();
stmt.close();
} catch(SQLException ex) {
ex.printStackTrace();
}
code 2 :
exemple de sélection

Dans le code 2, nous avons un exemple de sélection dans la table client. Ainsi, nous
effectuons la requête
select ncli, nom, cat, compte from client
. Cette requête risque de
retourner plusieurs lignes. Afin de gérer ces lignes séparément, l’objet
ResultSet
est utilisé. Il
s’agit d’un objet particulier permettant de traiter le résultat de la requête.

Grâce à
rs.next()
, il est possible de parcourir les résultats. Cet appel retourne
true
s’il y a
encore un résultat à traiter ou
false
sinon
.
Afin d’avoir accès aux éléments sélectionnés par
la requête exécutée, nous avons, dans l’objet
ResultSet
, bons nombres de méthodes. Ainsi,
comme montré dans l’exemple ci-dessus, la méthode
getString
qui prend en paramètre le
nom de la colonne voulue permet d’obtenir, sous la forme d’une chaîne de caractère, la
valeur de cette colonne. A coté de la méthode
getString,
il existe bien d’autres méthodes
permettant d’obtenir une valeur de colonne sous divers format. Ainsi,
getFloat
permet de
récupérer la valeur d’une colonne sous la forme d’un réel. Il faut bien sûr respecter le
schéma de la base de données afin de pouvoir réaliser les conversions.

En cas d’erreur lors de l’exécution de la requête, une exception de type
SQLException
est
levée. Il est primordial de capturer et traiter cette exception. Une fois l’exécution terminée, il
convient de terminer par l’appel aux méthodes close().

Comme déjà dit, nous éviterons toujours
des requêtes de type
select * from
puisque ce
type de requête empêche l’évolution du schéma de la base de données. De plus, il serait
probablement bon d’accéder aux informations contenues dans la base de données depuis
des vues. Ainsi, le schéma de la base de données peut évoluer indépendamment de
l’application.
© L. Swinnen, 2009 8
2.6 Requête de mise à jour
Dans ce paragraphe, nous allons aborder les requêtes ne retournant aucun résultat, il s’agit
principalement des requêtes de mise à jour (INSERT, UPDATE, DELETE). Le schéma
d’exécution est présenté ci-dessous (voir code 3).
import java.sql.*;
.
.
.
try {
Statement stmt = conn.createStatement();
stmt.executeUpdate("UPDATE Produit SET QSTOCK = 3 WHERE
NPRO = ABC123");
.
.
.
stmt.close();
} catch(SQLException ex) {
ex.printStackTrace();
}
code 3 :
exemple de mise à jour

Dans le code 3, nous avons un exemple de mise à jour au moyen d’une requête UPDATE. La
méthode
executeUpdate
retourne un entier représentant le nombre de lignes altérées
par la
mise à jour. Grâce à cette information, il est possible de vérifier si la mise à jour a bien eu
lieue.
Comme précédemment, en cas d’erreur, une exception
SQLException
est levée et il convient
de la capturer et la gérer. Une fois la fin du traitement atteint, il convient de terminer
l’exécution grâce à la méthode close().

2.7 L’utilisation des requêtes préparées
Il est très commode, lors de la rédaction du code, d’utiliser des requêtes préparées et de
garnir les différents éléments en fonction des besoins. Cette façon de procéder simplifie
grandement l’écriture du code et assure une certaine portabilité.

En effet, lors de la migration vers un autre SGBD, il est nécessaire de vérifier chaque requête
afin de s’assurer que la syntaxe demeure correcte pour le nouveau SGBD. Or, le format des
dates par exemple, peut amener des problèmes : en effet, ce dernier peut varier d’un SGBD
à l’autre. Dès lors, l’utilisation de requêtes préparées évite ces problèmes et rend la
migration vers un autre SGBD plus aisé.
Le code 4 montre un exemple de mise à jour au moyen de requêtes préparées.
© L. Swinnen, 2009 9
import java.sql.*;
.
.
.
try {
PreparedStatement pst = conn.prepareStatement(
"INSERT INTO COMMANDE (NCOM, NCLI, DATECOM) VALUES(?,?,?)");

pst.setString(1, "C010"); // set NCOM
pst.setString(2, "CL01"); // set NCLI
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yy");
pst.setDate(3, sdf.parse("20/02/08", new ParsePosition(0));

pst.executeUpdate();
pst.close();
} catch(SQLException ex) {
ex.printStackTrace();
}
code 4 :
exemple de requête préparée

Une requête préparée prend la forme d’un objet
PreparedStatement
. On remarque que la
requête contient plusieurs caractères « ? » représentant
des paramètres
. Grâce aux
méthodes
set
utilisées sur l’objet
PreparedStatement
, ces caractères seront remplacés par
des valeurs. Ainsi, la ligne mentionnant
pst.setString(1, "C010")
remplace le 1
ier
caractère
« ? » par la valeur
C010
. Ensuite, le second paramètre est remplacé par la valeur
CL01
et le
dernier par la date du 20 février 2008.
Une fois tous les paramètres garnis, il reste à exécuter la requête. Il suffit dès lors d’appeler
la méthode
executeUpdate
sur cet objet pour lancer cette exécution.

Nous remarquons immédiatement que même en cas de changement de SGBD, le code ci-
dessus reste valide. Cela n’aurait pas été nécessairement le cas si nous avions construit à la
main la chaîne à exécuter.
Bien sûr, les requêtes préparées peuvent également être utilisées dans des requêtes de
sélection. Ainsi, au lieu d’exécuter la méthode
executeUpdate
, il convient d’appeler la
méthode
executeQuery
qui retourne un
ResultSet
qu’il est ensuite possible de parcourir,
comme vu précédemment.
2.8 Les transactions
Lorsqu’il est nécessaire de regrouper plusieurs requêtes SQL au sein d’une transaction (afin
de profiter des propriétés ACID –
A
tomicité,
C
ohérence,
I
solation,
D
urabilité), il faut
empêcher le SGBD de traiter chaque requête indépendamment. Nous allons examiner le
code 5 qui illustre le concept de transaction en Java.

© L. Swinnen, 2009 10
import java.sql.*;
.
.
.
try {
conn.setAutoCommit(false);
// requete 1
Statement stmt1 = conn.createStatement();
stmt1.executeUpdate(...);
.
.
.
// requete 2
Statement stmt2 = conn.createStatement();
stmt2.executeUpdate(...);
stmt1.close();
stmt2.close();
conn.commit();
conn.setAutoCommit(true);
} catch(SQLException ex1) {
try { conn.rollback(); conn.setAutoCommit(true);
} catch(SQLException ex2) { /* nothing to do */ }
ex1.printStackTrace();
}
code 5 :
exemple de transaction

Comme le montre le code 5, la création de la transaction commence par empêcher les
commandes
commit
implicites réalisées automatiquement par le SGBD grâce à l’appel de la
méthode
setAutoCommit(false)
. Désormais, les instructions
commit
devront être réalisées
manuellement.
Ensuite, les différentes requêtes appartenant à la transaction sont exécutées.

Enfin, au terme de la transaction, il faut accepter les modifications au moyen de l’appel à la
méthode
commit()
. Enfin, il convient de revenir à l’état initial en demandant au SGBD de
réaliser automatiquement les
commit
après chaque requête via l’exécution de
setAutoCommit(true)
.

Attention ! Comme le montre le code 5, il convient de compléter la gestion des erreurs
pour appeler la méthode
rollback()
(annulation de toutes les modifications consécutives à la
transaction) et retourner à l’état initial au moyen de
setAutoCommit(true)
. Comme
l’exécution de ces méthodes peut entraîner une exception de type
SQLException
, nous
voyons qu’un nouveau bloc
try … catch
est nécessaire dans le bloc
catch
précédent.


© L. Swinnen, 2009 11
3. Quelques erreurs courantes

3.1 Séparation des couches
Il est important de bien séparer les couches logiques de votre programme. Ainsi, la couche
accès BD
est la seule à utiliser la couche JDBC et accéder à la base de données. Il faut donc
prendre l’habitude de :
• masquer et traiter les erreurs de type
SQLException
car celles-ci ne devraient jamais
être transmises à une autre couche
o vous pouvez choisir de renvoyer un état ou une exception que vous avez
créée vous même.
o Evitez les blocs
try … catch
dont l’exception capturée est
Exception
et dont le
contenu est vide, cela montre clairement que votre programmation n’est pas
achevée
• retourner les informations sous la forme de données de l’application. Ne retournez
donc pas de
ResultSet
ou autre chose provenant de JDBC. Vous pouvez :
o Utilisez un conteneur pour assembler les données (liste, map, set)
o Modéliser directement votre information. Par exemple, retourner un client ou
un produit. Il faut pour cela définir l’objet
Modèle
et garnir les données en
fonction de ce qui est observé dans la base de données. C’est probablement
la manière la plus propre. Ainsi, vous concentrez les informations et requêtes
SQL au niveau de la couche
accès BD
uniquement.

3.2 Indépendance par rapport au modèle
Prenez également l’habitude d’être indépendant du SGBD. Pour ce faire, il convient de :
• Eviter à tout prix les requêtes de type select * from, car en cas de modification
du schéma, votre couche
accès BD
doit être adaptée.
• Accéder à la base de données au travers de vues. C’est une autre façon de
« prendre de la distance » par rapport au schéma de la BD (pour autant que votre
vue n’utilise pas un
select * from
).

3.3 Découpe selon les fonctionnalités
Il arrive également très souvent que les étudiants construisent les méthodes de la couche
accès BD
en fonction des requêtes et non des fonctionnalités. Ainsi,
• Il est plus judicieux de prévoir des fonctionnalités complètes :
ajout d’une
commande
par exemple qui peut comporter plusieurs requêtes :
vérifier l’état du
stock, enregistrer les lignes de commande et enregistrer la commande
.
• Lorsqu’on découpe en fonctionnalité, il est également nécessaire de prévoir des
transactions pour s’assurer que l’opération est effectuée complètement, ou pas du
tout. Pour rappel, une transaction en JDBC débute avec l’instruction
setAutoCommit(false)
et se termine par un
commit
ou un
rollback
suivi de
l’instruction
setAutoCommit(true)
.
o En conséquence, il ne faut pas utiliser
BEGIN TRANSACTION et END
TRANSACTION !

© L. Swinnen, 2009 12
4. Exemple d’une couche d’accès BD
Supposons que nous devions construire une application JAVA capable de réaliser
l’enregistrement d’une nouvelle commande
,
supprimer une commande existante
,
consulter
les commandes d’un client
et
permettre l’approvisionnement du stock
. Ces différentes
fonctionnalités doivent être programmées au niveau de la couche
accès BD
.

Ainsi, l’
enregistrement d’une nouvelle commande
doit :
1. Créer un nouvel enregistrement dans la table
commande
pour le client concerné
2. Créer les lignes de commande en fonction des demandes du client
3. Vérifier que le stock actuel peut satisfaire la commande, sinon la rejeter.

La
suppression de la commande existante
doit :
1. Supprimer toutes les lignes de commandes liées à cette commande
2. Supprimer la commande concernée

La
consultation des commandes d’un client
doit permettre d’obtenir toutes les commandes
de ce client. Il faut, en outre, obtenir les détails (i.e. les lignes de commande) en relation
avec ces commandes.
L’
approvisionnement du stock
permet de mettre à jour le stock d’un produit donné.

Comme nous le voyons, toutes ces fonctionnalités entraînent la présence de transaction afin
d’assurer l’
atomicité
et la
cohérence
de la base de données.

L’exemple ci-dessous comprend à la fois la
couche accès BD
qui contient les requêtes SQL et
la programmation JDBC mais également les objets
modèles
comme Commande, Produit,
Client, LigneCommande. Enfin, un programme de test est également donné en guise
d’exemple.
Il s’agit bien sûr d’un exemple d’implantation et non d’une quelconque démarche à
suivre. Ainsi, cet exemple pourrait être amélioré par l’utilisation d’exception particulière afin
de signaler le type d’erreur rencontré, réaliser davantage de vérification des données reçues
et obtenue de la base de données, exploiter des vues plutôt que les tables directement.

Je laisse le soin au lecteur de corriger et améliorer ce code.

© L. Swinnen, 2009 13
fichier BDAccess.java (couche accès BD)

package BD;

import BO.Commande;
import BO.LigneCommande;
import BO.Produit;

import java.sql.*;
import java.util.List;
import java.util.ArrayList;

/**
* Created by IntelliJ IDEA.
* User: Louis SWINNEN
* Date: 08-avr.-2009
* Time: 21:27:37
*/
public class
BDAccess
{
public static final String[] CONN_STR = {
"
jdbc:sqlserver://<serverName>:1433;databaseName=<dbName>;user=<userName>;password=<passwd>
",
"
jdbc:oracle:thin:<userName>/<passwd>@//<serverName>:1521/<dbName>
"};
public static final String[] CLASS_STR = { "
com.microsoft.sqlserver.jdbc.SQLServerDriver
",
"
oracle.jdbc.OracleDriver
"};
public static final int MSSQL = 0;
public static final int ORACLE = 1;
private String serverName;
private String dbName;
private String userName;
private String password;
private boolean isConnected = false;
private Connection dbConnection = null;

public
BDAccess
() { } /*
default contructor */

public
BDAccess
(String _serverName, String _dbName, String _userName, String _pass) {
serverName = _serverName;
dbName = _dbName;
userName = _userName;
password = _pass;
}
public boolean
connect
(int _dbType) {
try {
Class.forName(CLASS_STR[_dbType]);
String connStr = CONN_STR[_dbType].replaceAll("
<serverName>
",
serverName).replaceAll("
<dbName>
", dbName).replaceAll("
<userName>
",
userName).replaceAll("
<passwd>
", password);
dbConnection = DriverManager.getConnection(connStr);
isConnected = true;
return true;
} catch(ClassNotFoundException ex) {
ex.printStackTrace();
return false;
} catch(SQLException ex) {
ex.printStackTrace();
return false;
} catch(ArrayIndexOutOfBoundsException ex) {
System.out.println("
[BDAccess][connect] dbType not supported
");
return false;
}
}
public boolean
disconnect
() {
if(isConnected) {
try {
dbConnection.close();
return true;
} catch(SQLException ex) {
ex.printStackTrace();
return false;
}
} else
return true;
}
public boolean
ajouteCommande
(Commande _cmd) {
Statement stmt;
PreparedStatement pst;
© L. Swinnen, 2009 14
ResultSet rs;
if(! isConnected)
return false;

try {


/* *** 1. Start transaction *** */
dbConnection.setAutoCommit(false);


/* *** 2. CREATE COMMANDE *** */
pst = dbConnection.prepareStatement("
INSERT INTO commande(NCOM, NCLI, DATECOM)
VALUES (?,?,?)
");
pst.setString(1, _cmd.getNumeroCommande());
pst.setString(2, _cmd.getNumeroClient());
pst.setDate(3, new java.sql.Date(_cmd.getDateCommande().getTime()));
pst.executeUpdate();
pst.close();

/* *** 3. CREATE LIGNECOM AND DECREASE STOCK *** */
for(int i=0; i<_cmd.getNombreLigne(); ++i) {
LigneCommande lc = _cmd.getLigneCommande(i);


/* *** 3a. DECREASE STOCK *** */
stmt = dbConnection.createStatement();
rs = stmt.executeQuery("
SELECT QSTOCK FROM Produit WHERE NPRO = '
" +
lc.getNumeroProduit()+"
'
");

if(rs.next()) {

/* only 1 row because select query on primary key */

int qte = rs.getInt("
QSTOCK
");
if(qte - lc.getQteCommandee() < 0) {

/* stock is too low -> aborting */
dbConnection.rollback();
dbConnection.setAutoCommit(true);
return false;
}
rs.close();
stmt.close();
stmt = dbConnection.createStatement();
stmt.executeUpdate("
UPDATE Produit SET QSTOCK=
" + (qte –
lc.getQteCommandee()) + "
WHERE NPRO='
" + lc.getNumeroProduit()+"
'
");
} else {

/* no row ? aborting ! */
dbConnection.rollback();
dbConnection.setAutoCommit(true);
return false;
}

/* *** 3b. CREATE LIGNECOMMANDE *** */
stmt = dbConnection.createStatement();
stmt.executeUpdate("
INSERT INTO LIGNECOM(NCOM, NPRO, QCOM) VALUES ('
" +
lc.getNumeroCommande() + "
','
"+ lc.getNumeroProduit() +
"
',
"+lc.getQteCommandee()+ "
)
");
stmt.close();
}

/* *** 4. End of Transaction *** */
dbConnection.commit();
dbConnection.setAutoCommit(true);
return true;

} catch(SQLException ex) {
ex.printStackTrace();
try { dbConnection.rollback(); dbConnection.setAutoCommit(true);
} catch (SQLException e) { }
return false;
}
}
public boolean
supprimeCommande
(Commande _cmd) {
PreparedStatement pst;
Statement stmt;
if(!isConnected)
return false;

try {
© L. Swinnen, 2009 15

/* *** 1. Start transaction *** */
dbConnection.setAutoCommit(false);


/* *** 2. Delete ALL lignecom from this commande *** */
pst = dbConnection.prepareStatement("DELETE
FROM LIGNECOM WHERE NCOM=?
");
pst.setString(1, _cmd.getNumeroCommande());
pst.executeUpdate();
pst.close();

/* *** 3. Delete requested commande *** */
pst = dbConnection.prepareStatement("
DELETE FROM COMMANDE WHERE NCOM=?
");
pst.setString(1, _cmd.getNumeroCommande());
pst.executeUpdate();
pst.close();

/* *** 4. End of Transaction *** */
dbConnection.commit();
dbConnection.setAutoCommit(true);
return true;
} catch(SQLException ex) {
ex.printStackTrace();
try { dbConnection.rollback(); dbConnection.setAutoCommit(true);
} catch (SQLException e) { }
return false;
}
}
public Produit
obtenirProduit
(String _npro) {
Produit p = new Produit();
PreparedStatement pst;
ResultSet rs;
try {
pst = dbConnection.prepareStatement("
SELECT NPRO, LIBELLE, PRIX, QSTOCK FROM
Produit WHERE NPRO=?
");
pst.setString(1, _npro);
rs = pst.executeQuery();

if(rs.next()) {
p.setNumero(_npro);
p.setLibelle(rs.getString("
LIBELLE
"));
p.setPrix(rs.getInt("
PRIX
"));
p.setStock(rs.getInt("
QSTOCK
"));
return p;
} else
return null;
/* aucun produit trouvé */


} catch(SQLException ex) {
ex.printStackTrace();
return null;
}
}
public boolean
modifieProduit
(Produit _prod) {
PreparedStatement pst;
if(_prod.getNumero() == null)
return false;
try {
pst = dbConnection.prepareStatement("
UPDATE PRODUIT SET LIBELLE=?, PRIX=?,
QSTOCK=? WHERE NPRO=?
");
pst.setString(1, _prod.getLibelle());
pst.setInt(2, _prod.getPrix());
pst.setInt(3, _prod.getStock());
pst.setString(4, _prod.getNumero());
pst.executeUpdate();
pst.close();
return true;
} catch(SQLException ex) {
ex.printStackTrace();
return false;
}
}
public List<Commande>
obtenirCommandeClient
(String _ncli) {
List<Commande> liste = new ArrayList<Commande>();

© L. Swinnen, 2009 16
if(_ncli == null) return null;
try {


/* *** TASK 1: Find all 'Commandes' *** */
ResultSet listeCommande;
PreparedStatement pstCommande = dbConnection.prepareStatement("
SELECT NCOM, NCLI,
DATECOM FROM COMMANDE WHERE NCLI=?
");
pstCommande.setString(1, _ncli);
listeCommande = pstCommande.executeQuery();
while(listeCommande.next()) {
Commande cmd = new Commande(listeCommande.getString("
NCOM
"),
listeCommande.getString("
NCLI
"), listeCommande.getDate("
DATECOM
"));


/* *** TASK 2: Find all 'LigneCom' for the 'commande' *** */
ResultSet listeLigne;
PreparedStatement pstLignecom = dbConnection.prepareStatement("
SELECT NCOM,
NPRO, QCOM FROM LIGNECOM WHERE NCOM=?
");
pstLignecom.setString(1, cmd.getNumeroCommande());
listeLigne = pstLignecom.executeQuery();
while(listeLigne.next()) {
cmd.ajouteLigneCommande(listeLigne.getString("
NPRO
"),
listeLigne.getInt("
QCOM
"));
}
listeLigne.close();
liste.add(cmd);
}
listeCommande.close();
return liste;

} catch(SQLException ex) {
ex.printStackTrace();
return null;
}
}
public String
getServerName
() { return serverName; }
public String
getDBName
() { return dbName; }
public String
getUserName
() { return userName; }
public String
getPassword
() { return password; }
public boolean
isConnected
() { return isConnected; }
public void
setServerName
(String _serverName) { serverName = _serverName; }
public void
setDBName
(String _dbName) { dbName = _dbName; }
public void
setUserName
(String _userName) { userName = _userName; }
public void
setPassword
(String _pass) { password = _pass; }

}
© L. Swinnen, 2009 17
fichier Client.java (couche traitement Business Object)

package BO;

/**
* Created by IntelliJ IDEA.
* User: Louis SWINNEN
* Date: 08-avr.-2009
* Time: 23:10:32
*/
public class
Client
{
private String numClient;
private String nom;
private String adresse;
private String localite;
private String categorie;
private float compte;

public
Client
() { }
public
Client
(String _ncli, String _nom, String _adr, String _loc, String _cat,
float _cpt ) {
numClient = _ncli;
nom = _nom;
adresse = _adr;
localite = _loc;
categorie = _cat;
compte = _cpt;
}
public String
getNumero
() { return numClient; }
public String
getNom
() { return nom; }
public String
getAdresse
() { return adresse; }
public String
getLocalite
() { return localite; }
public String
getCategorie
() { return categorie; }
public float
getCompte
() { return compte; }
public void
setNumero
(String _num) { numClient = _num; }
public void
setNom
(String _nom) { nom = _nom; }
public void
setAdresse
(String _adr) { adresse = _adr; }
public void
setLocalite
(String _loc) { localite = _loc; }
public void
setCategorie
(String _cat) { categorie = _cat; }
public void
setCompte
(float _cpt) { compte = _cpt; }
}
fichier Produit.java (couche traitement Business Object)

package BO;

/**
* Created by IntelliJ IDEA.
* User: Louis SWINNEN
* Date: 08-avr.-2009
* Time: 23:17:44
*/
public class
Produit
{
private String numProduit;
private String libelle;
private int prix;
private int qteStock;

public
Produit
() { }
public
Produit
(String _num, String _lib, int _prix, int _qteStock) {
numProduit = _num;
libelle = _lib;
prix = _prix;
qteStock = _qteStock;
}
public String
getNumero
() { return numProduit; }
public String
getLibelle
() { return libelle; }
public int
getPrix
() { return prix; }
public int
getStock
() { return qteStock; }
public void
setNumero
(String _num) { numProduit = _num; }
public void
setLibelle
(String _lib) { libelle = _lib; }
public void
setPrix
(int _prix) { prix = _prix; }
public void
setStock
(int _stock) { qteStock = _stock; }
public String
toString
() { return numProduit + ":" + libelle + ":" + prix + ":" +
qteStock; }
}
© L. Swinnen, 2009 18
fichier Commande.java (couche traitement Business Object)

package BO;

import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;

/**
* Created by IntelliJ IDEA.
* User: Louis SWINNEN
* Date: 08-avr.-2009
* Time: 23:25:01
*/
public class
Commande
{
private String numCommande;
private String numClient;
private Date dateCommande;
private List<LigneCommande> liste = new ArrayList<LigneCommande>();

public
Commande
() { }

public
Commande
(String _numCommande, String _numClient, Date _dateCommande) {
numCommande = _numCommande;
numClient = _numClient;
dateCommande = _dateCommande;
}
public void
ajouteLigneCommande
(LigneCommande _ligneCom) {
liste.add(_ligneCom);
}
public void
ajouteLigneCommande
(String _numProduit, int _qteCommandee) {
liste.add(new LigneCommande(numCommande, _numProduit, _qteCommandee));
}
public LigneCommande
supprimeLigneCommande
(int _index) {
if(_index < liste.size()) {
return liste.remove(_index);
} else
return null;
}
public LigneCommande
getLigneCommande
(int _index) {
if(_index < liste.size()) {
return liste.get(_index);
} else
return null;
}
public String
getNumeroCommande
() { return numCommande; }
public String
getNumeroClient
() { return numClient; }
public Date
getDateCommande
() { return dateCommande; }
public int
getNombreLigne
() { return liste.size(); }
public void
setNumeroCommande
(String _num) { numCommande = _num; }
public void
setNumeroClient
(String _num) { numClient = _num; }
public void
setDateCommande
(Date _date) { dateCommande = _date; }
public String
toString
() {
String result = numCommande + "
:
" + numClient + "
:
"+dateCommande;
Iterator<LigneCommande> it = liste.iterator();
while(it.hasNext()) {
LigneCommande lc = it.next();
result = result + lc;
}
return result;
}
}
© L. Swinnen, 2009 19
fichier LigneCommande.java (couche traitement Business Object)

package BO;

/**
* Created by IntelliJ IDEA.
* User: Louis SWINNEN
* Date: 08-avr.-2009
* Time: 23:26:36
*/
public class
LigneCommande
{
private String numCommande;
private String numProduit;
private int qteCommandee;

public
LigneCommande
() { }
public
LigneCommande
(String _numCommande, String _numProduit, int _qte) {
numCommande = _numCommande;
numProduit = _numProduit;
qteCommandee = _qte;
}
public String
getNumeroCommande
() { return numCommande; }
public String
getNumeroProduit
() { return numProduit; }
public int
getQteCommandee
() { return qteCommandee; }
public void
setNumeroCommande
(String _num) { numCommande = _num; }
public void
setNumeroProduit
(String _num) { numProduit = _num; }
public void
setQteCommandee
(int _qte) { qteCommandee = _qte; }
public String
toString
() { return "
[
" + numCommande + "
:
" + numProduit + "
:
" +
qteCommandee + "
]
"; }
}
fichier Programme.java (Programme de test AccesBD)

import BD.BDAccess;
import BO.Commande;
import BO.Produit;

import java.text.SimpleDateFormat;
import java.text.ParsePosition;

/**
* Created by IntelliJ IDEA.
* User: Louis SWINNEN
* Date: 08-avr.-2009
* Time: 22:40:39
*/
public class
Programme
{
BDAccess bda;
public
Programme
() {
bda = new BDAccess("
172.16.130.122
", "
XE
", "
clicmd
", "
clicmd
");
// bda = new BDAccess("172.16.130.124", "clicmd", "info", "info"); SQL SERVER
// if(bda.connect(BDAccess.MSSQL)) {
if(bda.connect(BDAccess.ORACLE)) {
System.out.println("
Connexion

établie
");
SimpleDateFormat sdf = new SimpleDateFormat("
dd/MM/yy
");
Commande cmd = new Commande("
C190
", "
B112
",sdf.parse("
09/04/09
", new
ParsePosition(0)));
cmd.ajouteLigneCommande("
PA60
", 10);
cmd.ajouteLigneCommande("
PA45
", 15);
cmd.ajouteLigneCommande("
CS264
", 100);
cmd.ajouteLigneCommande("
PS222
", 200);
Commande cmd2 = new Commande("
C190
", null, null);

if(bda.ajouteCommande(cmd) )
System.out.println("
Ajout OK
");
else
System.out.println("
Ajout KO !!
");

if(bda.supprimeCommande(cmd2))
System.out.println("
Suppression OK
");
else
System.out.println("
Suppression KO !!
");
System.out.println(bda.obtenirProduit("
CS264
"));
System.out.println(bda.obtenirCommandeClient("
C400
"));
© L. Swinnen, 2009 20
Produit p = bda.obtenirProduit("
PA60
");
p.setStock(p.getStock() + 20);
if(bda.modifieProduit(p))
System.out.println("
[produit] OK
");
else
System.out.println("
[produit] KO
");

bda.disconnect();
} else {
System.out.println("
Connexion perdue !
");
}
}
public static void
main
(String[] args) {
new Programme();
}
}

© L. Swinnen, 2009 21
Bibliographie et références
[1]

JDBC Overview, Sun Microsystem,
http://java.sun.com/products/jdbc/overview.html
,
consulté en juin 2008
[2]

Getting Started with JDBC API, Sun Microsystem,
http://java.sun.com/j2se/1.5.0/docs/guide/jdbc/getstart/GettingStartedTOC.fm.html
,
consulté en juin 2008
[3]

C. S. Hortsmann et G. Cornell
,
Au cœur de Java 2

: Notions fondamentales (Vol. 1)
,
CampusPress, Paris, 2004
[4]

C. S. Hortsmann et G. Cornell
,
Au cœur de Java 2

: Fonctions avan
cées (Vol. 2)
,
CampusPress, Paris, 2004


Information de Copyright

Oracle
TM
, SQLJ
TM
, Oracle Express Edition
TM
are registered trademarks of Oracle Corporation
and/or its affiliates.
Java
TM
, JDBC
TM
, Netbeans
TM
are trademarks or registered trademarks of Sun Microsystems,
Inc. or its subsidiaries in the United States and other countries.

Eclipse © Copyright Eclipse contributors and others 2000, 2009. All rights reserved. Eclipse
is a trademark of Eclipse Foundation Inc.
IntelliJ IDEA © 2000-2009 JetBrains s.r.o. All rights reserved.

Microsoft
®
, Microsoft SQL Server
®
, MS SQL Server
®
, SQL Server
®
2005 are trademarks of
Microsoft Corporation and/or its affiliates.
Tous les noms et marques des produits cités sont des marques déposées par leurs
producteurs respectifs.