La déclaration préparée est une version légèrement plus puissante d’une déclaration et doit toujours être au moins aussi rapide et facile à manipuler qu’une déclaration.
La déclaration préparée peut être paramétrée
La plupart des bases de données relationnelles traitent une requête JDBC/SQL en quatre étapes:
Une instruction suivra toujours les quatre étapes ci-dessus pour chaque requête SQL envoyée à la base de données. Une instruction préparée pré-exécute les étapes (1) - (3) dans le processus d'exécution ci-dessus. Ainsi, lors de la création d'une instruction préparée, une pré-optimisation est effectuée immédiatement. L'effet est de réduire la charge sur le moteur de base de données au moment de l'exécution.
Maintenant, ma question est la suivante: "Y at-il un autre avantage à utiliser Prepared Statement?"
Avantages d'un PreparedStatement
:
La précompilation et la mise en cache côté SQL de l'instruction SQL permettent globalement une exécution plus rapide et la possibilité de réutiliser la même instruction SQL dans lots .
Prévention automatique de injection SQLattaques par échappement intégré des guillemets et autres caractères spéciaux. Notez que cela nécessite que vous utilisiez l’une des méthodes PreparedStatement
setXxx()
pour définir les valeurs.
preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
preparedStatement.setString(1, person.getName());
preparedStatement.setString(2, person.getEmail());
preparedStatement.setTimestamp(3, new Timestamp(person.getBirthdate().getTime()));
preparedStatement.setBinaryStream(4, person.getPhoto());
preparedStatement.executeUpdate();
et ainsi ne pas insère les valeurs de la chaîne SQL par concaténation de chaînes.
preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email) VALUES ('" + person.getName() + "', '" + person.getEmail() + "'");
preparedStatement.executeUpdate();
Facilite la définition d'objets Java non standard dans une chaîne SQL, par exemple. Date
, Time
, Timestamp
, BigDecimal
, InputStream
( Blob
) et Reader
( Clob
). Sur la plupart de ces types, vous ne pouvez pas "simplement" faire une toString()
comme vous le feriez dans un simple Statement
. Vous pouvez même refactoriser le tout en utilisant PreparedStatement#setObject()
à l'intérieur d'une boucle, comme illustré dans la méthode de l'utilitaire ci-dessous:
public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setObject(i + 1, values[i]);
}
}
Qui peut être utilisé comme ci-dessous:
preparedStatement = connection.prepareStatement("INSERT INTO Person (name, email, birthdate, photo) VALUES (?, ?, ?, ?)");
setValues(preparedStatement, person.getName(), person.getEmail(), new Timestamp(person.getBirthdate().getTime()), person.getPhoto());
preparedStatement.executeUpdate();
Ils sont précompilés (une fois), donc plus rapidement pour l'exécution répétée de SQL dynamique (où les paramètres changent)
La mise en cache des instructions de base de données améliore les performances d'exécution de la base de données
Les bases de données stockent des caches de plans d'exécution pour des instructions précédemment exécutées. Cela permet au moteur de base de données de réutiliser les plans pour les instructions précédemment exécutées. Etant donné que PreparedStatement utilise des paramètres, chaque fois qu'il est exécuté, il apparaît comme le même code SQL, la base de données peut réutiliser le plan d'accès précédent, ce qui réduit le traitement. Les instructions "inline" les paramètres dans la chaîne SQL et n'apparaissent donc pas comme le même code SQL dans la base de données, ce qui empêche l'utilisation du cache.
Le protocole de communication binaire signifie moins de bande passante et des communications plus rapides vers le serveur de base de données
Les instructions préparées sont normalement exécutées via un protocole binaire non SQL. Cela signifie que les paquets contiennent moins de données, ce qui accélère les communications avec le serveur. En règle générale, les opérations réseau sont d'un ordre de grandeur plus rapide que les opérations sur disque, lesquelles sont d'un ordre de grandeur plus rapide que les opérations de processeur en mémoire. Par conséquent, toute réduction de la quantité de données envoyées sur le réseau aura un effet positif sur les performances globales.
Ils protègent contre l'injection SQL en échappant le texte pour toutes les valeurs de paramètre fournies.
Ils offrent une séparation plus étroite entre le code de requête et les valeurs de paramètre (par rapport aux chaînes SQL concaténées), améliorant la lisibilité et aidant les responsables de code à comprendre rapidement les entrées et les sorties de la requête.
En Java, vous pouvez appeler getMetadata () et getParameterMetadata () pour réfléchir aux champs de l'ensemble de résultats et aux champs de paramètres, respectivement.
En Java, accepte intelligemment les objets Java en tant que types de paramètre via setObject, setBoolean, setByte, setDate, setDouble, setDouble, setDloble, setFloat, setInt, setLong, setShort, setTime, setTimestamp - convertit au format de type JDBC non compliqué. au format DB (pas seulement au format toString ()).
En Java, accepte les SQL ARRAY, en tant que type de paramètre via la méthode setArray
En Java, accepte les CLOB, BLOB, OutputStreams et Readers en tant que paramètres "feeds" via les méthodes setClob/setNClob, setBlob, setBinaryStream, setCharacterStream/setAsciiStream/setNCharacterStream, respectivement
En Java, permet de définir des valeurs spécifiques à la base de données pour les méthodes SQL DATALINK, SQL ROWID, SQL XML et NULL via les méthodes setURL, setRowId, setSQLXML et setNull
En Java, hérite de toutes les méthodes de Statement. Il hérite de la méthode addBatch et permet en outre d'ajouter un ensemble de valeurs de paramètres pour correspondre à l'ensemble de commandes SQL mises en lots via la méthode addBatch.
En Java, un type spécial de PreparedStatement (la sous-classe CallableStatement) permet l'exécution de procédures stockées: prise en charge des performances élevées, encapsulation, programmation procédurale et SQL, administration/maintenance/optimisation de la base de données, et utilisation de la logique et des fonctions de base de données propriétaires.
PreparedStatement
est une très bonne défense (mais pas infaillible) pour empêcher attaques par injection SQL . La liaison des valeurs de paramètres est un bon moyen de se protéger contre "petites tables de Bobby" d'effectuer une visite indésirable.
Les avantages de PreparedStatement par rapport à Statement sont les suivants:
En savoir plus sur le problème d’injection SQL à l’adresse http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example
rien à ajouter,
1 - Si vous souhaitez exécuter une requête dans une boucle (plus d'une fois), une instruction préparée peut être plus rapide, en raison de l'optimisation que vous avez mentionnée.
La requête à 2 paramètres est un bon moyen d'éviter l'injection SQL, qui n'est disponible que dans PreparedStatement.
L'instruction est statique et l'instruction préparée est dynamique.
Instruction convient pour DDL et instruction préparée pour DML.
La déclaration est plus lente que la déclaration préparée est plus rapide.
Ne peut pas faire CLOB dans une déclaration.
Et: (OraclePreparedStatement) ps
l'injection SQL est ignorée par l'instruction préparée de sorte que la sécurité est augmentée dans l'instruction préparée
Comme cité par mattjames
L'utilisation d'une instruction dans JDBC doit être localisée à 100% et utilisée pour DDL (ALTER, CREATE, GRANT, etc.), car ce sont les seuls types d'instructions qui ne peuvent pas accepter les variables BIND. PreparedStatements ou CallableStatements doivent être utilisés pour TOUS les types d'instructions (DML, Queries). Comme ce sont les types d'instructions qui acceptent les variables de liaison.
C’est un fait, une règle, une loi - utiliser des déclarations préparées PARTOUT. Utilisez des déclarations presque pas où.
Instruction sera utilisée pour exécuter des instructions SQL statiques et ne peut accepter les paramètres d'entrée.
PreparedStatement sera utilisé pour exécuter des instructions SQL de nombreuses fois de manière dynamique. Il acceptera les paramètres d'entrée.
Autre caractéristique de la requête préparée ou paramétrée: Référence tirée de cet article.
Cette instruction est l'une des fonctionnalités du système de base de données dans laquelle la même instruction SQL s'exécute de manière répétée avec une grande efficacité. Les instructions préparées sont un type de modèle et sont utilisées par l’application avec différents paramètres.
Le modèle d’instruction est préparé et envoyé au système de base de données et celui-ci effectue l’analyse, la compilation et l’optimisation de ce modèle et le stocke sans l’exécuter.
Certains paramètres, tels que ceux où la clause n'est pas transmise lors de la création ultérieure du modèle, envoient ces paramètres au système de base de données et le modèle d'utilisation de la base de données de l'instruction SQL sont exécutés à la demande.
Les instructions préparées sont très utiles contre l'injection SQL car l'application peut préparer un paramètre à l'aide de techniques et de protocoles différents.
Lorsque le nombre de données augmente et que les index changent fréquemment à ce moment-là, les instructions préparées peuvent échouer car, dans cette situation, un nouveau plan de requête est requis.
L'interface Statement
exécute des instructions SQL statiques sans paramètres
PreparedStatement
interface (extend Statement) exécute une instruction SQL précompilée avec/sans paramètres
Efficace pour les exécutions répétées
Il est précompilé, donc c'est plus rapide
Ne pas confondre: rappelez-vous simplement
J'ai suivi toutes les réponses de cette question pour changer un code existant en utilisant - Statement
(mais avec des injections SQL) en une solution en utilisant PreparedStatement
avec un code beaucoup plus lent en raison d'une mauvaise compréhension de la sémantique autour de Statement.addBatch(String sql)
& PreparedStatement.addBatch()
.
Je liste donc mon scénario ici pour que les autres ne commettent pas la même erreur.
Mon scénario était
Statement statement = connection.createStatement();
for (Object object : objectList) {
//Create a query which would be different for each object
// Add this query to statement for batch using - statement.addBatch(query);
}
statement.executeBatch();
Donc, dans le code ci-dessus, j'avais des milliers de requêtes différentes, toutes ajoutées à la même instruction et ce code fonctionnait plus rapidement, car les instructions non mises en cache étaient bonnes et ce code était rarement exécuté dans l'application.
Maintenant, pour réparer les injections SQL, j'ai changé ce code en,
List<PreparedStatement> pStatements = new ArrayList<>();
for (Object object : objectList) {
//Create a query which would be different for each object
PreparedStatement pStatement =connection.prepareStatement(query);
// This query can't be added to batch because its a different query so I used list.
//Set parameter to pStatement using object
pStatements.add(pStatement);
}// Object loop
// In place of statement.executeBatch(); , I had to loop around the list & execute each update separately
for (PreparedStatement ps : pStatements) {
ps.executeUpdate();
}
Alors, voyez-vous, j'ai commencé à créer des milliers d'objets PreparedStatement
, puis à ne plus pouvoir utiliser le traitement par lots car mon scénario l'exigeait - il existe des milliers de requêtes UPDATE ou INSERT et toutes ces requêtes. les requêtes se trouvent être différentes.
La correction de l’injection SQL était obligatoire, sans aucune dégradation des performances, et je ne pense pas que cela soit possible avec PreparedStatement
dans ce scénario.
En outre, lorsque vous utilisez la fonction de traitement par lots intégrée, vous devez vous soucier de la fermeture d’une seule instruction, mais avec cette approche de liste, vous devez fermer une instruction avant de la réutiliser, Réutilisation d’une instruction PreparedStatement