S'il est vrai que, dans de nombreux cas, une instruction SQL de base fera le travail pour de nombreuses modifications ou requêtes de base de données, il s'agit souvent d'une meilleure pratique pour profiter de la flexibilité et des avantages que vous offre l'utilisation de PreparedStatements
.
Les principales différences entre une instruction JDBC standard et un PreparedStatement
sont mieux définis par les avantages qu'un PreparedStatement
vous offre, à vous et à votre application. Ci-dessous, nous examinerons les trois principaux avantages de PreparedStatements
sur les instructions JDBC/SQL normales.
Prévention des injections SQL
Le premier avantage d'utiliser un PreparedStatement
c'est que vous pouvez profiter de la multitude de .setXYZ()
méthodes, telles que .setString()
, qui permet à votre code d'échapper automatiquement les caractères spéciaux tels que les guillemets dans l'instruction SQL transmise, empêchant l'SQL injection
toujours dangereuse attaque.
Par exemple, dans une instruction SQL standard, il peut être courant d'insérer des valeurs directement en ligne avec l'instruction, comme ceci :
statement = "INSERT INTO books (title, primary_author, published_date) VALUES ('" + book.getTitle() + "', '" + book.getPrimaryAuthor() + "', '" + new Timestamp(book.getPublishedDate().getTime()) + "'";
Cela vous obligerait à exécuter votre propre code pour empêcher les injections SQL en échappant les guillemets et autres caractères spéciaux des valeurs insérées.
Inversement, un PreparedStatement
peut être appelé comme suit, en utilisant le .setXYZ()
méthodes pour insérer des valeurs avec un caractère d'échappement automatique lors de l'exécution de la méthode :
ps = connection.prepareStatement("INSERT INTO books (title, primary_author, published_date) VALUES (?, ?, ?)");
ps.setString(1, book.getTitle());
ps.setString(2, book.getPrimaryAuthor());
ps.setTimestamp(3, new Timestamp(book.getPublishedDate().getTime()));
ps.executeUpdate();
Pré-compilation
Un autre avantage d'un PreparedStatement
est que le SQL lui-même est pre-compiled
une seule fois, puis conservée en mémoire par le système, plutôt que d'être compilée à chaque fois que l'instruction est appelée. Cela permet une exécution plus rapide, en particulier lorsqu'un PreparedStatement
est utilisé en conjonction avec batches
, qui vous permettent d'exécuter une série (ou batch
) d'instructions SQL en même temps au cours d'une seule connexion à la base de données.
Par exemple, nous avons ici une fonction qui accepte une List
de livres. Pour chaque book
dans la liste, on veut exécuter un INSERT
déclaration, mais nous allons tous les ajouter à un lot de PreparedStatements
et exécutez-les tous d'un seul coup :
public void createBooks(List<Entity> books) throws SQLException {
try (
Connection connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement("INSERT INTO books (title, primary_author, published_date) VALUES (?, ?, ?)");
) {
for (Entity book : books) {
ps.setString(1, book.getTitle());
ps.setString(2, book.getPrimaryAuthor());
ps.setTimestamp(3, new Timestamp(book.getPublishedDate().getTime()));
ps.addBatch();
}
ps.executeBatch();
}
}
Insertion de types de données anormaux dans l'instruction SQL
Le dernier avantage de PreparedStatements
que nous couvrirons est la possibilité d'insérer des types de données anormaux dans l'instruction SQL elle-même, comme Timestamp
, InputStream
, et bien d'autres.
Par exemple, nous pouvons utiliser un PreparedStatement
pour ajouter une photo de couverture à notre notice de livre en utilisant le .setBinaryStream()
méthode :
ps = connection.prepareStatement("INSERT INTO books (cover_photo) VALUES (?)");
ps.setBinaryStream(1, book.getPhoto());
ps.executeUpdate();