¿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

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


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


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

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.


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;

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


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.