PreparedStatement con lista de parámetros en una cláusula IN [duplicado]

Resuelto Harish asked hace 14 años • 14 respuestas

Cómo establecer el valor de la cláusula in en una declaración preparada en JDBC mientras se ejecuta una consulta.

Ejemplo:

connection.prepareStatement("Select * from test where field in (?)");

Si esta cláusula interna puede contener múltiples valores, ¿cómo puedo hacerlo? A veces conozco la lista de parámetros de antemano o, a veces, no la sé de antemano. ¿Cómo manejar este caso?

Harish avatar Jun 24 '10 10:06 Harish
Aceptado

Lo que hago es agregar un "?" para cada valor posible.

var stmt = String.format("select * from test where field in (%s)",
                         values.stream()
                         .map(v -> "?")
                         .collect(Collectors.joining(", ")));

Uso alternativo StringBuilder(que fue la respuesta original hace más de 10 años)

List values = ... 
StringBuilder builder = new StringBuilder();

for( int i = 0 ; i < values.size(); i++ ) {
    builder.append("?,");
}

String placeHolders =  builder.deleteCharAt( builder.length() -1 ).toString();
String stmt = "select * from test where field in ("+ placeHolders + ")";
PreparedStatement pstmt = ... 

Y luego felizmente establecí los parámetros.

int index = 1;
for( Object o : values ) {
   pstmt.setObject(  index++, o ); // or whatever it applies 
}
OscarRyz avatar Jun 24 '2010 03:06 OscarRyz

Podrías usar setArrayel método como se menciona en el javadoc a continuación:

http://docs.oracle.com/javase/6/docs/api/java/sql/PreparedStatement.html#setArray(int, java.sql.Array)

Código:

PreparedStatement statement = connection.prepareStatement("Select * from test where field in (?)");
Array array = statement.getConnection().createArrayOf("VARCHAR", new Object[]{"A1", "B2","C3"});
statement.setArray(1, array);
ResultSet rs = statement.executeQuery();
madx avatar Apr 05 '2016 08:04 madx

No puede reemplazar ?en su consulta con una cantidad arbitraria de valores. Cada uno ?es un marcador de posición para un único valor únicamente. Para admitir una cantidad arbitraria de valores, deberá crear dinámicamente una cadena que contenga ?, ?, ?, ... , ?la misma cantidad de signos de interrogación que la cantidad de valores que desea en su incláusula.

Asaph avatar Jun 24 '2010 03:06 Asaph

No desea usar PreparedStatment con consultas dinámicas usando la cláusula IN, al menos está seguro de que siempre tiene menos de 5 variables o un valor pequeño como ese, pero incluso así creo que es una mala idea (no es terrible, pero sí mala). Como la cantidad de elementos es grande, será peor (y terrible).

Imagine cientos o miles de posibilidades en su cláusula IN:

  1. Es contraproducente, perdió rendimiento y memoria porque almacena en caché cada vez que se realiza una nueva solicitud, y PreparedStatement no es solo para inyección SQL, se trata de rendimiento. En este caso, Statement es mejor.

  2. Su grupo tiene un límite de PreparedStatment (-1 predeterminado pero debe limitarlo) y alcanzará este límite. y si no tiene límite o tiene un límite muy grande, tiene cierto riesgo de pérdida de memoria y, en casos extremos, errores de OutofMemory. Entonces, si es para tu pequeño proyecto personal utilizado por 3 usuarios, no es dramático, pero no querrás eso si estás en una gran empresa y tu aplicación es utilizada por miles de personas y millones de solicitudes.

Un poco de lectura. IBM: Consideraciones sobre la utilización de la memoria cuando se utiliza el almacenamiento en caché de declaraciones preparadas

amdev avatar Oct 11 '2016 11:10 amdev

¡Necesitas jdbc4 y luego puedes usar setArray!

En mi caso no funcionó, ya que el tipo de datos UUID en postgres parece tener todavía sus puntos débiles, pero para los tipos habituales funciona.

ps.setArray(1, connection.createArrayOf("$VALUETYPE",myValuesAsArray));

Por supuesto, reemplace $VALUETYPE y myValuesAsArray con los valores correctos.

Observación siguiente al comentario de Marks:

¡Su base de datos y el controlador deben admitir esto! Probé Postgres 9.4 pero creo que esto se introdujo antes. Necesita un controlador jdbc 4; de lo contrario, setArray no estará disponible. Utilicé el controlador postgresql 9.4-1201-jdbc41 que se envía con Spring Boot.

Patrick Cornelissen avatar Mar 19 '2016 09:03 Patrick Cornelissen