Каков наилучший подход с использованием JDBC для параметризации предложения IN?
Скажем, что у меня есть запрос формы
SELECT * FROM MYTABLE WHERE MYCOL in (?)
И я хочу параметризовать аргументы в.
Есть ли простой способ сделать это в Java с JDBC, таким образом, чтобы работать с несколькими базами данных без изменения самого SQL?
Самый близкий вопрос, который я нашел, связан с С#, мне интересно, есть ли что-то другое для Java/JDBC.
Ответы
Ответ 1
В JDBC нет простого способа сделать это. Некоторые драйверы JDBC, похоже, поддерживают PreparedStatement#setArray()
в предложении IN
. Я не уверен, какие именно.
Вы можете просто использовать вспомогательный метод с String#join()
и Collections#nCopies()
для создания заполнителей для предложения IN
и другого вспомогательного метода для установки всех значений в цикле с помощью PreparedStatement#setObject()
.
public static String preparePlaceHolders(int length) {
return String.join(",", Collections.nCopies(length, "?"));
}
public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setObject(i + 1, values[i]);
}
}
Здесь вы можете использовать его:
private static final String SQL_FIND = "SELECT id, name, value FROM entity WHERE id IN (%s)";
public List<Entity> find(Set<Long> ids) throws SQLException {
List<Entity> entities = new ArrayList<Entity>();
String sql = String.format(SQL_FIND, preparePlaceHolders(ids.size()));
try (
Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(sql);
) {
setValues(statement, ids.toArray());
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
entities.add(map(resultSet));
}
}
}
return entities;
}
private static Entity map(ResultSet resultSet) throws SQLException {
Enitity entity = new Entity();
entity.setId(resultSet.getLong("id"));
entity.setName(resultSet.getString("name"));
entity.setValue(resultSet.getInt("value"));
return entity;
}
Обратите внимание, что некоторые базы данных имеют ограничение допустимого количества значений в предложении IN
. Oracle, например, имеет этот предел для 1000 элементов.
Ответ 2
Поскольку никто не отвечает в случае предложения большого IN (более 100), я брошу свое решение по этой проблеме, которая отлично работает для JDBC. Короче говоря, я заменяю IN
на INNER JOIN
на таблице tmp.
Что я делаю, так это то, что я называю таблицей пакетных идентификаторов, и в зависимости от СУБД я могу сделать это tmp-таблицу или таблицу памяти.
Таблица имеет два столбца. Один столбец с идентификатором из раздела IN и другой столбец с идентификатором пакета, который я генерирую на лету.
SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?
Перед тем, как вы выберете, вы переместите свои идентификаторы в таблицу с заданным идентификатором партии.
Затем вы просто заменяете исходный запрос IN предложением с помощью комбинации INNER JOIN в вашей таблице идентификаторов. WHERE batch_id равно вашей текущей партии. После вашего завершения удалите записи для вас.
Ответ 3
Стандартный способ сделать это (если вы используете Spring JDBC) - использовать org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.
Используя этот класс, можно определить List как ваш параметр SQL и использовать NamedParameterJdbcTemplate для замены именованного параметра. Например:
public List<MyObject> getDatabaseObjects(List<String> params) {
NamedParameterJdbcTemplate jdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
String sql = "select * from my_table where my_col in (:params)";
List<MyObject> result = jdbcTemplate.query(sql, Collections.singletonMap("params", params), myRowMapper);
return result;
}
Ответ 4
Я решил это, построив строку SQL с таким количеством ?
, как у меня есть значения для поиска.
SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?)
Сначала я искал тип массива, который я могу передать в оператор, но все типы массивов JDBC специфичны для поставщика. Поэтому я остался с несколькими ?
.
Ответ 5
Я получил ответ от docs.spring(19.7.3)
Стандарт SQL позволяет выбирать строки на основе выражения, содержащего список переменных. Типичным примером может быть выбор * из T_ACTOR, где id в (1, 2, 3). Этот список переменных напрямую не поддерживается для подготовленных операторов стандартом JDBC; вы не можете объявить переменное количество заполнителей. Вам нужно несколько вариантов с желаемым количеством подготовленных заполнителей или вам нужно генерировать строку SQL динамически, как только вы знаете, сколько заполнителей требуется. Именованный параметр поддержки, предоставляемый в NamedParameterJdbcTemplate и JdbcTemplate, использует последний подход. Передайте значения как java.util.List примитивных объектов. Этот список будет использоваться для вставки необходимых заполнителей и передачи значений во время выполнения инструкции.
Надеюсь, это поможет вам.
Ответ 6
AFAIK, в JDBC нет стандартной поддержки для обработки Коллекций в качестве параметров. Было бы здорово, если бы вы могли просто перейти в список, и это было бы расширено.
Spring Доступ JDBC поддерживает передачу коллекций в качестве параметров. Вы можете посмотреть, как это делается для вдохновения для безопасного кодирования.
См. Автоматическое расширение коллекций в качестве параметров JDBC
(В статье сначала обсуждается Hibernate, затем обсуждается JDBC.)
Ответ 7
См. мою пробную версию и успех. Говорят, что размер списка имеет потенциальное ограничение. Список l = Arrays.asList(новое целое число [] {12496,12497,12498,12499}); Карта param = Collections.singletonMap( "goodsid", l);
NamedParameterJdbcTemplate namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource());
String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid in(:goodsid)";
List<Long> list = namedParameterJdbcTemplate.queryForList(sql, param2, Long.class);
Ответ 8
sormula делает это простым (см. Пример 4):
ArrayList<Integer> partNumbers = new ArrayList<Integer>();
partNumbers.add(999);
partNumbers.add(777);
partNumbers.add(1234);
// set up
Database database = new Database(getConnection());
Table<Inventory> inventoryTable = database.getTable(Inventory.class);
// select operation for list "...WHERE PARTNUMBER IN (?, ?, ?)..."
for (Inventory inventory: inventoryTable.
selectAllWhere("partNumberIn", partNumbers))
{
System.out.println(inventory.getPartNumber());
}
Ответ 9
Существуют различные альтернативные подходы, которые мы можем использовать.
- Выполнять одиночные запросы - медленно и не рекомендуется
- Использование хранимой процедуры - спецификация базы данных
- Динамическое создание запроса PreparedStatement - хорошая производительность, но отсутствие преимуществ кэширования и перекомпиляции потребностей.
- Использование NULL в запросе PreparedStatement - я думаю, что это хороший подход с оптимальной производительностью.
Подробнее об этих здесь.
Ответ 10
Один из способов, о котором я могу думать, - использовать java.sql.PreparedStatement и немного присяжных присяжных
PreparedStatement подготовленStmt = conn.prepareStatement( "SELECT * FROM MYTABLE WHERE MYCOL в (?)" );
... и затем...
prepareStmt.setString(1, [ваши строгие параметры]);
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html