Une introduction à JDBC

Présentation de JDBC

Les objectifs de JDBC

JDBC = Java Data Base Connectivity

JDBC est basé sur

Objectifs :

JDBC dans un client léger

Utilisation de JDBC dans un client léger:

figures/jdbc/clt-applet.png

JDBC dans une architecture J2EE

Architecture d'exécution répartie dans la plateforme J2EE :

figures/jdbc/j2ee.png

Architecture Logicielle

figures/jdbc/struct-log.png

Les pilotes JDBC

Il existe quatre types de pilote :

figures/jdbc/pilotes.png

Utilisation de JDBC

Squelette de notre exemple

import java.sql.DriverManager;  // gestion des pilotes
import java.sql.Connection;     // une connexion à la BD
import java.sql.Statement;      // une instruction 
import java.sql.ResultSet;      // un résultat (lignes/colonnes)
import java.sql.SQLException;   // une erreur

public class ExempleJdbc {

    // chargement du pilote

    // ouverture de connexion

    // exécution d'une requête

    // programme principal

}

Le paquetage java.sql regroupe les interfaces et les classes de l'API JDBC.

Déclaration du pilote JDBC

Méthode de chargement explicite d'un pilote :

void loadDriver() throws ClassNotFoundException {
    Class.forName("com.mysql.jdbc.Driver");
}

Connexion à la base de données

Méthode d'ouverture d'une nouvelle connexion :

Connection newConnection() throws SQLException {
    final String url = "jdbc:mysql://localhost/dbessai";
    Connection conn = DriverManager.getConnection(url, "bduser", "SECRET");
    return conn;
}

L'URL est de la forme

jdbc:sous-protocole:sous-nom

Quelques exemples (à chercher dans la documentation du pilote) :

jdbc:oracle://srv.dil.univ-mrs.fr:1234/dbtest
jdbc:odbc:msql;USER=fred;PWD=secret

Les requêtes en JDBC

Un exemple d'utilisation :

public void listPersons() throws SQLException {
    Connection conn = null;
    try {
        // create new connection and statement
        conn = newConnection();
        Statement st = conn.createStatement();

        String query = "SELECT nom,prenom,age FROM personne ORDER BY age";
        ResultSet rs = st.executeQuery(query);

        while (rs.next()) {
            System.out.printf("%-20s | %-20s | %3d\n", //
                    rs.getString(1), rs.getString("prenom"), rs.getInt(3));
        }
    } finally {
        // close result, statement and connection
        if (conn != null) conn.close();
    }
}

Conseils :

Programme principal

Mise en oeuvre et gestion des erreurs :

public static void main(String[] Args) {
    ExempleJdbc test = new ExempleJdbc();
    try {
        test.loadDriver();
        test.listPersons();
        ...
    } catch (ClassNotFoundException e) {
        System.err.println("Pilote JDBC introuvable !");
    } catch (SQLException e) {
        System.out.println("SQLException: " + e.getMessage());
        System.out.println("SQLState:     " + e.getSQLState());
        System.out.println("VendorError:  " + e.getErrorCode());
        e.printStackTrace();
    }
}

L'interface java.sql.ResultSet

Accès aux valeurs :

Type getType( int numeroDeColonne );
Type getType( String nomDeColonne );
boolean next();

Le Type peut être

Byte Boolean AsciiStream
Short String UnicodeStream
Int Bytes BinaryStream
Long Date Object
Float Time
BigDecimal TimeStamp

Correspondance des types Java / SQL

SQL Java
CHAR String
VARCHAR String
LONGVARCHAR String
NUMERIC java.math.BigDecimal
DECIMAL java.math.BigDecimal
BIT boolean
TINYINT byte
SMALLINT short
INTEGER int
BIGINT long
REAL float
FLOAT double
DOUBLE double
BINARY byte[]
VARBINARY byte[]
LONGVARBINARY byte[]

Correspondance des dates et heures

Correspondance des dates

SQL Java Explication
DATE java.sql.Date codage de la date
TIME java.sql.Time codage de l'heure
TIMESTAMP java.sql.TimeStamp codage de la date et de l'heure

Modification de la base

Insertion de lignes

Un exemple:

Statement st = conn.createStatement();

int nb = st.executeUpdate(
    "INSERT INTO personne(Nom,Age) " +
    "VALUES ('" + nom + "', " + age + ")"
    );

System.out.println(nb + " ligne(s) insérée(s)");
st.close();

Ce principe est aussi utilisable pour les instructions UPDATE et DELETE.

Difficultés à manipuler des données

Un exemple:

Statement st = conn.createStatement();

int nb = st.executeUpdate(
    "UPDATE personne " +
    "SET Age = " + age + " " +
    "WHERE Nom = '" + nom + "' "
    );

Inconvénients: solution coûteuse (boucle) et difficile à mettre en oeuvre.

SQL Préformaté

Code SQL avec partie variable :

PreparedStatement st = conn.prepareStatement(
    "UPDATE personne SET Age = ? " +
    "WHERE Nom = ? "
    );

for( ... ) {
    st.setInt(1, age[i]);
    st.setString(2, nom[i]);
    st.execute();
    }

Avantages: compilation unique et paramètres binaires plus faciles à passer.

Appel de procédures stockées en base

Un exemple:

CallableStatement st = conn.prepareCall(
    "{call ma_procedure[(?,?)]}"  );
    // ou {? = call nom_de_fonction[(?, ..., ?)]}

// fixer le type de paramètre de sortie
st.registerOutParameter(2, java.sql.Types.FLOAT);

st.setInt(1, valeur); // fixer la valeur du paramètre

st.execute();
System.out.println("résultat = " + st.getFloat(2));

Avantages:

Inconvénient: pas de norme!

Erreurs et warnings

La classe java.sql.SQLException enrichit la classe java.lang.Exception:

La classe java.sql.SQLWarning enrichit la classe java.sql.SQLException:

Gestion des transactions

Le mode par défaut est « Auto Commit »:

Obtenir des informations sur la BD

Méta Informations sur les Result Set

Exemple :

ResultSetMetaData m = rs.getMetaData();

Informations disponibles :

Avantages :

Méta Informations sur la B.D.

Exemple :

DataBaseMetaData dbmd = connexion.getMetaData();

Informations disponibles :

JDBC version 2.1

Contenu:

Nouvelle version des « Result Set »

Il existe quatre types de Result Set:

♦ Scroll-insensitive: Vision figée du résultat de la requête au moment de son évaluation.

♦ Scroll-sensitive: Le Result Set montre l'état courant des données (modifiées/détruites).

♦ Read-only: Pas de modification possible (JDBC 1.0) donc un haut niveau de concurrence.

♦ updatable: Possibilité de modification donc pose de verrou et faible niveau de concurrence.

Mise en oeuvre de ces « Result Set »

Statement stmt = con.createStatement(
    ResultSet.TYPE_SCROLL_INSENSITIVE,
    ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery("SELECT * FROM Personne");

Ce ResultSet est modifiable mais il ne reflète pas les modifications faites par d'autres transactions.

Déplacement dans un « Result Set »


rs.first();

rs.beforeFirst();

rs.next();

rs.previous();

rs.afterLast();

rs.absolute( n );

rs.relative( n );

Modification d'un « Result Set »

Modification:

rs.absolute(100);
rs.updateString("Nom", "Fred");
rs.updateInt("Age", 30);
rs.updateRow();

Destruction:

rs.deleteRow();

Insertion de lignes:

rs.moveToInsertRow();
rs.updateString("Nom", "Fred");
rs.updateInt("Age", 30);
rs.insertRow();
rs.first();

batch updates

Regroupement de plusieurs mise à jour:

connexion.setAutoCommit(false);
Statement st = connexion.createStatement();

st.addBatch("INSERT ...");
st.addBatch("INSERT ...");

int[] nb = st.executeBatch();

On peut combiner des « Prepared Statement » et des « Batch updates ».

JDBC 3.0

Améliorations:

♦ nouveau package javax.sql.*,

♦ Save point: pose de point de sauvegarde.

♦ Connection Pool: Gestion des ensembles de connexions partagées.

♦ Support des séquences (auto génération de valeurs).

♦ Augmentation et mise à jour des types (CLOB, BLOB, références SQL3).

♦ Prise en compte de SQL-3.

Les DataSource

L'interface javax.sql.DataSource permet:

Les RowSet

L'accès aux données est encapsulé dans un seul Bean:

javax.sql.rowset.CachedRowSet rs =
    new com.sun.rowset.CachedRowSetImpl();

rs.setUrl("jdbc:mysql://localhost/dbessai");
rs.setCommand("SELECT * FROM personne");
rs.setUsername("massat");
rs.setPassword("...");
rs.setConcurrency(ResultSet.CONCUR_UPDATABLE);
rs.execute();

while (rs.next())
    System.out.println("Nom : " + rs.getString("nom"));

rs.close();

Il existe trois types de RowSet: