У меня есть заявление вставки, как показано ниже

private final COLUMNS = "NAME,TYPE,STATUS,CATEGORY";

String values = list
    .stream()
    .collect(Collectors.joining(","));

String insertStatement = String.format("INSERT INTO ifc.documents (%s) VALUES (%s) ",COLUMNS,values); 

Я могу легко поместить COLUMNS, так как кавычки не требуются, но для значений мой SQL не выполняется, и я жалуюсь на пропущенные кавычки для кода выше.

Итак, я попробовал

 String values = list.stream()
.collect(Collectors.joining("','"));

Но с этим тоже ничего не получится. Так что я сделал обходной путь и добавил еще один оператор с префиксом и суффиксом кавычки, и он начал работать.

 values = "'"+values+"'";

Чтобы быть более конкретным, если я скажу «отдых», «тест» и «лучший» в списке

Тогда ожидаемый результат

'rest','test','best'

Кто-нибудь знает лучшее решение для этого?

12
Vinay Prajapati 23 Фев 2018 в 21:59

4 ответа

Лучший ответ

Вы можете использовать Collectors.joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix) и смотреть здесь для API.

String values = list.stream().collect(Collectors.joining("','", "'", "'"));
20
developer 23 Фев 2018 в 19:04

Вы можете использовать перегрузка Collectors.joining, которая принимает параметры префикса и суффикса.

 String values = list.stream()
    .collect(Collectors.joining("','", "'", "'"));

Это поместит одинарные кавычки вокруг всех ваших значений правильно, при условии, что все ваши значения являются строками, которые требуют одинарных кавычек для синтаксиса.

Но, как и в случае со всеми проблемами, связанными с построением запроса путем конкатенации значений, это делает вас уязвимым для SQL-инъекций. Более безопасное и универсальное решение предполагает использование {{X0} }. Заполнители значений - это ? символов, и вы можете использовать различные методы setXyz для безопасной установки значений. Он защищает от внедрения SQL и позволяет использовать любые типы данных, а не только строковые.

6
rgettman 23 Фев 2018 в 19:06

Не создавайте часть «значения», используя конкатенацию строк, так как это открывает возможность для атак SQL-инъекций.

Я бы использовал готовое заявление здесь. Вы все еще можете построить свой запрос так:

List<String> columns = Arrays.asList("column1", "column2", "column3");
String columnsFragment = columns.stream().collect(Collectors.joining(","));
String placeholdersFragment = columns.stream().filter(s -> "?").collect(Collectors.joining(","))
String insertStatement = String.format("INSERT INTO ifc.documents (%s) VALUES (%s) ", columnsFragment, placeholdersFragment);

А затем используйте insertStatement с PreparedStatement:

PreparedStatement st = connection.prepareStatement(insertStatement);
for (int i = 0; i < values.size(); i++) {
    // +1 because prepared statement parameters indices are 1-based
    st.setString(i + 1, values.get(i));
}
st.executeUpdate();

В этом случае результирующий запрос будет выглядеть так

INSERT INTO ifc.documents (column1, column2, column3) VALUES (?, ?, ?)
4
Roman Puchkovskiy 23 Фев 2018 в 19:16

В другом решении используется метод String.join похож на этот

String result = "'"+String.join("','", list)+"'";

 Function<List<String>,String> function = list2->"'".concat(String.join("','",list2)).concat("'");
 System.out.println(function.apply(list));

Или используйте сокращение, как это

String result =   "'"+list.stream().reduce((s, p) -> s + "','" + p).get()+"'";
1
Hadi J 24 Фев 2018 в 10:03