Prepare statement IN clause alternatives?

quelles sont les meilleures solutions pour utiliser une clause SQL IN avec des instances de java.sql.PreparedStatement , qui n'est pas prise en charge pour des valeurs multiples en raison de problèmes de sécurité D'attaque D'injection SQL: un placeholder ? représente une valeur, plutôt qu'une liste de valeurs.

considérez L'énoncé SQL suivant:

SELECT my_column FROM my_table where search_column IN (?)

L'utilisation de preparedStatement.setString( 1, "'A', 'B', 'C'" ); est essentiellement une tentative de non-travail pour contourner les raisons de l'utilisation ? en premier lieu.

quelles solutions de rechange sont disponibles?

302
demandé sur Jonas 2008-10-07 17:41:36

27 réponses

Une analyse des différentes options disponibles, et les avantages et les inconvénients de chaque type est disponible ici .

les options suggérées sont:

  • préparer SELECT my_column FROM my_table WHERE search_column = ? , l'Exécuter pour chaque valeur et L'UNION des résultats côté client. N'exige qu'une seule déclaration préparée. Lente et douloureuse.
  • préparer SELECT my_column FROM my_table WHERE search_column IN (?,?,?) et l'exécuter. Exige une déclaration préparée par taille de la liste. Rapide et évidente.
  • préparer SELECT my_column FROM my_table WHERE search_column = ? ; SELECT my_column FROM my_table WHERE search_column = ? ; ... et l'exécuter. [Ou utiliser UNION ALL à la place de ces points-virgule. -- ed] exige une déclaration préparée par taille de la liste. Stupidement lent , strictement pire que WHERE search_column IN (?,?,?) , donc je ne sais pas pourquoi le blogueur l'a suggéré.
  • utiliser une procédure stockée pour construire le jeu de résultats.
  • Préparer la N de taille différente-de-DANS-la liste des requêtes; dire, avec 2, 10 et 50 valeurs. À la recherche d'un Dans la liste avec 6 valeurs différentes, peuplez la requête size-10 pour qu'elle ressemble à SELECT my_column FROM my_table WHERE search_column IN (1,2,3,4,5,6,6,6,6,6) . N'importe quel serveur décent optimisera les valeurs dupliquées avant d'exécuter la requête.

aucune de ces options n'est super grande, cependant.

Dupliquer les questions ont été répondues dans ces lieux, avec d'aussi sain d'esprit de solutions de rechange, toujours aucun d'eux super grand:

la bonne réponse, si vous utilisez JDBC4 et un serveur qui supporte x = ANY(y) , est d'utiliser PreparedStatement.setArray comme décrit ici:

il ne semble pas aucun moyen de les faire setArray travailler avec les listes.

168
répondu Dónal 2017-05-23 12:10:30

Solution pour PostgreSQL:

final PreparedStatement statement = connection.prepareStatement(
        "SELECT my_column FROM my_table where search_column = ANY (?)"
);
final String[] values = getValues();
statement.setArray(1, connection.createArrayOf("text", values));
final ResultSet rs = statement.executeQuery();
try {
    while(rs.next()) {
        // do some...
    }
} finally {
    rs.close();
}

ou

final PreparedStatement statement = connection.prepareStatement(
        "SELECT my_column FROM my_table " + 
        "where search_column IN (SELECT * FROM unnest(?))"
);
final String[] values = getValues();
statement.setArray(1, connection.createArrayOf("text", values));
final ResultSet rs = statement.executeQuery();
try {
    while(rs.next()) {
        // do some...
    }
} finally {
    rs.close();
}
107
répondu Boris 2012-04-20 04:47:57

Pas de moyen simple autant que je sache. Si la cible est de garder cache d'instruction ratio élevé (j'.e de ne pas créer une déclaration pour chaque paramètre comte de), vous pouvez faire ce qui suit:

  1. créer une déclaration avec quelques (Par exemple 10) paramètres:

    ... OÙ (?,?,?,?,?,?,?,?,?,?) ...

  2. lier tous les paramètres actuels

    setString(1,"toto"); setString (2, " bar");

  3. lient le reste comme nul

    setNull (3, Types.VARCHAR) ... setNull (10, Types.VARCHAR)

NULL ne correspond jamais à quoi que ce soit, il est donc optimisé par le constructeur du plan SQL.

la logique est facile à automatiser lorsque vous passez une liste dans une fonction DAO:

while( i < param.size() ) {
  ps.setString(i+1,param.get(i));
  i++;
}

while( i < MAX_PARAMS ) {
  ps.setNull(i+1,Types.VARCHAR);
  i++;
}
18
répondu Vladimir Dyuzhev 2008-10-09 21:52:49

Un travail désagréable, mais certainement possible est d'utiliser une requête imbriquée. Créez une table temporaire MYVALUES avec une colonne dedans. Insérez votre liste de valeurs dans la table MYVALUES. Alors exécutez

select my_column from my_table where search_column in ( SELECT value FROM MYVALUES )

Laid, mais une alternative viable si votre liste de valeurs est très grand.

cette technique a l'avantage supplémentaire de potentiellement de meilleurs plans de requête de l'optimiseur (vérifier une page pour des valeurs multiples, tablescan une seule fois au lieu de cela, une fois par valeur, etc) peut sauvegarder sur overhead si votre base de données ne cache pas les déclarations préparées. Vos "INSERTS" devront être faits en lot et la table MYVALUES devra peut-être être ajustée pour avoir un verrouillage minimal ou d'autres protections surélevées.

10
répondu James Schek 2008-10-09 21:39:33

Limitations de l'opérateur in () est la racine de tout mal.

il fonctionne pour les cas triviaux, et vous pouvez l'étendre avec" génération automatique de l'énoncé préparé " cependant il a toujours ses limites.

  • si vous créez une déclaration avec un nombre variable de paramètres, qui fera une analyse sql au-dessus de chaque appel
  • sur de nombreuses plateformes, le nombre de paramètres de l'opérateur in() est limité
  • sur toutes les plateformes, la taille totale du texte SQL est limitée, ce qui rend impossible l'envoi de 2000 espaces réservés pour les in params
  • envoyer vers le bas des variables de liaison de 1000-10k n'est pas possible, car le pilote JDBC a ses limites

L'() approche peut être assez bon pour certains cas, mais pas de fusée, la preuve :)

la solution anti-fusée est de passer le nombre arbitraire de paramètres dans un séparez l'appel (en passant un sabot de params, par exemple), et puis ayez une vue (ou de toute autre façon) pour les représenter en SQL et utiliser dans vos critères où.

une variante de la force brute est ici http://tkyte.blogspot.hu/2006/06/varying-in-lists.html

cependant si vous pouvez utiliser PL / SQL, ce désordre peut devenir assez soigné.

function getCustomers(in_customerIdList clob) return sys_refcursor is 
begin
    aux_in_list.parse(in_customerIdList);
    open res for
        select * 
        from   customer c,
               in_list v
        where  c.customer_id=v.token;
    return res;
end;

alors vous pouvez passer le nombre arbitraire de noms de clients séparés par des virgules dans le paramètre, et:

  • n'aura pas de délai d'analyse, car le SQL pour select est stable
  • pas de pipeline fonctions de la complexité - c'est juste une requête
  • le SQL utilise une jointure simple, au lieu d'un opérateur IN, qui est assez rapide
  • après tout, il est une bonne règle de pouce de pas frapper la base de données avec tout simple sélectionner ou DML, car il est Oracle, qui offre années-lumière de plus de MySQL ou des moteurs de base de données simples similaires. PL / SQL vous permet de cacher le modèle de stockage de votre application domain model d'une manière efficace.

L'astuce ici est:

  • nous avons besoin d'un appel qui accepte la longue chaîne, et stocke quelque part où la session db peut y accéder (par exemple simple package variable, ou dbms_session.set_context)
  • alors nous avons besoin d'une vue qui peut analyser ce à lignes
  • et puis vous avez une vue qui contient les ID que vous posez, donc tout ce que vous avez besoin est une simple jointure à la table interrogée.

Le point de vue ressemble à:

create or replace view in_list
as
select
    trim( substr (txt,
          instr (txt, ',', 1, level  ) + 1,
          instr (txt, ',', 1, level+1)
             - instr (txt, ',', 1, level) -1 ) ) as token
    from (select ','||aux_in_list.getpayload||',' txt from dual)
connect by level <= length(aux_in_list.getpayload)-length(replace(aux_in_list.getpayload,',',''))+1

où aux_in_list.getpayload se réfère à la chaîne d'entrée originale.


une approche possible serait de passer des tableaux pl/sql (supportés par Oracle seulement), cependant vous ne pouvez pas utiliser ceux en SQL pur, une étape de conversion est donc toujours nécessaire. La conversion ne peut pas être faite en SQL, donc après tout, passer un sabot avec tous les paramètres dans la chaîne et le convertir dans une vue est la solution la plus efficace.

8
répondu Gee Bee 2016-02-17 14:44:18

Je n'ai jamais essayé, mais je le ferais .setArray () faire ce que vous cherchez?

mise à Jour : à l'Évidence pas. setArray ne semble fonctionner qu'avec un java.SQL.Tableau qui vient d'une colonne de tableau que vous avez récupéré à partir d'une requête précédente, ou un sous-jeu avec une colonne de tableau.

5
répondu Paul Tomblin 2008-10-09 17:45:41

ma solution est:

create or replace type split_tbl as table of varchar(32767);
/

create or replace function split
(
  p_list varchar2,
  p_del varchar2 := ','
) return split_tbl pipelined
is
  l_idx    pls_integer;
  l_list    varchar2(32767) := p_list;
  l_value    varchar2(32767);
begin
  loop
    l_idx := instr(l_list,p_del);
    if l_idx > 0 then
      pipe row(substr(l_list,1,l_idx-1));
      l_list := substr(l_list,l_idx+length(p_del));
    else
      pipe row(l_list);
      exit;
    end if;
  end loop;
  return;
end split;
/

Maintenant vous pouvez utiliser une variable pour obtenir des valeurs dans une table:

select * from table(split('one,two,three'))
  one
  two
  three

select * from TABLE1 where COL1 in (select * from table(split('value1,value2')))
  value1 AAA
  value2 BBB

ainsi, la déclaration préparée pourrait être:

  "select * from TABLE where COL in (select * from table(split(?)))"

Cordialement,

Javier Ibanez

5
répondu Javier Ibanez 2011-02-24 12:44:47

Voici comment je l'ai résolu dans ma propre application. Idéalement, vous devriez utiliser un StringBuilder au lieu d'utiliser + pour les Chaînes.

    String inParenthesis = "(?";
    for(int i = 1;i < myList.size();i++) {
      inParenthesis += ", ?";
    }
    inParenthesis += ")";

    try(PreparedStatement statement = SQLite.connection.prepareStatement(
        String.format("UPDATE table SET value='WINNER' WHERE startTime=? AND name=? AND traderIdx=? AND someValue IN %s", inParenthesis))) {
      int x = 1;
      statement.setLong(x++, race.startTime);
      statement.setString(x++, race.name);
      statement.setInt(x++, traderIdx);

      for(String str : race.betFair.winners) {
        statement.setString(x++, str);
      }

      int effected = statement.executeUpdate();
    }

utiliser une variable comme x ci-dessus au lieu de nombres concrets aide beaucoup si vous décidez de changer la requête à un moment ultérieur.

5
répondu m.sabouri 2016-04-08 05:06:57

je suppose que vous pourriez (en utilisant la manipulation de chaîne de base) générer la chaîne de requête dans le PreparedStatement pour avoir un nombre de ? 's correspondant au nombre d'articles dans votre liste.

bien sûr, si vous faites cela, vous n'êtes qu'à un pas de générer une chaîne géante OR dans votre requête, mais sans avoir le bon numéro de ? dans la chaîne de requête, Je ne vois pas comment vous pouvez faire autrement.

3
répondu Adam Bellaire 2008-10-07 13:47:36

vous pouvez utiliser la méthode setArray comme mentionné dans ce javadoc :

PreparedStatement statement = connection.prepareStatement("Select * from emp where field in (?)");
Array array = statement.getConnection().createArrayOf("VARCHAR", new Object[]{"E1", "E2","E3"});
statement.setArray(1, array);
ResultSet rs = statement.executeQuery();
2
répondu Panky031 2016-06-09 20:12:44

essayez d'utiliser la fonction instr?

select my_column from my_table where  instr(?, ','||search_column||',') > 0

puis

ps.setString(1, ",A,B,C,"); 

certes, il s'agit d'un peu d'un piratage sale, mais il ne réduit les possibilités pour l'injection sql. Travaille dans oracle, de toute façon.

1
répondu stjohnroe 2008-10-07 14:13:09

Sormula prend en charge SQL in operator en vous permettant de fournir un java.util.Objet Collection comme paramètre. Il crée une déclaration préparée avec un ? pour chacun des éléments de la collection. Voir exemple 4 (SQL dans l'exemple est un commentaire pour clarifier ce qui est créé mais n'est pas utilisé par Sormula).

1
répondu Jeff Miller 2011-11-21 19:30:54

au lieu d'utiliser

SELECT my_column FROM my_table where search_column IN (?)

utilisez la déclaration Sql comme

select id, name from users where id in (?, ?, ?)

et

preparedStatement.setString( 1, 'A');
preparedStatement.setString( 2,'B');
preparedStatement.setString( 3, 'C');

ou utiliser une procédure stockée ce serait la meilleure solution, puisque les déclarations sql seront compilées et stockées dans le serveur de base de données

1
répondu kapil das 2013-08-24 19:29:46

j'ai rencontré un certain nombre de limitations liées à la déclaration préparée:

  1. les déclarations préparées ne sont mises en cache qu'à L'intérieur de la même session (Postgres), de sorte qu'elles ne fonctionneront réellement qu'avec la mise en commun des connexions
  2. un grand nombre de différentes déclarations préparées comme proposées par @BalusC peuvent causer un débordement de la cache et les déclarations déjà mises en cache seront abandonnées
  3. la requête doit être optimisée et utiliser des indices. Semble évident, cependant par exemple le N'importe quel (tableau...) déclaration proposée par @Boris dans l'une des meilleures réponses ne peut pas utiliser les indices et la requête sera lente malgré la mise en cache
  4. l'énoncé préparé cache également le plan de requête et les valeurs réelles de tout paramètre spécifié dans l'énoncé ne sont pas disponibles.

parmi les solutions proposées, je choisirais celle qui ne diminue pas la performance de la requête et fait le moins de requêtes. Ce sera le #4 (grouper quelques requêtes) à partir du lien @Don ou spécifier des valeurs nulles pour inutile?'marques proposées par @ Vladimir Dyuzhev

1
répondu Alexander 2013-09-12 07:21:19

Voici une solution complète en Java pour créer la déclaration préparée pour vous:

/*usage:

Util u = new Util(500); //500 items per bracket. 
String sqlBefore  = "select * from myTable where (";
List<Integer> values = new ArrayList<Integer>(Arrays.asList(1,2,4,5)); 
string sqlAfter = ") and foo = 'bar'"; 

PreparedStatement ps = u.prepareStatements(sqlBefore, values, sqlAfter, connection, "someId");
*/



import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class Util {

    private int numValuesInClause;

    public Util(int numValuesInClause) {
        super();
        this.numValuesInClause = numValuesInClause;
    }

    public int getNumValuesInClause() {
        return numValuesInClause;
    }

    public void setNumValuesInClause(int numValuesInClause) {
        this.numValuesInClause = numValuesInClause;
    }

    /** Split a given list into a list of lists for the given size of numValuesInClause*/
    public List<List<Integer>> splitList(
            List<Integer> values) {


        List<List<Integer>> newList = new ArrayList<List<Integer>>(); 
        while (values.size() > numValuesInClause) {
            List<Integer> sublist = values.subList(0,numValuesInClause);
            List<Integer> values2 = values.subList(numValuesInClause, values.size());   
            values = values2; 

            newList.add( sublist);
        }
        newList.add(values);

        return newList;
    }

    /**
     * Generates a series of split out in clause statements. 
     * @param sqlBefore ""select * from dual where ("
     * @param values [1,2,3,4,5,6,7,8,9,10]
     * @param "sqlAfter ) and id = 5"
     * @return "select * from dual where (id in (1,2,3) or id in (4,5,6) or id in (7,8,9) or id in (10)"
     */
    public String genInClauseSql(String sqlBefore, List<Integer> values,
            String sqlAfter, String identifier) 
    {
        List<List<Integer>> newLists = splitList(values);
        String stmt = sqlBefore;

        /* now generate the in clause for each list */
        int j = 0; /* keep track of list:newLists index */
        for (List<Integer> list : newLists) {
            stmt = stmt + identifier +" in (";
            StringBuilder innerBuilder = new StringBuilder();

            for (int i = 0; i < list.size(); i++) {
                innerBuilder.append("?,");
            }



            String inClause = innerBuilder.deleteCharAt(
                    innerBuilder.length() - 1).toString();

            stmt = stmt + inClause;
            stmt = stmt + ")";


            if (++j < newLists.size()) {
                stmt = stmt + " OR ";
            }

        }

        stmt = stmt + sqlAfter;
        return stmt;
    }

    /**
     * Method to convert your SQL and a list of ID into a safe prepared
     * statements
     * 
     * @throws SQLException
     */
    public PreparedStatement prepareStatements(String sqlBefore,
            ArrayList<Integer> values, String sqlAfter, Connection c, String identifier)
            throws SQLException {

        /* First split our potentially big list into lots of lists */
        String stmt = genInClauseSql(sqlBefore, values, sqlAfter, identifier);
        PreparedStatement ps = c.prepareStatement(stmt);

        int i = 1;
        for (int val : values)
        {

            ps.setInt(i++, val);

        }
        return ps;

    }

}
1
répondu dwjohnston 2015-04-20 09:33:55

Printemps permet de en passant java.util.Listes à Namedparameterjdbcctemplate , qui automatise la génération de (?, ?, ?, ..., ?), selon le nombre d'arguments.

Pour Oracle, ce blogue traite de l'utilisation d'oracle.SQL.TABLEAU (la Connexion.createArrayOf ne fonctionne pas avec Oracle). Pour cela, vous devez modifier votre instruction SQL:

SELECT my_column FROM my_table where search_column IN (select COLUMN_VALUE from table(?))

Le oracle la fonction de table transforme le tableau passé en une valeur de type table utilisable dans la déclaration IN .

1
répondu Hans-Peter Störr 2015-06-01 12:18:02

juste pour l'exhaustivité: tant que l'ensemble des valeurs n'est pas trop grand, vous pourrait aussi simplement chaîne de caractères-construire une déclaration comme

... WHERE tab.col = ? OR tab.col = ? OR tab.col = ?

que vous pouvez ensuite passer à prepare(), puis utiliser setXXX() dans une boucle pour définir toutes les valeurs. Cela semble fâcheux, mais beaucoup de" grands " systèmes commerciaux font régulièrement ce genre de chose jusqu'à ce qu'ils atteignent des limites spécifiques de DB, comme 32 ko (je pense que c'est) pour les déclarations dans Oracle.

bien sûr, vous devez vous assurer que l'ensemble ne sera jamais déraisonnablement grand, ou faire le piégeage d'erreur dans le cas où il est.

0
répondu Carl Smotricz 2008-10-07 14:15:46

suivant L'idée D'Adam. Faites votre déclaration préparée sorte de sélectionner my_column à partir de my_table où search_column dans (#) Créer une chaîne x et la remplir avec un nombre de "?,?,?"en fonction de votre liste de valeurs Puis il suffit de changer le # dans la requête pour votre nouvelle chaîne X une populate

0
répondu 2008-10-07 15:49:34

génère la chaîne de requête dans L'État de préparation pour avoir un certain nombre de ?'s correspondant au nombre d'articles dans votre liste. Voici un exemple:

public void myQuery(List<String> items, int other) {
  ...
  String q4in = generateQsForIn(items.size());
  String sql = "select * from stuff where foo in ( " + q4in + " ) and bar = ?";
  PreparedStatement ps = connection.prepareStatement(sql);
  int i = 1;
  for (String item : items) {
    ps.setString(i++, item);
  }
  ps.setInt(i++, other);
  ResultSet rs = ps.executeQuery();
  ...
}

private String generateQsForIn(int numQs) {
    String items = "";
    for (int i = 0; i < numQs; i++) {
        if (i != 0) items += ", ";
        items += "?";
    }
    return items;
}
0
répondu neu242 2012-06-07 08:55:02

il existe différentes approches alternatives que nous pouvons utiliser dans la clause dans L'État de préparation.

  1. à l'Aide de Simples Requêtes - le plus lent de la performance et de ressources
  2. utilisant la procédure de stockage-plus rapide mais spécifique à la base de données
  3. créer une requête dynamique pour PreparedStatement-bonne Performance mais ne bénéficie pas de la mise en cache et PreparedStatement est recompilé à chaque fois.
  4. Utiliser la valeur NULL dans PreparedStatement requêtes - des performances Optimales, fonctionne très bien quand vous savez que la limite de la clause d'arguments. S'il n'y a pas de limite, alors vous pouvez exécuter des requêtes par lots. Exemple extrait de code;

        int i = 1;
        for(; i <=ids.length; i++){
            ps.setInt(i, ids[i-1]);
        }
    
        //set null for remaining ones
        for(; i<=PARAM_SIZE;i++){
            ps.setNull(i, java.sql.Types.INTEGER);
        }
    

vous pouvez vérifier plus de détails sur ces approches alternatives ici .

0
répondu Pankaj 2014-01-26 09:32:03

pour certaines situations regexp pourrait aider. Voici un exemple que j'ai vérifié sur Oracle, et il fonctionne.

select * from my_table where REGEXP_LIKE (search_column, 'value1|value2')

mais il ya un certain nombre d'inconvénients avec elle:

  1. toute colonne qu'il applique doit être convertie en varchar/char, au moins implicitement.
  2. doit être prudent avec les caractères spéciaux.
  3. il peut ralentir la performance - dans mon cas dans la version utilise l'index et le balayage de gamme, et Version REGEXP faire une numérisation complète.
0
répondu Vasili 2015-01-02 06:23:42

après avoir examiné diverses solutions dans différents forums et de ne pas trouver une bonne solution, je pense que le hack ci-dessous, je suis venu avec, est le plus facile à suivre et le code:

exemple: supposons que vous ayez plusieurs paramètres à passer dans la clause' IN'. Il suffit de mettre une ficelle à l'intérieur de la clause 'IN', par exemple, "PARAM" indique la liste des paramètres qui viendront à la place de cette ficelle.

    select * from TABLE_A where ATTR IN (PARAM);

vous pouvez collecter tous les paramètres dans une seule variable de chaîne de caractères dans votre code Java. Cela peut se faire comme suit:

    String param1 = "X";
    String param2 = "Y";
    String param1 = param1.append(",").append(param2);

vous pouvez ajouter tous vos paramètres séparés par des virgules dans une seule variable de chaîne, 'param1', dans notre cas.

après avoir rassemblé tous les paramètres dans une seule chaîne, vous pouvez simplement remplacer le texte fictif de votre requête, i.e.," PARAM " dans ce cas, avec la chaîne de paramètres, i.e., param1. Voici ce que vous devez faire:

    String query = query.replaceFirst("PARAM",param1); where we have the value of query as 

    query = "select * from TABLE_A where ATTR IN (PARAM)";

vous pouvez maintenant exécuter votre requête en utilisant la méthode executeQuery (). Assurez-vous juste que vous n'avez pas le mot "PARAM" dans votre requête n'importe où. Vous pouvez utiliser une combinaison de caractères spéciaux et des alphabets au lieu du mot "PARAM" afin de s'assurer qu'il n'y a aucune possibilité d'un tel mot venant dans la requête. J'espère que vous avez la solution.

Note: Bien que ce ne soit pas une requête préparée, il fait le travail que je voulais que mon code fasse.

0
répondu bnsk 2015-03-15 15:01:50

juste pour être complet et parce que je n'ai vu personne d'autre le suggérer:

avant de mettre en œuvre l'une des suggestions compliquées ci-dessus, considérez si L'injection SQL est effectivement un problème dans votre scénario.

dans de nombreux cas, la valeur prévue à l'alinéa (...) est une liste d'identifiants qui ont été générés dans une manière que vous pouvez être sûr que personne d'injection est possible... (par exemple, les résultats d'un précédent select some_id de some_table où some_condition.)

Si c'est le cas, vous pouvez simplement concaténer cette valeur et ne pas utiliser les services ou de l'instruction préparée pour elle ou les utiliser pour d'autres paramètres de cette requête.

query="select f1,f2 from t1 where f3=? and f2 in (" + sListOfIds + ");";
0
répondu epeleg 2015-04-01 18:12:37

prepare Statement ne fournit pas de bonne façon de traiter avec SQL dans la clause. Par http://www.javaranch.com/journal/200510/Journal200510.jsp#a2 " vous ne pouvez pas substituer des choses qui sont destinées à faire partie de la déclaration SQL. Cela est nécessaire car si le SQL lui-même peut changer, le pilote ne peut pas précompiler la déclaration. Il a également l'effet secondaire agréable de prévenir les attaques par injection SQL."J'ai fini par utiliser l'approche suivante:

String query = "SELECT my_column FROM my_table where search_column IN ($searchColumns)";
query = query.replace("$searchColumns", "'A', 'B', 'C'");
Statement stmt = connection.createStatement();
boolean hasResults = stmt.execute(query);
do {
    if (hasResults)
        return stmt.getResultSet();

    hasResults = stmt.getMoreResults();

} while (hasResults || stmt.getUpdateCount() != -1);
0
répondu pedram bashiri 2017-06-12 19:26:38

SetArray est la meilleure solution, mais elle n'est pas disponible pour de nombreux pilotes plus âgés. La solution de contournement suivante peut être utilisée dans java8

String baseQuery ="SELECT my_column FROM my_table where search_column IN (%s)"

String markersString = inputArray.stream().map(e -> "?").collect(joining(","));
String sqlQuery = String.format(baseSQL, markersString);

//Now create Prepared Statement and use loop to Set entries
int index=1;

for (String input : inputArray) {
     preparedStatement.setString(index++, input);
}

cette solution est meilleure que d'autres solutions de boucle tout laid où la chaîne de requête est construite par itérations manuelles

0
répondu Raheel 2018-03-01 08:29:19

vous pouvez utiliser Collections.nCopies pour générer une collection de placeholders et les joindre en utilisant String.join :

List<String> params = getParams();
String placeHolders = String.join(",", Collections.nCopies(params.size(), "?"));
String sql = "select * from your_table where some_column in (" + placeHolders + ")";
try (   Connection connection = getConnection();
        PreparedStatement ps = connection.prepareStatement(sql)) {
    int i = 1;
    for (String param : params) {
        ps.setString(i++, param);
    }
    /*
     * Execute query/do stuff
     */
}
0
répondu Gurwinder Singh 2018-04-29 09:33:40

ma solution (JavaScript)

    var s1 = " SELECT "

 + "FROM   table t "

 + "  where t.field in ";

  var s3 = '(';

  for(var i =0;i<searchTerms.length;i++)
  {
    if(i+1 == searchTerms.length)
    {
     s3  = s3+'?)';
    }
    else
    {
        s3  = s3+'?, ' ;
    }
   }
    var query = s1+s3;

    var pstmt = connection.prepareStatement(query);

     for(var i =0;i<searchTerms.length;i++)
    {
        pstmt.setString(i+1, searchTerms[i]);
    }

SearchTerms est le tableau qui contient vos entrées / clés / champs etc

-1
répondu smooth_smoothie 2017-02-01 19:28:25