web-dev-qa-db-fra.com

Quand devrions-nous utiliser un PreparedStatement au lieu d'un Statement?

Je connais les avantages d’utiliser PreparedStatement, qui sont

  • la requête est réécrite et compilée par le serveur de base de données
  • protection contre l'injection SQL

Mais je veux savoir quand on l'utilise au lieu de Statement?

25
Johanna
  1. La requête est réécrite et compilée par le serveur de base de données.

    Si vous n'utilisez pas d'instruction préparée, le serveur de base de données devra analyser et calculer un plan d'exécution pour l'instruction à chaque exécution. Si vous constatez que vous exécutez la même instruction plusieurs fois (avec des paramètres différents), alors il vaut la peine de préparer l’instruction une fois et de la réutiliser. Si vous interrogez la base de données adhoc, cela ne vous apportera probablement que peu d'avantages.

  2. Protégé contre l'injection SQL

    C’est un avantage que vous voulez presque toujours, donc une bonne raison d’utiliser une PreparedStatement à chaque fois. C'est une conséquence de devoir paramétrer la requête mais cela rend sa gestion beaucoup plus sûre. La seule fois où je peux penser que cela ne serait pas utile, c’est si vous autorisiez les requêtes de base de données ad hoc; Vous pouvez simplement utiliser l'objet Statement si vous étiez en train de prototyper l'application et que c'est plus rapide pour vous, ou si la requête ne contient aucun paramètre.

25
Martin Booth

Demandez à Tom opinion :

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 nulle part.

Il parle spécifiquement d'Oracle, mais le même principe s'applique à toute base de données mettant en cache des plans d'exécution.

Des applications de base de données qui adaptent et empêchent les attaques d'injection SQL simultanément? Quel est l'inconvénient?

16
mattjames

Je voudrais tourner ce cycle: dans une application distribuée publiquement, vous devriez généralement toujours utiliser les instructions préparées à moins que vous n'ayez vraiment une raison impérieuse de , et vous devez toujours fournir les paramètres "correctement" à l'instruction préparée , et non en les épissant dans la chaîne de requête.

Pourquoi? Eh bien, essentiellement à cause des raisons que vous avez données (ou au moins la deuxième) ...

9
Neil Coffey

PreparedStatements doit être utilisé très soigneusement dans les clauses WHERE.

Supposons qu'une table est définie comme:

create table t (int o, k varchar(100), v varchar(100))

(par exemple, "o: ID d'objet (clé étrangère), k: clé d'attribut, v: valeur d'attribut").

De plus, il existe un index (non unique) sur v.

create index ixt on t ( v )

Supposons que cette table contienne 200 millions de lignes insérées comme ceci:

for (i = 0; i < 100*1000*1000; i++) {
  insert into t (o,k,v) values (i,'k1','v1');
  insert into t (o,k,v) values (i,'k2', Convert(i, varchar));
}

("Ainsi, chaque objet o a les attributs k1 = v1 et k2 = o")

Ensuite, vous ne devriez pas créer de requêtes telles que:

select o,p,v from t as tx, t as ty where tx.o=ty.o and tx.k=? and tx.v=? and ty.k=? and ty.v=?

("trouver des objets qui ont deux attributs donnés")

Mon expérience avec Oracle et MSSQL est que ces requêtes pourraient avoir besoin plusieurs minutes revenir. Cela est vrai même si aucune ligne ne correspond à la clause where. Cela dépend si le serveur SQL décide de rechercher tx.v ou ty.v en premier.

On devrait mettre les valeurs des colonnes k et v directement dans la déclaration. Je pense que cela est dû au fait que les serveurs SQL prennent en compte les valeurs lors du calcul du plan d'exécution.

Une requête qui ressemble à ceci retourne toujours après des millisecondes:

select o,p,v from t as tx, t as ty where tx.o=ty.o and tx.k='k1' and tx.v='v1' and ty.k='k2' and ty.v='1234'

("Le serveur SQL recherchera toujours d'abord v = '1234', puis v = 'v1'")

Cordialement
Wolfgang

5
Wolfgang

Je préfère utiliser PreparedStatement si je veux exécuter une requête. (Insert, Select, ... etc). Le même rasoson que pour d'autres écrit ci-dessus.

Mais si j'ai besoin d'utiliser paramètres dynamiques j'utilise simplement Statement parce que je peux facilement construire ma chaîne SQL puis l'exécuter. (inmyopinion) Si vous voulez créer une requête dynamique avec prepareStatment, vous aurez un énorme code spaghetti que personne ne comprendra.

1
Farkas Csanád

En plus d'empêcher l'injection SQL, la portabilité du formatage (que vous ne pouvez pas obtenir de Statement), les performances en sont la raison évidente. Cependant, PreparedStatement ne vient pas sans pénalité. Par exemple, il est généralement plus lent que Statement s'il ne s'exécute qu'une seule fois, car il y a une surcharge. L'idée générale est donc que PreparedStatement devrait être utilisé lorsque vous exécutez plusieurs fois la même requête. Cependant, le temps système est très spécifique à l’implémentation du serveur de base de données. Par conséquent, le moment exact pour choisir PreparedStatement sur Statement doit vraiment être basé sur votre expérience/expérience réelle d’un serveur de base de données spécifique.

0
bryantsai

Vous pouvez toujours utiliser PreparedStatement au lieu de Statment (sélectionnez, insérez, mettez à jour, supprimez). Meilleures performances et protection contre l'injection SQL.

Mais ne l'utilisez pas avec une requête dynamique comme une requête avec WHERE variable IN [ hundreds possibilities ]:

  1. C'est contre-productif, vous perdez des performances et de la mémoire car vous mettez en cache à chaque fois une nouvelle requête, et PreparedStatement ne concerne pas uniquement l'injection SQL, c'est une question de performances. Dans ce cas, Statement ne sera pas plus lent.

  2. Votre piscine a une limite PreparedStatment (-1 par défaut, mais vous devez la limiter), et vous atteindrez cette limite! et si vous n'avez aucune limite ou une limite très grande, vous courez un risque de fuite de mémoire et, dans les cas extrêmes, d'erreurs OutofMemory. Donc, si c'est pour votre petit projet personnel utilisé par 3 utilisateurs, ce n'est pas spectaculaire, mais vous ne le souhaitez pas si vous êtes dans une grande entreprise et que votre application est utilisée par des milliers de personnes et des millions de requêtes.

Un peu de lecture. IBM: Considérations relatives à l'utilisation de la mémoire lors de l'utilisation de la mise en cache d'instructions préparées

0
amdev

Instruction : Chaque fois que la requête SQL est en cours d'exécution, cette instruction SQL est envoyée au SGBD où elle est compilée. Donc, cela augmente les charges du serveur et diminue les performances.

connection con=null; 
  String sql="select * from employee where id=5";
Statement st=conn.createStatement();

PreparedStatement : Contrairement à l'instruction, PreparedStatement reçoit une requête SQL en tant que paramètre lors de sa création.

connection con=null; 
String sql="select * from employee where id=?";
PreparedStatement ps=conn.prepareStatement(sql);

Cette instruction SQL est envoyée à la base de données où elle est compilée. Ainsi, dans la préparation de PreparedStatement ne se produit qu’une seule fois, mais dans l’instruction compilée à chaque appel de Instruction.

0
Arindam Kotal