Як ефективно виконувати запити IN () SQL за допомогою JDBCTemplate Spring?


177

Мені було цікаво, чи є більш елегантний спосіб робити запити IN () за допомогою JDBCTemplate Spring. В даний час я роблю щось подібне:

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

Що досить болісно, ​​оскільки якщо у мене є дев'ять рядків просто для створення пункту запиту IN (). Я хотів би мати щось на зразок підстановки параметрів підготовлених операторів

Відповіді:


275

Ви хочете джерело параметрів:

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

Це працює лише в тому випадку, якщо getJdbcTemplate()повертає екземпляр типуNamedParameterJdbcTemplate


5
Ідеально, NamedParameterJdbcTemplate було саме те, що я шукав. Крім того, мені подобаються названі параметри більше, ніж ці знаки запитань у всьому місці. Дуже дякую!
Малакс

5
Це працює для невеликих списків, але спроба використовувати його у великому списку призводить до запиту, де: ідентифікатори замінюються на "?,?,?,?, ... ..." і з достатньою кількістю списків він переповнює. Чи є рішення, яке працює для великих списків?
nsayer

Ви, ймовірно, повинні вставити значення у тимчасову таблицю і побудувати умову, використовуючи WHERE NOT EXISTS (SELECT ...).
позіхати

6
Для отримання відповіді на відповідь: Весна 3.1 Посилання - передача списків значень для пункту IN . Але в довідці нічого не було сказано про це: можна передавати будь-яку колекцію .
Тимофей Горшков

9
дивно, я отримую "код помилки [17004]; неправильний тип стовпця", коли я намагаюся це зробити.
Тревор

61

Я роблю запит "в пункті" з весною jdbc так:

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
Ви щойно опублікували відповідь на майже трирічне запитання з тим самим рішенням, що й у прийнятій відповіді. Чи є якась вагома причина для цього? :-)
Малакс

16
Ця відповідь надає більшої ясності, оскільки вона ілюструє, що NamedParameterJdbcTemplate потрібен для цього API ... тож дякую за додаткову деталізацію janwen
IcedDante

@janwen, Дякую за рішення !!! Це працює чудово згідно моєї вимоги !!
Karthik Amarnath Saakre

19

Якщо ви отримуєте виняток для: Недійсний тип стовпця

Будь ласка, використовуйте getNamedParameterJdbcTemplate()замістьgetJdbcTemplate()

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

Зауважте, що два інші аргументи обміняються місцями.


2
Це, здається, не є відповіддю на це питання. Чи повинен це бути коментар до іншої відповіді?
Дейв Швайсгут

2
@DaveSchweisguth Через два роки це, безумовно, вимагає відповіді.
dwjohnston

2

Зверніться сюди

написати запит із названим параметром, використовувати простий ListPreparedStatementSetterз усіма параметрами в послідовності. Просто додайте нижче фрагмент, щоб перетворити запит у традиційну форму на основі доступних параметрів,

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;

для мене це була єдина відповідь, яка спрацювала так, як я просто хотів встановити декілька заповнювачів
Капіль

-4

Багато речей змінилося з 2009 року, але я можу знайти лише відповіді, які говорять про те, що вам потрібно використовувати NamedParametersJDBCTemplate.

Для мене це працює, якщо я просто роблю

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

використовуючи SimpleJDBCTemplate або JDBCTemplate


11
Проблема цього рішення полягає в тому, що вміст у listeParamsForInClauseзвичайному не можна уникати і робить вас вразливим до ін'єкції SQL.
Малакс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.