Como executar consultas SQL IN () com JDBCTemplate do Spring efetivamente?

177

Eu queria saber se existe uma maneira mais elegante de fazer consultas IN () com o JDBCTemplate do Spring. Atualmente eu faço algo assim:

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

O que é bastante doloroso, pois se eu tiver nove linhas apenas para criar a cláusula para a consulta IN (). Eu gostaria de ter algo como a substituição de parâmetro de instruções preparadas

Malax
fonte

Respostas:

275

Você deseja uma fonte 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());

Isso funciona apenas se getJdbcTemplate()retornar uma instância do tipoNamedParameterJdbcTemplate

bocejar
fonte
5
Perfeito, o NamedParameterJdbcTemplate era exatamente o que eu estava procurando. Além disso, eu gosto de parâmetros nomeados mais do que esses pontos de interrogação em todo o lugar. Muito obrigado!
Malax 25/08
5
Isso funciona para listas pequenas, mas a tentativa de usá-lo em uma lista grande resulta em uma consulta na qual: ids é substituída por "?,?,?,?,? ......" e com itens de lista suficientes, ela transborda. Existe uma solução que funcione para grandes listas?
Nsayer
Você provavelmente deve inserir os valores em uma tabela temporária e criar a condição usando WHERE NOT EXISTS (SELECT ...).
bocejo 26/05
6
Para completar a resposta: Referência do Spring 3.1 - Passagem de listas de valores para a cláusula IN . Mas em Reference não se falou nada: é possível passar em qualquer coleção .
Timofey Gorshkov
9
estranho, recebo "código de erro [17004]; tipo de coluna inválido" quando tento isso.
quer
61

Eu faço a consulta "na cláusula" com o jdbc do spring assim:

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);
Mr Lou
fonte
10
Você acabou de postar uma resposta para uma pergunta de quase três anos com a mesma solução que a resposta aceita. Existe alguma boa razão por trás disso? :-)
Malax
16
Esta resposta fornece mais clareza porque ilustra que o NamedParameterJdbcTemplate é necessário para esta API ... então obrigado pela janwen detalhes adicionais
IcedDante
@janwen, Obrigado pela solução !!! Está funcionando bem conforme minha exigência !!
Karthik Amarnath Saakre
19

Se você receber uma exceção para: Tipo de coluna inválido

Por favor use em getNamedParameterJdbcTemplate()vez degetJdbcTemplate()

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

Observe que os dois segundos argumentos são trocados.

Mahmood Omari
fonte
2
Isso não parece ser uma resposta para esta pergunta. Deveria ser um comentário sobre outra resposta?
Dave Schweisguth
2
@DaveSchweisguth Dois anos depois, definitivamente merece ser uma resposta.
precisa saber é o seguinte
2

Consulte aqui

escrever consulta com parâmetro nomeado, use simple ListPreparedStatementSettercom todos os parâmetros em sequência. Basta adicionar o snippet abaixo para converter a consulta no formato tradicional com base nos parâmetros disponíveis,

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
fonte
para mim, esta foi a única resposta que funcionou, pois eu só queria definir alguns espaços reservados
Kapil 24/01
-4

Muitas coisas mudaram desde 2009, mas só consigo encontrar respostas dizendo que você precisa usar o NamedParametersJDBCTemplate.

Para mim, funciona se eu apenas fizer uma

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

usando SimpleJDBCTemplate ou JDBCTemplate

luso
fonte
11
O problema com esta solução é que o conteúdo listeParamsForInClausenão será escapado e o tornará vulnerável à injeção de SQL.
Malax