Java Data Base Connectivity
Transcription
Java Data Base Connectivity
Java Data Base Connectivity 1 Connexion aux bases de données Rappels API JDBC Pilotes JDBC Connexion ResultSet Requêtes interprétées Requêtes préparées 2 Modélisation des données Création d'un schéma entités/associations Entités : identifiables ( notion de clé) ● pertinente pour l'application ● caractérisées par des propriétés (attributs) ● Représentation graphique ● OMT, Merise, UML 3 Schéma relationnel schéma entité/association A A attr schéma relationnel B - tables A et B - la clé de A devient un attribut de B ( clé étrangère) B - tables A et B - table AB pour l'association avec clé=(clé A,clé B) - attribut de l'association = attribut de la table AB 4 Mysql Types mysql : CHAR(n) INTEGER VARCHAR(n) DECIMAL(m,n) DATE TIME DATETIME TEXT chaîne de longueur n entier chaîne de longueur <=n m= longueur partie entière, n=lg partie décimale jour, mois, an heure, minutes et secondes (temps sur 24h) date et horaire texte de longueur quelconque Serveur de bases de données Relationnelles SQL Expression des contraintes DEFAULT val NOT NULL val = valeur par défaut valeur obligatoire Clés PRIMARY KEY( num) l'attribut num est la clé primaire FOREIGN KEY(idad) REFERENCES Personne la clé étrangère idad référence la clé primaire de la table Personne 5 Correspondances des types de données SQL/Java Type SQL CHAR VARCHAR Type Java String Méthode getter getString() INTEGER int getInt() TINYINT byte getByte() SMALLINT short getShort() BIGINT long getLong() BIT boolean getBoolean() REAL float getFloat() FLOAT DOUBLE double getDouble() NUMERIC DECIMAL java.math.BigDecimal getBigDecimal() DATE java.sql.Date getDate() TIME java.sql.Time getTime() TIMESTAMP java.sql.Timestamp getString() 6 SQL SELECT attr, ... FROM table, ... WHERE condition où condition est une expression sur les attributs des tables du FROM les opérateurs applicables sont : ● opérateurs arithmétiques : +,*,-, ... ● opérateurs relationnels : <,>,>=,... ,!= ● opérateurs logiques : AND, NOT, OR ● BETWEEN, IN, LIKE caractères joker ● '_' remplace n'importe quel caractère ● '%' remplace n'importe quelle chaîne de caractères Destruction DELETE FROM table WHERE condition Modification UPDATE table SET attr1=val1,Ai=vali,... WHERE condition 7 EXEMPLES SELECT * FROM Boutique WHERE nom IN ('Dijon','Lyon') nom ventes date Dijon 1500€ 05-Jan-2009 Lyon 2400€ 21-Jan-2009 nom ventes Dijon 1500€ SELECT * FROM Boutique Chalon 400€ WHERE Date BETWEEN '06-Jan-2009' AND '11-Jan-2009' SELECT * FROM Boutique WHERE nom LIKE '%ON%' date 05-Jan-2009 09-Jan-2009 nom ventes date Dijon 1500€ 05-Jan-2009 Chalon 400€ 09-Jan-2009 Lyon 2400€ 21-Jan-2009 SELECT nom FROM Boutique WHERE ventes > 1000 OR (ventes < 500 AND ventes > 275) 8 Introduction à JDBC Pour les applications web utilisant une ou plusieurs bases de données, les servlets et l'API JDBC offrent une solution : ● ● efficace, la connexion est maintenue durant tout le cycle de vie de l'application flexible, l'accès à une base de données dépend peu de son fournisseur. Il est facile de porter une application vers une base de données d'un autre fournisseur 9 Principe de fonctionnement de JDBC Permet à un programme Java d'interagir localement ou à distance avec une base de données relationnelle Fonctionne selon un principe client/serveur client = le programme Java serveur = la base de données Principe le programme Java ouvre une connexion il envoie des requêtes SQL il récupère les résultats ... il ferme la connexion 10 Architecture 3 tiers utilisateur couche présentation interface web couche métier (domaine) couche d'accès aux données BD les trois couches sont rendues indépendantes grâce à l'utilisation d'interfaces Java 11 API JDBC l'API JDBC se trouve dans le paquetage java.sql l'API JDBC est constituée d'un ensemble d'interfaces pour interagir avec des bases de données l'API JDBC ne fournit donc aucune implémentation des comportements spécifiés par les interfaces l'API JDBC permet d'exécuter des instructions SQL et de récupérer les résultats La version la plus récente de l'API est la version 3.0 12 Pilotes JDBC (1/2) Le pilote JDBC a pour rôle de permettre l'accès à un système de bases de données particulier. Il gère le détail des communications avec un type de SGBD A une API JDBC correspond plusieurs implémentations selon les fournisseurs (un type de driver par SGBD Oracle, Sybase, Access, ...). Il en existe pour la plupart des SGBD relationnels Le pilote implémente l'interface java.sql.Driver Il existe un driver générique JDBC-ODBC pour toutes les données accessibles par ODBC - SGBD : MS Access, SQL Server, Oracle, dBase, ... - tableurs : Excel, ... - tout autre application conforme ODBC 13 Gestionnaire de pilotes DriverManager DriverManager est une classe, gestionnaire de tous les drivers chargés par un programme Java ● ● Chaque programme Java charge le (ou les) drivers dont il a besoin L'enregistrement des pilotes est automatique 14 Java et les BD application Connexion Connexion Gestionnaire Gestionnairede depilotes pilotes Pilote PiloteOracle Oracle Pont PontJDBC_ODBC JDBC_ODBC Pilote PiloteSybase Sybase Pilote PiloteODBC ODBC BD Oracle BD Sybase BD ODBC 15 Connexion à une base de données Disposer de l'URL d'un pilote pour le SGBD com:mysql:jdbc:Driver Sun:jdbc:odbc:JdbcOdbcDriver jdbc:sqlite Charger le pilote Class.forName("com:mysql:jdbc:Driver"); // création d'une instance du pilote // enregistrement automatique auprès du DriverManager Etablir l'URL (protocole) de la base String dbUrl="jdbc:mysql://localhost:3306/maBD"; String dbUrl="jdbc:sqlite:mesdata/mesbds/mags.sqlite"; Ouvrir la connexion : le DriverManager choisit le pilote approprié à cette URL parmi l'ensemble des pilotes disponibles. Connection dbcon = DriverManager.getConnection(dbUrl,user,password); Connection dbcon = DriverManager.getConnection(dbUrl); 16 Code de connexion : exemple (1/2) try { //Déclaration du driver : Class.forName("com.mysql.jdbc.Driver"); //Url de connexion String dbUrl="jdbc:mysql://localhost:3306/laboBD"; //Profil/mot de passe String user = "root"; String password = "19AB5"; //connexion à la base dbcon = DriverManager.getConnection(dbUrl,user,password); } 17 Code de connexion : exemple (2/2) catch( ClassNotFoundException e ){ // erreur de chargement du pilote } catch( SQLException e ){ // erreur d'obtention de la connexion } finally{ // fermeture de la connexion pour libérer // les ressources de la BD try{ if (dbcon!=null) dbcon.close(); } catch(SQLException e){...} } 18 Sqlite : connexion Class.forName("org.sqlite.JDBC"); Connection conn = DriverManager.getConnection ("jdbc:sqlite:mesdata.mesbds.magss.sqlite"); Fermeture de la connexion conn.close(); 19 3 types de requêtes interface Statement interface PreparedStatement requêtes interprétées à chaque exécution requêtes précompilées interface CallableStatement requêtes stockées dans la base 20 L'interface ResultSet Un objet de type ResultSet est une table représentant le résultat d'une requête à une base de données Le nombre de lignes du tableau peut être nul. Les données de la table sont accessibles ligne par ligne grâce à un curseur Un resultSet gère un curseur sur la ligne courante de la table des données satisfaisant aux conditions de la requête. 21 Exécution d'une requête SQL de type Statement (1/2) ● ● Le type de résultat obtenu dépend de la manière dont on souhaite manipuler le ResultSet 3 constantes de types : TYPE_FORWARD_ONLY ( le curseur se déplace uniquement vers l'avant ) TYPE_SCROLL_INSENSITIVE ( le curseur se déplace en avant et en arrière mais le Resultset est insensible aux modifications apportées à la BD durant le traitement) TYPE_SCROLL_SENSITIVE ( le curseur se déplace en avant et en arrière mais les changements faits à la BD durant le traitement sont reflétés dans le ResultSet) ● 2 constantes de concurrence : CONCUR_READ_ONLY ( les données du Resultset ne peuvent pas être modifiées) CONCUR_UPDATABLE ( des modifications de la BD peuvent être effectuées via le Resultset ) 22 Exemple ● // // // // // Créer un objet de la classe java.sql.Statement Statement stmt = dbcon.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); indique que le ResultSet est scrollable et non modifiable. ResultSet.CONCUR_UPDATABLE indiquerait qu'un seul utilisateur à la fois peut modifier la donnée Statement stmt = dbcon.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); // lorsqu'on apporte un changement au ResultSet, on peut y // accéder par la suite car le type est "scrollable" et on peut // récupérer sa valeur car le type est "SENSITIVE" Pour que les modifications apportées au Resultset prennent effet dans la base, il faut appliquer les méthodes updateRow 23 Sqlite : création Statement stat = conn.createStatement(); stat.executeUpdate("drop table if exists magasins;"); stat.executeUpdate("create table magasins (id, magasin,produit,prix);"); 24 Exécution d'une requête SQL de type Statement (2/2) Par défaut, le ResultSet n'est ni scrollable, ni modifiable Statement stmt = dbcon.createStatement(); dbcon.createStatement(TYPE_FORWARD_ONLY, CONCUR_READ_ONLY); Construire la requête SQL Soit la table personne(nom,prenom,age) String reqSQL = "select * from personne"; Exécuter la requête ResultSet rs = stmt.executeQuery( reqSQL ); 25 Exploiter les résultats d'une requête (1/5) A la création du ResultSet, le curseur est placé avant la première ligne (N°1). Pour le déplacer, on dispose des méthodes : ● next() déplacement d'une ligne en avant ● previous() déplacement d'une ligne en arrière ● first() déplacement vers la première ligne ● last() déplacement vers la dernière ligne ● ● beforeFirst() déplacement avant la première ligne. Si le ResultSet est vide, cela n'a aucun effet. afterLast() déplacement après la dernière ligne. Si le ResultSet est vide, cela n'a aucun effet. ● relative(int i) déplace de i lignes par rapport à la position courante ● absolute(int i) déplace le curseur vers la i-ème ligne 26 Exploiter les résultats d'une requête (2/5) Déplacement absolu absolute( int i) i>0 déplacement vers la ligne numéro i rs.absolute(1) => déplacement vers la ligne 1 i<0 déplacement à partir de la fin rs.absolute(-1) => déplacement sur la dernière ligne rs.absolute(-3)=> si 30 lignes, déplacement vers ligne 28 Déplacement relatif relative( int i) On spécifie le nombre de déplacements à partir de la ligne courante i>0 => déplacements en avant i<0 => déplacements en arrière rs.absolute(4); // curseur sur la 4ème ligne rs.relative(-3); // curseur sur la première ligne rs.relative(2); // curseur sur la 3ème ligne 27 Exploiter les résultats d'une requête (3/5) On récupère le contenu d'une ligne du ResultSet colonne par colonne Les méthodes d'accès sont construites sur le modèle : getXXX (XXX étant un type primitif ou String) La colonne est spécifiée soit par son nom, soit par son numéro (la première colonne est numérotée 1) par son nom String getString(String nomAttribut) float getFloat(String nomAttribut) par le numéro de la colonne qui lui correspond String getString(int indexColonne) float getFloat(int indexColonne) idem pour les int, double, long, boolean, Object 28 Exploiter les résultats d'une requête (4/5) Pour obtenir le n° de la ligne courante int getRow() rs.absolute(5); int i = rs.getRow(); // i=5 Pour tester la position dans le Resultset isFirst, isLast, isBeforeFirst, isAfterLast. if (rs.isAfterLast() == false) { rs.afterLast(); } 29 Exploiter les résultats d'une requête (4/5) String reqSQL = "select * from personne"; ResultSet rs = stmt.executeQuery( reqSQL ); while (rs.next()){ String prenom = rs.getString("prenom"); int age = rs.getInt("age"); } 30 Sqlite : interrogation ResultSet rs = stat.executeQuery ("select * from magasins;"); while (rs.next()) { System.out.println("magasin = " + rs.getString("magasin")); System.out.println("produit = " + rs.getString("produit")); System.out.println("prix = " + rs.getString("prix")); } rs.close(); 31 Mises à jour de table (1/2) Il est préférable que le ResultSet soit scrollable lors d'une mise à jour Statement stmt = con.createStatement( ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); Pour mettre à jour, 2 méthodes: updateXXX avec 2 paramètres (nom et valeur de la colonne) updateRow() rendre effective la mise à jour 32 Mises à jour de table (2/2) Nom Meyer Legrand Dupont Prénom René Léa Marcel->Robert Age 54->37 18 43 String reqSQL = "select * from personne"; ResultSet rs = stmt.executeQuery( reqSQL ); rs.next(); rs.updateInt("Age",37); rs.last(); rs.updateString("Prénom","Robert"); rs.updateRow(); 33 L'interface ResultSetMetaData Permet d'obtenir la description du contenu du ResultSet ResultSetMetaData metaData = rs.getMetaData(); A partir de cette information, obtenir le nombre de colonnes du résultat int nbCols = metaData.getColumnCount(); Obtenir le nom de la colonne connaissant son indice (à partir de 1) String nomCol = metaData.getColumnName( int i ); 34 Exemple ResultSet rs = stmt.executeQuery( "SELECT * FROM personne" ); StringBuffer resultat = new StringBuffer(); ResultSetMetaData metaData = rs.getMetaData(); int nbCols = metaData.getColumnCount(); for ( int i=1;i<=nbCols;i++ ) resultats.append( metaData.getColumnName( i )+"\t" ); resultats.append( "\n" ); while ( rs.next() ){ for ( int i=1;i<=nbCols;i++ ) // pour simplifier, chaque donnée est du type Object // sinon le type de la valeur est obtenu par getColumnType resultats.append( rs.getObject( i )+"\t" ); resultats.append( "\n" ); } 35 Requêtes préparées L'exécution de chaque requête à une BD nécessite 4 étapes : analyse compilation optimisation Exécution Pour des requêtes identiques, les 3 premières étapes n'ont pas à être effectuées de nouveau. Avec la notion de requête préparée, les 3 premières étapes ne sont effectuées qu'une seule fois. JDBC propose l'interface PreparedStatement qui dérive de l'interface Statement pour modéliser cette notion. 36 Exécution d'une requête SQL de type PreparedStatement Avec l'interface Statement, on écrivait : Statement smt = dbcon.createStatement(); ResultSet rs = smt.executeQuery("SELECT * FROM personne" ); Avec l'interface PreparedStatement, on écrit : PreparedStatement pSmt = dbcon.prepareStatement("SELECT * FROM personne" ); ResultSet rs = pSmt.executeQuery(); ● On voit que la requête est préparée à l'avance et n'est donc pas transmise en argument au moment de l'exécution ( executeQuery()). 37 Exécution d'une requête SQL de type PreparedStatement Pour préparer des requêtes paramétrées de la forme : SELECT nom FROM Personnes WHERE age > ? AND adresse = ? On utilise les PreparedStatement avec les méthodes setType(numéroDeLArgument, valeur) Type représente le type de l'argument numéroDeLArgument représente le numéros de l'argument (commençant à 1 dans l'ordre d'apparition dans la requête). valeur représente la valeur qui lui est associée Exemple : PreparedStatement pSmt = dbcon.prepareStatement ("SELECT nom FROM Personne WHERE age > ? AND prenom=?" ); pSmt.setInt(1, 22); pSmt.setString(2, "Adrien"); ResultSet rs = pSmt.executeQuery(); 38 Exécution d'une requête SQL de type PreparedStatement PreparedStatement psmt = dbcon.prepareStatement( "update personne set nom = ? where prenom like ?"); psmt.setString(1, "Bauer"); psmt.setString(2, "René"); int i = psmt.executeUpdate(): On a changé en Bauer le nom de la personne dont le prénom est René La valeur retournée par executeUpdate() est le nombre de lignes mises à jour 39 Sqlite : insertion PreparedStatement prep = conn.prepareStatement( "insert into magasins (magasin,produit,prix) values (?, ?, ?);"); prep.setString(1, "fdk"); prep.setString(2, "cafe"); prep.setFloat(3, 0.40f); prep.addBatch(); prep.setString(1, "fdk"); prep.setString(2, "soda"); prep.setFloat(3, 1.10f); prep.addBatch(); prep.setString(1, "fdk"); prep.setString(2, "barres"); prep.setFloat(3, 0.90f); prep.addBatch(); conn.setAutoCommit(false); prep.executeBatch(); conn.setAutoCommit(true); 40 Procédures stockées (1/2) ● Les procédures stockées permettent d'embarquer du code procédural directement dans la base ● Elles exécutent une ou plusieurs requêtes SQL ● Intérêt : ● Elles sont précompilées ● Ne nécessitent pas de trafic réseau ● JDBC utilise la classe java.sql.CallableStatement ● Syntaxe d'appel des procédures stockées ● {call nom-proc-stokée(?,?)} // procédure sans résultat ● {?=call nom-proc-stokée(?,?)} // retour d'une valeur 41 Procédures stockées (2/2) CallableStatement cstmt = dbcon.prepareCall("{call nom-proc-stokée(?,?)}"); cstmt.registerOutParameter(2,java.sql.Types.FLOAT); // indique que le second paramètre donne le résultat cstmt.setInt(1,ID); // fixe la donnée en entrée cstmt.execute(); //exécute la procédure stockée System.out.println("Solde : " +cstmt.getFloat(2); // affiche le résultat de l'exécution de la procédure // stockée figurant dans le second paramètre 42 Exercice ● Soit une BD de nom " mags ", constituée de la seule table magasins : (ID, magasin, produit, prix) ● Cette BD est accessible avec les identifiants (" root ", " 2dwsk8 ") Inutile pour sqlite ● Ecrire le morceau de code Java permettant d'afficher le résultat de la requête : " liste des produits dont le prix est inférieur à 50€ " sous la forme : produit ..... ..... ● ● prix ..... ..... On utilisera pour cela les requêtes préparées de JDBC Compléter le code pour modifier la table afin de ramener les prix 43 de 50€ à 49,95€ Utilisation de sqlite ● SQLite : gestionnaire de BD sans nécessité d'un serveur de BD ● Télécharger le pilote JDBC pour SQLite : sqlitejdbc-v056.jar ● Sous firefox, télécharger et installer le add-on : https://addons.mozilla.org/en-US/firefox/addon/sqlite-manager/ ● Sous firefox, lancer le manager (menu outils) ● Entrer le nom de la BD ● Connecter la BD ● Créer les tables ● Insérer les valeurs 44 Exporter une BD Mysql Sous wamp, il faut exporter la base pour obtenir le script sql correspondant On ajoute ensuite une ligne qui permet de vérifier l'existence d'une Base de même nom DROP DATABASE IF EXISTS `tp`; CREATE DATABASE `tp` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci; USE `tp`; CREATE TABLE `personne` (`nom` varchar(20) NOT NULL default '',`prenom` varchar(20) NOT NULL default '',`age` tinyint(4) NOT NULL default '0',PRIMARY KEY (`nom`,`prenom`)) ENGINE=MyISAM DEFAULT CHARSET=latin1; INSERT INTO `personne` VALUES ('Meyer', 'Luc', 30); INSERT INTO `personne` VALUES ('Dupont', 'René', 40); Il suffit d'enregistrer le script dans un fichier sur votre clé usb ATTENTION : chaque commande sql est situé sur une seule ligne 45