Comment exécuter efficacement des requêtes SQL IN () avec le JDBCTemplate de Spring?


180

Je me demandais s'il existe un moyen plus élégant de faire des requêtes IN () avec le JDBCTemplate de Spring. Actuellement, je fais quelque chose comme ça:

StringBuilder jobTypeInClauseBuilder = new StringBuilder();
for(int i = 0; i < jobTypes.length; i++) {
    Type jobType = jobTypes[i];

    if(i != 0) {
        jobTypeInClauseBuilder.append(',');
    }

    jobTypeInClauseBuilder.append(jobType.convert());
}

Ce qui est assez pénible puisque si j'ai neuf lignes juste pour construire la clause pour la requête IN (). Je voudrais avoir quelque chose comme la substitution de paramètres des instructions préparées

Réponses:


277

Vous voulez une source de paramètres:

Set<Integer> ids = ...;

MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("ids", ids);

List<Foo> foo = getJdbcTemplate().query("SELECT * FROM foo WHERE a IN (:ids)",
     parameters, getRowMapper());

Cela ne fonctionne que si getJdbcTemplate()renvoie une instance de typeNamedParameterJdbcTemplate


7
Parfait, le NamedParameterJdbcTemplate était exactement ce que je recherchais. De plus, j'aime plus les paramètres nommés que ces points d'interrogation partout. Merci beaucoup!
Malax

5
Cela fonctionne pour les petites listes, mais essayer de l'utiliser sur une grande liste entraîne une requête où: ids est remplacé par "?,?,?,?,? ......" et avec suffisamment d'éléments de liste, cela déborde. Existe-t-il une solution qui fonctionne pour les grandes listes?
nsayer

Vous devriez probablement insérer les valeurs dans une table temporaire et créer la condition en utilisant WHERE NOT EXISTS (SELECT ...).
bâillement


10
étrange, j'obtiens "code d'erreur [17004]; type de colonne non valide" lorsque j'essaye ceci.
Trevor

62

Je fais la requête "in clause" avec spring jdbc comme ceci:

String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid IN (:goodsid)";

List ids = Arrays.asList(new Integer[]{12496,12497,12498,12499});
Map<String, List> paramMap = Collections.singletonMap("goodsid", ids);
NamedParameterJdbcTemplate template = 
    new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource());

List<Long> list = template.queryForList(sql, paramMap, Long.class);

10
Vous venez de publier une réponse à une question vieille de presque trois ans avec la même solution que la réponse acceptée. Y a-t-il une bonne raison derrière cela? :-)
Malax

16
Cette réponse apporte plus de clarté car elle illustre que le NamedParameterJdbcTemplate est nécessaire pour cette API ... alors merci pour le détail supplémentaire janwen
IcedDante

@janwen, merci pour la solution !!! Cela fonctionne bien selon mon exigence !!
Karthik SA

19

Si vous obtenez une exception pour: Type de colonne non valide

Veuillez utiliser à la getNamedParameterJdbcTemplate()place degetJdbcTemplate()

 List<Foo> foo = getNamedParameterJdbcTemplate().query("SELECT * FROM foo WHERE a IN (:ids)",parameters,
 getRowMapper());

Notez que les deux seconds arguments sont permutés.


2
Cela ne semble pas être une réponse à cette question. Devrait-il s'agir d'un commentaire sur une autre réponse?
Dave Schweisguth

2
@DaveSchweisguth Deux ans plus tard, cela justifie définitivement d'être une réponse.
dwjohnston

2

Reportez-vous ici

écrire une requête avec un paramètre nommé, utiliser simple ListPreparedStatementSetteravec tous les paramètres dans l'ordre. Ajoutez simplement l'extrait ci-dessous pour convertir la requête sous forme traditionnelle en fonction des paramètres disponibles,

ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(namedSql);

List<Integer> parameters = new ArrayList<Integer>();
for (A a : paramBeans)
    parameters.add(a.getId());

MapSqlParameterSource parameterSource = new MapSqlParameterSource();
parameterSource.addValue("placeholder1", parameters);
// create SQL with ?'s
String sql = NamedParameterUtils.substituteNamedParameters(parsedSql, parameterSource);     
return sql;

pour moi, c'était la seule réponse qui a fonctionné car je voulais juste définir quelques espaces réservés
Kapil

-4

Beaucoup de choses ont changé depuis 2009, mais je ne trouve que des réponses indiquant que vous devez utiliser NamedParametersJDBCTemplate.

Pour moi, ça marche si je fais juste un

db.query(sql, new MyRowMapper(), StringUtils.join(listeParamsForInClause, ","));

en utilisant SimpleJDBCTemplate ou JDBCTemplate


12
Le problème avec cette solution est que le contenu listeParamsForInClausene sera pas échappé et vous rend vulnérable à l'injection SQL.
Malax
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.