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

Malax
la source

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

bâillement
la source
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
6
Pour compléter la réponse: Spring 3.1 Reference - Passer des listes de valeurs pour la clause IN . Mais dans Reference, rien n'a été dit: il est possible de passer n'importe quelle collection .
Timofey Gorshkov
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);
Monsieur Lou
la source
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.

Mahmood Omari
la source
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;
Abhishek Chatterjee
la source
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

luso
la source
12
Le problème avec cette solution est que le contenu listeParamsForInClausene sera pas échappé et vous rend vulnérable à l'injection SQL.
Malax