Statement
s'obtient en appelant la méthode
createStatement()
de l'interface
Connection
.
Statement
.
resultSetType
: un entier qui peut prendre les valeurs
TYPE_FORWARD_ONLY
,
TYPE_SCROLL_INSENSITIVE
et
TYPE_SCROLL_SENSITIVE
.
resultSetConcurrency
: un entier qui peut prendre les valeurs
CONCUR_READ_ONLY
et
CONCUR_UPDATABLE
.
resultSetHoldability
: de même, un entier qui peut prendre les valeurs
HOLD_CURSORS_OVER_COMMIT
et
CLOSE_CURSORS_AT_COMMIT
.
ResultSet
. Nous verrons leur signification exacte dans le paragraphe sur les
ResultSet
.
Il est important de fermer un
Statement
lorsqu'il n'est plus utilisé, par appel à sa méthode
close()
.
Statement
. Chacune de ces méthodes doit être utilisée dans son propre contexte.
executeQuery(String)
doit être utilisée si l'exécution du
Statement
retourne une liste d'objets (cas d'un
select
) ;
executeUpdate(String)
doit être utilisée si l'exécution du
Statement
retourne un nombre d'objets modifiés (cas des requête de création, d'effacement ou de mise à jour) ;
execute(String
doit être utilisée si le type de
Statement
exécuté n'est pas connu.
ResultSet
. Si la requête SQL passée en paramètre ne correspond pas (par exemple, si elle contient un
insert
), alors une exception de type
SQLException
est jetée.
Exemple 6. Méthode
Statement.executeQuery(String)
Statement smt = connection.createStatement() ; ResultSet rs = smt.executeQuery("select nom, prenom from Marins") ; // exploitation du resultat while (rs.hasNext()) { ... }
Exemple 7. Méthode
Statement.executeUpdate(String)
Statement smt = connection.createStatement() ; int count = smt.executeUpdate("insert into Marins(nom, prenom) " + "values ('Robert', 'Surcouf')") ; // exploitation du resultat if (count > 0) { ... }
true
si l'objet retourné est de type
ResultSet
et
false
s'il s'agit d'un entier. On peut ensuite obtenir ces objets en appelant les méthodes
getResultSet()
et
getUpdateCount()
.
De plus, si l'exécution de cette requête a généré plusieurs résultats, la méthode
getMoreResult()
doit être appelée. Voyons l'utilisation de toute ceci.
Exemple 8. Méthode
Statement.execute(String)
Statement stmt = conn.createStatement() ; boolean returnedValue = smt.execute(sql) ; // on ne connaît pas la nature // de cette requête ResultSet rs ; int count ; do { if (returnedValue) { // le résultat est un result set rs = stmt.getResultSet(); // exploitation du result set ... } else { // le résultat est un entier count = stmt.getUpdateCount() ; if (count == -1) { // une valeur -1 indique qu'il n'y a plus de résultat à exploiter break ; // sortie du while } else { // exploitation du count ... } } returnedValue = smt.getMoreResult() ; } while (true) ;
resultSet
par appel à la méthode
getResultSet()
ferme automatiquement l'ancien. Ce comportement peut être changé, si le pilote utilisé le permet, comme nous le verrons dans le paragraphe sur les
ResultSet
.
Statement
doivent être fermés par un appel à la méthode
close()
une fois qu'ils ne sont plus utilisés. La fermeture d'un
Statement
entraîne automatiquement la fermeture de tous les objets
ResultSet
qui ont été ouverts sur ce
Statement
.
Notons que même si la fermture d'une connexion (par appel à sa méthode
close()
) ferme automatiquement tous les
Statement
ouverts dessus, il est recommandé de le faire à la main. De même que pour la fermeture des
ResultSet
.
Une fois qu'un objet
Statement
ou
ResultSet
a été fermé, il est illégal d'invoquer une de ses méthodes. L'appel d'une telle méthode peut jeter systématiquement une exception de type
SQLException
.
PreparedStatement
étend l'interface
Statement
, donc tout ce qui précède et qui concerne l'interface
Statement
reste valide pour l'interface
PreparedStatement
.
L'interface
PreparedStatement
ajoute la possibilité de paramétrer des requêtes SQL. Les instances de
PreparedStatement
s'utilisent quand une même requête doit être exécutée plusieurs fois, avec des paramètres différents. La chaîne de caractères SQL contient donc des marqueurs, qui seront remplacés par des valeurs à chaque exécution.
On préfèrera systématiquement l'utilisation de
PreparedStatement
aux
Statement
classiques, en particulier pour les requêtes de mise à jour. Ces requêtes comportent le plus souvent des paramètres, qu'il faut ajouter à la chaîne de caractères SQL à exécuter. Utiliser des
PreparedStatement
permet de passer par les méthodes de paramétrage du pilote de base, ce qui nous décharge de la délicate gestion des erreurs, et évitera les attaques par injection de code SQL.
PreparedStatement
se crée de la même façon qu'un
Statement
normal.
Exemple 9. Création d'un
PreparedStatement
PreparedStatement ps = conn.prepareStatement(
"insert into Marins (nom, prenom, age) values (?, ?, ?)") ;
Statement
normal, on peut passer des options lors de la création d'un
PreparedStatement
.
On remarque la présence de trois caractères
?
dans la chaîne SQL passée en paramètre. Ce sont ces caractères qui doivent être remplacés par des valeurs afin de pouvoir exécuter la requête SQL proprement dite.
PreparedStatement
propose un jeu de méthode
set<Type>(int, Type)
:
setInt(int, int)
,
setFloat(int, float)
,
setString(int, String)
, etc... Chacune de ces méthodes peut être appelée pour fixer la valeur d'un paramètre donné.
Exemple 10. Fixer les paramètres d'un
PreparedStatement
PreparedStatement ps = conn.prepareStatement( "insert into Marins (nom, prenom, age) values (?, ?, ?)") ; ps.setString(1, "Surcouf") ; ps.setString(2, "Robert") ; ps.setInt(3, 32) ;
SQLException
.
On peut effacer la valeur de tous les paramètres d'un
PreparedStatement
en appelant la méthode
clearParameter()
.
null
nécessite l'utilisation d'une méthode particulière :
setNull(int, int)
. Cette méthode prend deux paramètres :
java.sql.Types
Exemple 11. Positionner un paramètre à
null
PreparedStatement ps = conn.prepareStatement( "insert into Marins (nom, prenom, age) values (?, ?, ?)") ; ps.setString(1, "Surcouf") ; ps.setString(2, "Robert") ; ps.setNull(3, java.sql.Types.INTEGER) ;
PreparedStatement
se déroule de la même manière que pour un
Statement
, à l'aide des trois méthodes
executeQuery()
,
executeUpdate()
et
execute()
.
PreparedStatement
propose deux méthodes :
getMetaData()
: retourne un objet
ResultSetMetaData
, qui contient des informations sur les colonnes retournées par la requête SQL ;
getParameterMetaData()
: retourne un objet
ParameterMetaData
, qui contient des informations sur les paramètres déclarés dans ce
PreparedStatement
.
getMetaData()
retourne un objet de type
ResultSetMetaData
qui propose un jeu d'une vingtaine de méthodes pour déterminer le types de colonnes du résultat de la requête. Voyons ceci sur un exemple.
Exemple 12. Utilisation d'un objet
ResultSetMetaData
// on ne connaît pas les colonnes du résultat de cette requête PreparedStatement pstmt = conn.prepareStatement("select * from Marins where id = ?") ; ResultSetMetaData rsmd = pstmt.getMetaData() ; // lecture du nombre de colonne int columnCount = rsmd.geColumnCount() ; for (int i = 1 ; i <= columnCount ; i++) { int columnType = rsmd.getColumnType(i) ; // label de la colonne String columnLabel = rsmd.getColumnLabel(i) ; // nom de la colonne String columnName = rsmd.getColumnName(i) ; }
getParameterMetaData()
retourne un objet de type
ParameterMetaData
, qui lui-même propose quelques méthodes pour obtenir des informations sur les paramètres d'un
PreparedStatement
. Voyons un exemple d'utilisation.
Exemple 13. Utilisation d'un objet
ParameterMetaData
// on ne connaît pas les colonnes du résultat de cette requête PreparedStatement pstmt = conn.prepareStatement("select * from Marins where id = ?") ; ParameterMetaData pmd = pstmt.getParameterMetaData() ; // lecture du nombre de colonne int parameterCount = pmd.geParameterCount() ; for (int i = 1 ; i <= parameterCount ; i++) { // type du paramètre (une constante de java.sql.Types) int parameterType = pmd.getColumnType(i) ; // mode du paramètre int parameterMode = pmd.getParameterMode(i) ; }
IN
,
OUT
ou
INOUT
est utilisé pour les
CallableStatements
.
Exemple 14. Méthode
getGeneratedKeys()
Statement stmt = connection.createStatement() ; // le paramètre RETURN_GENERATED_KEYS va nous permettre de récupérer // la clé générée par MySQL int rowCount = smt.executeUpdate( "insert into Marins (nom, prenom) values ('Surcouf', 'Robert')", Statement.RETURN_GENERATED_KEYS) ; ResultSet rs = stmt.getGeneratedKeys(); if (rs.next()) { // récupération de la clé primaire int idSurcouf = rs.getInt(1) ; }
Statement
et de les exécuter en une seule fois. On appelle ce genre d'exécution
l'exécution en batch
. En général cela signifie que l'on s'attend à ce que l'exécution totale soit assez longue, et que pendant ce temps, le système peut faire autre chose.
L'exécution de commandes SQL en batch n'est possible que pour des requêtes de modification de la base, celles que l'on exécute avec la méthode
executeUpdate(String)
. Toute requête SQL qui peut être exécutée de la sorte, peut également être passée en paramètre de la méthode
addBatch(String)
. L'appel de cette méthode ne fait que stocker la requête SQL, sans l'exécuter. C'est l'appel à la méthode
executeBatch()
qui déclenche l'exécution.
L'exécution en batch peut représenter des gains substantiels en performances. Le coût d'une aller et retour avec la base de données est en lui-même coûteux, et l'on tentera le plus possible de minimiser ces allers et retours.
Statement
.
Exemple 15. Exécution d'un
Statement
en batch
// l'exécution en batch doit se faire en mode non auto-commit connection.setAutoCommit(false) ; Statement smt = connection.createStatement() ; smt.addBatch("insert into Marins(nom, prenom) values ('Surcouf', 'Robert')) ; smt.addBatch("insert into Marins(nom, prenom) values ('Tabarly', 'Eric')) ; ... // lancement de l'exécution de toutes nos insertions int [] updateCounts = smt.executeBatch() ;
executeBatch()
d'annuler la transaction et de remettre la base dans l'état initial.
PreparedStatement
. Le mécanisme est toutefois légèrement différent, puisqu'un
PreparedStatement
est construit sur une unique requête SQL paramétrée.
PreparedStatement
propose une méthode
addBatch()
qui ne prend aucun paramètre. Au moment où cette méthode est appelée, JDBC construit une requête SQL avec la requête du
PreparedStatement
, et le jeu de paramètres qu'il a à sa disposition. Si l'un des paramètres n'a pas été fixé, une erreur est générée. On peut ainsi ajouter autant de requêtes que l'on veut.
L'exécution des requêtes se fait de la même manière et sous les mêmes contraintes que pour un
Statement
classique.
Exemple 16. Exécution d'un
PreparedStatement
en batch
// l'exécution en batch doit se faire en mode non auto-commit connection.setAutoCommit(false) ; PreparedStatement psmt = connection.prepareStatement("insert into Marins(nom, prenom) " + "values (?, ?)") ; psmt.setParameter(1, "Surcouf") ; psmt.setParameter(2, "Robert") ; // ajout d'une requête avec les paramètres ('Surcouf', 'Robert') psmt.addBatch() ; psmt.setParameter(2, "Pierre") ; // ajout d'une requête avec les paramètres ('Surcouf', 'Pierre') psmt.addBatch() ; psmt.setParameter(1, "Tabarly") ; psmt.setParameter(2, "Eric") ; // ajout d'une requête avec les paramètres ('Tabarly', 'Eric') psmt.addBatch() ; ... // lancement de l'exécution de toutes nos insertions int [] updateCounts = smt.executeBatch() ;