web-dev-qa-db-fra.com

Différence entre Statement et PreparedStatement

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:

  1. Analyser la requête SQL entrante
  2. Compiler la requête SQL
  3. Planifier/optimiser le chemin d'acquisition des données
  4. Exécuter la requête optimisée/acquérir et renvoyer des données

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?"

207
CodeBee..

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 PreparedStatementsetXxx() 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();
    
187
BalusC
  1. 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)

  2. 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.

  3. 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.

  4. Ils protègent contre l'injection SQL en échappant le texte pour toutes les valeurs de paramètre fournies.

  5. 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.

  6. 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.

  7. 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 ()).

  8. En Java, accepte les SQL ARRAY, en tant que type de paramètre via la méthode setArray

  9. 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

  10. 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

  11. 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.

  12. 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.

44
Glen Best

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.

36
duffymo

Les avantages de PreparedStatement par rapport à Statement sont les suivants:

  1. PreparedStatement nous aide à prévenir les attaques par injection SQL car il échappe automatiquement aux caractères spéciaux.
  2. PreparedStatement nous permet d'exécuter des requêtes dynamiques avec des entrées de paramètres.
  3. PreparedStatement fournit différents types de méthodes de définition pour définir les paramètres d'entrée de la requête.
  4. PreparedStatement est plus rapide que Statement. Cela devient plus visible lorsque nous réutilisons PreparedStatement ou que nous utilisons ses méthodes de traitement par lots pour exécuter plusieurs requêtes.
  5. PreparedStatement nous aide à écrire du code orienté objet avec des méthodes de définition, alors qu'avec Statement, nous devons utiliser la concaténation de chaînes pour créer la requête. S'il existe plusieurs paramètres à définir, l'écriture de requête à l'aide de la concaténation de chaînes est très laide et source d'erreurs.

En savoir plus sur le problème d’injection SQL à l’adresse http://www.journaldev.com/2489/jdbc-statement-vs-preparedstatement-sql-injection-example

28
Pankaj

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.

13
mhshams

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.

plus de différences

10
sandeep vanama

Ne peut pas faire CLOB dans une déclaration.

Et: (OraclePreparedStatement) ps

7
orbfish

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

5
ashish geol

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ù.

4
Root
  • C'est plus facile à lire
  • Vous pouvez facilement faire de la chaîne de requête une constante
4
nanda

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.

4
MARA MP

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.

4
Anvesh

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

  1. Efficace pour les exécutions répétées

  2. Il est précompilé, donc c'est plus rapide

3
Bernard

Ne pas confondre: rappelez-vous simplement

  1. L'instruction est utilisée pour les requêtes statiques telles que les DDL, c'est-à-dire que créer, déposer, modifier et préparerStatement est utilisé pour les requêtes dynamiques, c'est-à-dire les requêtes DML.
  2. Dans Statement, la requête n'est pas précompilée, tandis que dans prepareStatement, elle est précompilée, ce qui permet de réduire le temps de préparation.
  3. prepareStatement prend les arguments au moment de la création, tandis que Statement ne prend pas les arguments. Par exemple, si vous souhaitez créer une table et un élément, puis: :: Créer une table (statique) à l'aide de l'élément Statement et Insérer un élément (dynamique) à l'aide de prepareStatement.
1
Roopam

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

0
Sabir Khan