Jak efektywnie wykonywać zapytania IN () SQL za pomocą JDBCTemplate Springa?

177

Zastanawiałem się, czy istnieje bardziej elegancki sposób wykonywania zapytań IN () za pomocą JDBCTemplate Springa. Obecnie robię coś takiego:

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

Co jest dość bolesne, ponieważ mam dziewięć wierszy tylko do zbudowania klauzuli dla zapytania IN (). Chciałbym mieć coś takiego jak podstawianie parametrów przygotowanych wyciągów

Malax
źródło

Odpowiedzi:

275

Chcesz źródło parametrów:

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

Działa to tylko wtedy, gdy getJdbcTemplate()zwraca instancję typuNamedParameterJdbcTemplate

ziewać
źródło
5
Idealnie, NamedParameterJdbcTemplate był dokładnie tym, czego szukałem. Dodatkowo bardziej lubię nazwane parametry niż te znaki zapytania w każdym miejscu. Wielkie dzięki!
Malax
5
Działa to w przypadku małych list, ale próba użycia go na dużej liście skutkuje zapytaniem, w którym: ids jest zastępowane przez „?,?,?,?,? ......”, a przy wystarczającej liczbie elementów listy następuje przepełnienie. Czy istnieje rozwiązanie, które działa w przypadku dużych list?
nsayer
Prawdopodobnie powinieneś wstawić wartości do tymczasowej tabeli i zbudować warunek za pomocą WHERE NOT EXISTS (SELECT ...).
ziewać
6
Aby uzupełnić odpowiedź: Spring 3.1 Reference - Przekazywanie list wartości dla klauzuli IN . Ale w Referencji nic nie zostało powiedziane: można przekazać dowolną Kolekcję .
Timofey Gorshkov
9
dziwne, pojawia się komunikat „kod błędu [17004]; nieprawidłowy typ kolumny”, gdy próbuję to zrobić.
Trevor
61

Wykonuję zapytanie „in clause” z spring jdbc w ten sposób:

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);
Panie Lou
źródło
10
Właśnie opublikowałeś odpowiedź na prawie trzyletnie pytanie z takim samym rozwiązaniem, jakie miała zaakceptowana odpowiedź. Czy jest jakiś dobry powód? :-)
Malax
16
Ta odpowiedź zapewnia większą jasność, ponieważ ilustruje, że NamedParameterJdbcTemplate jest potrzebny dla tego interfejsu API ... więc dzięki za dodatkowe szczegóły janwen
IcedDante
@janwen, Dzięki za rozwiązanie !!! Działa dobrze, jak na moje wymagania !!
Karthik Amarnath Saakre
19

Jeśli otrzymasz wyjątek dla: Nieprawidłowy typ kolumny

Użyj getNamedParameterJdbcTemplate()zamiastgetJdbcTemplate()

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

Zwróć uwagę, że dwa drugie argumenty są zamienione miejscami.

Mahmood Omari
źródło
2
To nie wydaje się być odpowiedzią na to pytanie. Czy powinien to być komentarz do innej odpowiedzi?
Dave Schweisguth
2
@DaveSchweisguth Dwa lata później zdecydowanie zasługuje na odpowiedź.
dwjohnston
2

Zobacz tutaj

napisz zapytanie z nazwanym parametrem, użyj prostego ListPreparedStatementSetterze wszystkimi parametrami w kolejności. Wystarczy dodać poniższy fragment, aby przekonwertować zapytanie w formie tradycyjnej na podstawie dostępnych parametrów,

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
źródło
dla mnie była to jedyna odpowiedź, która zadziałała, ponieważ chciałem ustawić kilka symboli zastępczych
Kapil
-4

Wiele rzeczy zmieniło się od 2009 roku, ale mogę znaleźć tylko odpowiedzi mówiące, że musisz użyć NamedParametersJDBCTemplate.

U mnie działa, jeśli po prostu zrobię

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

przy użyciu SimpleJDBCTemplate lub JDBCTemplate

luso
źródło
11
Problem z tym rozwiązaniem polega na tym, że zawartość listeParamsForInClausenie zostanie przedostana i naraża Cię na wstrzyknięcie SQL.
Malax