Connexion aux bases de données via JDBC Principe Principe de JDBC • JBDC permet d’exécuter des instructions SQL ; • JDBC utilise le sous-ensemble « ANSI SQL -2 Entry Level » de SQL ; • package : java.sql • un système de base de données est accédé par un pilote qui implémente l’interface java.sql.Driver : • la plupart des SGDB proposent leur pilote ; • Pour des tests la base de données HSQLDB est très utilisée. • JDBC est supplanté par d’autres technologies telles que Hibernate, EJB… Benoît Charroux - JDBC - Mars 07 - 3 Se connecter à une base de données HSQLDB Connection connexion = null ; try { Class.forName("org.hsqldb.jdbcDriver") ; // chargement du pilote connexion = DriverManager.getConnection( "jdbc:hsqldb:hsql://localhost", "sa", null ); } catch( ClassNotFoundException e ){ } catch( SQLException pbSQL ){ login } finally{ try{ if( connexion != null ) connexion.close() ; } catch( SQLException pb ){ } } Benoît Charroux - JDBC - Mars 07 - 4 password Exécuter des requêtes SQL Connection Créer un Statement pour exécuter une requête. Statement createStatement() PreparedStatement prepareStatement( String sql ) close() Statement Retourne un ensemble de réponses. ResultSet executeQuery( String sql ) int executeUpdate( String sql ) Pour les requêtes ne retournant rien ou alors un numéro de rangée (INSERT, UPDATE, DELETE). PreparedStatement setXXX( int index, XXX ) ; Utilise la pré-compilation des requêtes dans les bases de données. Benoît Charroux - JDBC - Mars 07 - 5 Accéder aux données d’une base de données • ResultSet permet de parcourir l’ensemble des résultats d’une requête ; • attention ! le curseur est initialement positionné avant le premier rang. • pour une portabilité maximale, les colonnes doivent être lues de gauche à droite, et les rangées ne doivent être lues qu’une fois. • un ResultSet est lié à son Statement : toute fermeture ou autre utilisation d’un Statement entraîne la fermeture du ResultSet. ResultSet Pour passer à la rangée suivante. next() * getXXX( int numeroColonne ) * getXXX( String nomColonne ) ResultSetMetaData getMetaData() boolean wasNull() Récupère la valeur dans la rangée courante pour la colonne spécifiée. Pour analyser dynamiquement un résultSet. Pour tester si un champ est à nul. Benoît Charroux - JDBC - Mars 07 - 6 Récupérer des résultats de requêtes en Java SQL Java CHAR VARCHAR LONGVARCHAR NUMERIC DECIMAL BIT TINYINT SMALLINT INTEGER BIGINT REAL FLOAT DOUBLE BINARY VARBINARY String getString() String getString() InputStream getUnicodeStream() BigDecimal getBigDecimal() BigDecimal getBigDecimal() boolean getBoolean() byte getByte() short getShort() int getInt() long getLong() float getFloat() double getDouble() double getDouble() byte[] getBytes byte[] getBytes Benoît Charroux - JDBC - Mars 07 - 7 Exemples Gestion d’une base de clients • Comment insérer des données dans la table ? • Comment analyser dynamiquement la table ? Benoît Charroux - JDBC - Mars 07 - 9 Insertion dans la table Clients public void insert( Personne personne ) { try { String req = "INSERT INTO Clients (Nom,Prénom,Age,Adresse) VALUES ( ? , ? , ? , ? )" ; PreparedStatement prepStmt = connexionBASE.prepareStatement( req ) ; prepStmt.setString( 1, personne.getNom() ); prepStmt.setString( 2, personne.getPrenom() ); prepStmt.setInt( 3, personne.getAge() ) ; prepStmt.setString( 4, personne.getAdresse() ) ; prepStmt.executeUpdate(); prepStmt.close(); } catch (SQLException pbSQL) { pbSQL.printStackTrace() ; } } Benoît Charroux - JDBC - Mars 07 - 10 Analyser une table ResultSetMetaData int getColumnCount() String getColumnName() String req = "SELECT * FROM Clients" ; ResultSet resultat = requete.executeQuery( req ); ResultSetMetaData metaData = resultat.getMetaData() ; int nbColonnes = metaData.getColumnCount() ; for( int i=1; i<=nbColonnes; i++ ){ String nomColonne = metaData.getColumnName( i ) ; // … } while( resultat.next() ){ for( int i=1; i<=nbColonnes; i++ ){ Object objet = resultat.getObject( i ) ; if( objet != null ){ String valeur = objet.toString() + " " ) ; } } Benoît Charroux - JDBC - Mars 07 - 11 Gérer des transactions Principes • Une transaction est un ensemble de tâches qui s’exécutent de façon atomique : • soit toute les tâches sont accomplies (commit) ; • si une échoue, toutes échouent (rollback) • qui respectent les règles ACID. • Exemple de transactions entre deux comptes bancaires : begin transaction débiter compte1 créditer compte2 commint transaction Benoît Charroux - JDBC - Mars 07 - 13 Les transactions en Java • Une transaction est associée (et restreinte) à une connexion. True pour permettre les transactions (false par défaut). Connection setAutoCommit( boolean autoCommit ) commit() rollback() Valide tous les changements dans la base de données depuis le dernier commit ou rollback. Annule tous les changements dans la base de données depuis le dernier commit ou rollback. Benoît Charroux - JDBC - Mars 07 - 14 Exemple d’un transfert d’argent 1/2 public void transferer( Client client1, Client client2 ){ Connection connexion = null ; try{ connexion = DriverManager.getConnection( baseODBC, "", ""); connexion.setAutoCommit( false ) ; Float mont = new Float( montant ) ; String requete = "UPDATE Banque SET Solde=(Solde-1000) WHERE Détenteur=?" ; PreparedStatement statement = connexion.prepareStatement( requete ) ; statement.setString( 1, client1.getNom() ) ; statement.executeUpdate() ; client2.verifier() ; // lance une exception requete = "UPDATE Banque SET Solde=(Solde+1000) WHERE Détenteur=?" ; statement = connexion.prepareStatement( requere ) ; statement.setString( 1, client2.getNom() ) ; statement.executeUpdate() ; connexion.commit() ; } catch( Exception e ){ try{ connexion.rollback() ; } catch( SQLException sql ){} } ... Benoît Charroux - JDBC - Mars 07 - 15 Exemple d’un transfert d’argent 2/2 public void transferer( Client client1, Client client2 ){ Connection connexion = null ; try{ // ... } finally { try{ if( connexion != null ) connexion.close() ; } catch( SQLException e ){ } } return false ; } Benoît Charroux - JDBC - Mars 07 - 16 Gérer les connexions pour les transactions Réduire le nombre de connexions • Comment réduire le nombre de connexions tout en assurant une transaction ? public void transferer( Client client1, Client client2 ){ // ... Connection connexion = DriverManager.getConnection("jdbc:odbc:banque", "", "") ; } Une nouvelle connexion est créée à chaque appel de méthode. Benoît Charroux - JDBC - Mars 07 - 18 1ère solution : la synchronisation public class Banque{ Connection connexion ; public Banque() throws ClassNotFoundException { // … connexion = DriverManager.getConnection( "jdbc:odbc:banque", "", "") ; } synchronized public boolean transferer( Client client1, Client client2 ){ // ... connexion.setAutoCommit( false ) ; // ... } } • Inconvénient : un objet Banque est verrouillé à chaque appel de la méthode transferer => mauvaises performances ! Benoît Charroux - JDBC - Mars 07 - 19 2ième solution : utiliser un pool de connexions • Un pool de connexions est un ensemble de connexions déjà ouvertes et dans lequel on vient puiser. public class Banque{ PoolDeConnexions pool ; public Banque() throws Exception{ pool = new PoolDeConnexions() ; } public boolean transferer( Client client1, Client client2, float montant ){ Connection connexion = null ; try{ connexion = pool.getConnection() ; // ... } finally { if( connexion != null ) pool.libereConnexion( connexion ) ; } } } Benoît Charroux - JDBC - Mars 07 - 20