¿Cómo ejecutar consultas SQL IN () con Spring JDBCTemplate efectivamente?

177

Me preguntaba si hay una forma más elegante de hacer consultas IN () con JDBCTemplate de Spring. Actualmente hago algo así:

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());
}

Lo cual es bastante doloroso ya que si tengo nueve líneas solo para construir la cláusula para la consulta IN (). Me gustaría tener algo como la sustitución de parámetros de declaraciones preparadas

Malax
fuente

Respuestas:

275

Desea una fuente de parámetro:

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

Esto solo funciona si getJdbcTemplate()devuelve una instancia de tipoNamedParameterJdbcTemplate

bostezo
fuente
55
Perfecto, el NamedParameterJdbcTemplate era exactamente lo que estaba buscando. Además, me gustan los parámetros con nombre más que esos signos de interrogación en todo el lugar. ¡Muchas gracias!
Malax
55
Esto funciona para listas pequeñas, pero intentar usarlo en una lista grande da como resultado una consulta donde: ids se reemplaza con "?,?,?,?,? ......" y con suficientes elementos de lista se desborda. ¿Existe una solución que funcione para listas grandes?
nsayer
Probablemente debería insertar los valores en una tabla temporal y construir la condición usando WHERE NOT EXISTS (SELECT ...).
bostezo
66
Para completar la respuesta: Spring 3.1 Reference - Pasar listas de valores para la cláusula IN . Pero en Reference no se dijo nada: es posible pasar cualquier Colección .
Timofey Gorshkov
9
extraño, obtengo el "código de error [17004]; Tipo de columna no válido" cuando intento esto.
Trevor
61

Hago la consulta "en cláusula" con spring jdbc así:

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);
Señor lou
fuente
10
Acabas de publicar una respuesta a una pregunta de casi tres años con la misma solución que tenía la respuesta aceptada. ¿Hay alguna buena razón detrás de esto? :-)
Malax
16
Esta respuesta proporciona más claridad porque ilustra que se necesita el NamedParameterJdbcTemplate para esta API ... así que gracias por el detalle adicional janwen
IcedDante
@janwen, ¡Gracias por la solución! ¡Funciona bien según mi requisito!
Karthik Amarnath Saakre
19

Si obtiene una excepción para: Tipo de columna no válido

Por favor, use en getNamedParameterJdbcTemplate()lugar degetJdbcTemplate()

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

Tenga en cuenta que los dos segundos argumentos se intercambian.

Mahmood Omari
fuente
2
Esto no parece ser una respuesta a esta pregunta. ¿Debería ser un comentario sobre otra respuesta?
Dave Schweisguth
2
@DaveSchweisguth Dos años después, definitivamente garantiza ser una respuesta.
dwjohnston
2

Consulte aquí

consulta de escritura con parámetro con nombre, uso simple ListPreparedStatementSettercon todos los parámetros en secuencia. Simplemente agregue el fragmento a continuación para convertir la consulta en forma tradicional según los parámetros 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
fuente
para mí esta fue la única respuesta que funcionó, ya que solo quería establecer algunos marcadores de posición
Kapil
-4

Muchas cosas cambiaron desde 2009, pero solo puedo encontrar respuestas que dicen que necesita usar NamedParametersJDBCTemplate.

Para mí funciona si solo hago un

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

usando SimpleJDBCTemplate o JDBCTemplate

luso
fuente
11
El problema con esta solución es que el contenido listeParamsForInClauseno se escapará y lo hace vulnerable a la inyección de SQL.
Malax