Tengo una declaración de inserción como a continuación

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

Puedo poner COLUMNAS fácilmente ya que no se requieren comillas, pero para los valores, mi SQL falla y me quejo de las comillas faltantes para el código anterior.

Entonces intenté

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

Pero también falla con este, así que hice una solución alternativa y agregué otra declaración con el prefijo y el sufijo de una cita simple y comenzó a funcionar.

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

Para ser más específico, si tengo que decir "descanso", "prueba" y "mejor" en la lista

Entonces la salida esperada es

'rest','test','best'

Alguien sabe una mejor solución para esto?

10
Vinay Prajapati 23 feb. 2018 a las 21:59

4 respuestas

La mejor respuesta

En realidad, puede usar Collectors.joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix) y puede mirar aquí para la API.

String values = list.stream().collect(Collectors.joining("','", "'", "'"));
20
developer 23 feb. 2018 a las 19:04

Puede usar sobrecarga de Collectors.joining que toma parámetros de prefijo y sufijo.

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

Esto colocará comillas simples alrededor de todos sus valores correctamente, asumiendo que todos sus valores son cadenas que necesitan comillas simples para la sintaxis.

Pero, como es el caso con todos los problemas que implican la construcción de una consulta mediante la concatenación de valores, deja su vulnerabilidad a la inyección de SQL. Una solución más segura y versátil implica el uso de un {{X0} }. Los marcadores de posición de valor son caracteres ?, y puede usar varios métodos setXyz para establecer los valores de forma segura. Protege contra la inyección de SQL y permite el uso de cualquier tipo de datos, no solo los tipos de cadena.

6
rgettman 23 feb. 2018 a las 19:06

No construya la parte de 'valores' usando la concatenación de cadenas, ya que abre la posibilidad de ataques de inyección SQL.

Yo usaría una declaración preparada aquí. Todavía puede construir su solicitud de esta manera:

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

Y luego use insertStatement con 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();

En este caso, la consulta resultante se verá como

INSERT INTO ifc.documents (column1, column2, column3) VALUES (?, ?, ?)
4
Roman Puchkovskiy 23 feb. 2018 a las 19:16

Otra solución de uso de String.join método como este

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

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

O use reducción como esta

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