Introduction à JDBC Bases de données avancées JDBC Java DataBase Connectivity API java adaptée à la connexion aux bases de données relationnelles (SGBDR) Ensemble de classes et d'interfaces permettant l'utilisation sur le réseau d'un ou plusieurs SGBDR à partir d'un programme java Objectifs Permettre aux développeurs d'écrire un code indépendant de la base de données et du moyen de connexion utilisé Simple à mettre en oeuvre Indépendant de la SGBD cible Supportant les fonctionnalités du langage SQL Avantages de JDBC Portabilité sur de nombreux OS Portabilité sur de nombreuses SGBDR (Oracle, MySQL, Access, SyBase) Robuste et sécurisé Uniformité du langage de description des applications et des accès aux bases de données Liberté totale vis-à-vis des constructeurs L’API JDBC Fourni par le package java.sql Supporte le standard SQL2 Entry Level 8 interfaces définissant les objets nécessaires à : ✓ La connexion à une base éloignée ✓ La création et l'exécution de requêtes SQL Principes de fonctionnement Chaque base de données utilise un pilote (driver) qui lui est propre et qui permet de convertir les requêtes JDBC dans le langage natif du SGBDR Ces drivers existent pour tous les principaux constructeurs de SGBDR Oracle Application JDBC MySql Access Modèle à deux couches Couche externe ✓ Couche utile et visible pour développer des applications java utilisant l'API JDBC ✓ Représentée par le package java.sql Couche interne ✓ Destinée à faciliter l'implémentation de drivers pour des bases de données ✓ Représentant une interface entre les accès de bas niveau et la partie applicative Modèles de connexion Modèle 2-tiers : 2 entités interviennent ✓ l'application ✓ le SGBDR Modèle 3-tiers : 3 entités interviennent ✓ l'application ✓ un serveur (MiddleWare) installé sur le réseau ✓ le SGBDR Modèle 2-tiers Principe ✓l'application courante utilise JDBC pour parler directement avec le SGBDR qui gère la base de données Avantages ✓Simple à mettre en oeuvre ✓bon choix pour les applications clientes peu évoluées, a livrer rapidement Inconvénients ✓Dépendance forte entre le client et la structure du SGBDR ✓Tout le traitement est du coté client. Architecture 2-tiers Serveur Client SGBD Application JDBC BD Modèle 3-tiers Principes ✓ Le serveur MiddleWare est l'interlocuteur direct du code Java client ✓ C'est lui qui échange des données avec le SGBDR ✓ Pas nécessairement écrit en java Avantages ✓ Ajoute un niveau de sécurité ✓ Échanges avec le client grâce à Corba, RMI Inconvénients ✓ Plus lourd à gérer Architecture 3-tiers Middleware Client Serveur SGBD Application JDBC BD Mettre en oeuvre JDBC Principes 1. Importer le package java.sql 2. Enregistrer le driver JDBC 3. Établir la connexion à la base de données 4. Créer une zone de description de requêtes 5. Exécuter la requête 6. Traiter les données retournées 7. Fermer les différents espaces Enregistrer le pilote JDBC Dépend du type de SGBDR sur lequel on veut se connecter Charger la classe Class.forName("com.mysql.jdbc.Driver").newInstance(); Il existe des exceptions relatives à cette instruction, on les lèvera try { Class.forName("com.mysql.jdbc.Driver").newInstance(); catch(Exception e) { System.out.println("Erreur : " + e.getMessage()); } } Se connecter à la BD Accès à la base via une URL de la forme jdbc:<sous-protocole>:<nom-BD>;param=valeur,... ✓ Spécifie l'utilisation de JDBC ✓ Spécifie le driver ou le type de SGBDR ✓ Spécifie l'identification de la base locale ou distante Exemples String url = "jdbc:odbc:maBase"; String url = "jdbc:msql://leo.inria.fr:1114:maBase"; String url = "jdbc:mysql://localhost:3306/test"; Se connecter... suite Méthode getConnection() de DriverManager 3 arguments de type String ✓ l'URL de la base de données ✓ le nom de l'utilisateur (optionnel) ✓ son mot de passe (optionnel) Retourne : une instance de la classe Connection Connexion c = DriverManager.getConnection(url,user,passwd); Exceptions à lever de la classe SQLException Créer une zone de description Avant de pouvoir exécuter des requêtes SQL, il faut créer une zone de description de requêtes (classe Statement) L'objet Statement possède les méthodes nécessaires pour réaliser des requêtes Il existe 3 types de Statement ✓ Statement : Requêtes simples ✓ PreparedStatement : requêtes dynamiques précompilées ✓ CallableStatement : procédures stockées Créer une zone de description À partir de l'instance de l'objet Connection précédemment créé, on récupère le Statement associé Statement st = c.createStatement(); PreparedStatement pst = c.createPreparedStatement(); CallableStatement cst = c.prepareCall(); Éxecution d’une requête 3 types de requêtes ✓executeQuery() pour les requêtes SELECT qui retourne une instance de l'objet ResultSet contenant le résultat de la requête ✓executeUpdate() pour les requêtes de modification de base (INSERT, UPDATE, DELETE, CREATE TABLE, DROP TABLE) qui retourne un entier correspondant aux nombres de tuples traités ✓execute() procédures stockées (cas rare) Les deux premières méthodes prennent en argument une chaîne (String) indiquant la requête à effectuer Exemples st.executeQuery("SELECT * FROM matiere WHERE abbreviation='BD';"); int nb = st.executeUpdate("DROP TABLE MYTABLE"); Remarque: si une requête ne peut s'exécuter (erreur de syntaxe et autres), une exception SQLException est levée Traiter les données renvoyées L'objet ResultSet retourné lors d'une requête executeQuery() permet d'accéder aux champs de tuples sélectionnés Seules les données demandées sont transférées en mémoire dans le driver JDBC. Il faut donc les lire et les stocker en mémoire pour un usage ultérieur ResultSet rs = st.executeQuery("SELECT * FROM matiere WHERE abreviation='BD';"); L’objet resultSet L'objet ResultSet pointe sur enregistrements associés à la requête Au départ l'objet ResultSet pointe avant le premier résultat de la requête On peut parcourir les tuples résultants de différentes manières: méthodes next(), previous(), first(), last() ... Tant qu'il existe un enregistrement à lire les méthodes next() et false() renvoient true L’objet resultSet Une fois que l'on est sur l'enregistrement voulu, on peut se servir de cet objet ResultSet pour récupérer le contenu d'un enregistrement Les colonnes sont référencées par ✓ un numéro commençant à 1, ou ✓ leur nom Types de données Afin d'éviter un problème de conversion de données il existe des méthodes permettant de récupérer le contenu d'une colonne Méthode de la forme getXXX() correspondant aux types de données de la colonne en question Le XXX de getXXX() est le nom du type java correspondant Chaque driver a des correspondances entre les types SQL du SGBDR et les types JDBC Le programmeur est responsable du choix des méthodes : une exception SQLException est générée en cas d'erreur Exemples rs est de type ResultSet int val = rs.getInt(3); String p = rs.getString(“Produit"); double d = rs.getDouble(“Prix"); Correspondance des types Type JDBC Type JAVA CHAR,VARCHAR String java.math.BigDecimal NUMERIC, DECIMAL java.math.BigDecimal BINARY,VARBINARY byte[] BIT boolean INTEGER int BIGINT long REAL float DOUBLE oat double DATE java.sql.Date TIME java.sql.Time Les valeurs nulles On peut récupérer le cas ou une valeur d'un enregistrement est null Pour récupérer les valeurs NULL de la base, utiliser la méthode wasNull de la classe ResultSet qui renvoie true si on vient de lire un NULL Les méthodes getXXX() convertissent une valeur NULL en valeur acceptable 0 dans le cas d'un entier, false dans celui d'un booleen, null pour une référence Fermer les différents espaces Pour terminer proprement un programme, il faut fermer les différents espaces ouverts Chaque objet (ResultSet, Connection, Statement) possède une méthode close à appeler. Méta-données d’une requête La méthode getMetaData() de la classe ResultSet permet d'obtenir des informations sur les types de données du ResultSet Renvoie une instance de ResultSetMetaData On peut connaître entre autres: ✓ le nombre de colonnes : getColumnCount() ✓ le nom d'une colonne : getColumnName(int col) ✓ le type d'une colonne : getColumnType(int col) ✓ Si un NULL peut être stocké dans une colonne: isNullable() ✓ ... Exemple Resultset rs = stmt.executeQuery("SELECT * FROM emp"); ResultSetMetaData rsmd = rs.getMetatData(); int nbColonnes = rsmd.getColumnCount(); for(int i = 1; i <= nbColonnes; i++) { /* colonnes numerotees a partir de 1 (et non 0) */ String nomCol = rsmd.getColumnName(i); System.out.println(nomCol + " "); } Accès aux méta-données De la même façon, on peut récupérer les informations sur la base de données elle même Il faut utiliser la méthode getMetaData de la classe connection Renvoie une instance de la classe DatabaseMetaData On peut connaître entre autres : ✓les tables de la base : getTables() ✓le nom de l'utilisateur : getUser() ✓..... Résumé Driver Enregistre DriverManager Fournit Connexion Crée Statement Fournit DataBaseMetaData Retrouve ResultSet Fournit PreparedStatement CallableStatement ResultSetMetaData Requêtes pré-compilées L'objet PreparedStatement envoie une requête avec paramètres à la BD pour pré-compilation et spécifiera le moment venu la valeur des paramètres Plus rapide que les requêtes Statement classiques Permet l'exécution d'une même requête avec différents paramètres Les SGBDR n'acceptent pas toutes les requêtes pré-compilées Création La méthode prepareStatement() de l'objet Connection crée une instance de PreparedStatement Les arguments dynamiques sont spécifiés avec ? ils sont ensuite positionnés par les méthodes setXXX(). Ces méthodes prennent deux arguments : ✓ un entier indiquant le numéro relatif de l'argument de la requête ✓ la valeur à positionner Une fois les valeurs positionnées on exécute la requête avec executeUpdate() ou executeQuery() Exemple PrepareStatement ps = c.prepareStatement( "UPDATE emp SET sal = ? where name = ?"); int count; for(int i = 0; i < 10; i++) { ps.setFloat(1,salary[i]); ps.setString(2,name[i]); ps.executeUpdate(); ... } Validation de transaction Par défaut, un commit est effectué après chaque requête SQL : Mode Auto Commit On peut repasser en mode Manuel : connection.setAutoCommit(false); L'application doit alors renvoyer a la base un commit pour rendre permanents les changements occasionnés par la transaction connection.commit(); Annulation de transaction De même, on peut annuler une transaction Il suffit d'envoyer à la base un rollback retauration.rollback(); Restauration de la base comme elle était après le dernier commit Exceptions Une exception SQLException est générée dès qu'une connexion ou qu'une requête SQL ne se passe pas correctement La méthode getMessage() donne le message en clair de l'erreur