2010-05-18 6 views

Respuesta

3

Lo resolví construyendo la cadena de SQL con ? ya que tengo valores para buscar.

SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?) 

Primero buscó un tipo de matriz que puedo pasar a la declaración, pero todos los tipos de matriz JDBC son específicos del proveedor. Así que me quedé con el ? múltiple.

+1

Eso es lo que estamos haciendo en este momento, pero lo que esperaba era que hubiera una forma uniforme de hacerlo sin SQL personalizado ... – Uri

+0

Además, si es algo así como Oracle, tendrá que volver a analizar la mayoría de las declaraciones. – orbfish

-1

Una forma que se me ocurre es utilizar el java.sql.PreparedStatement y un poco de jurado aparejo

PreparedStatement preparedStmt = conn.prepareStatement ("SELECT * FROM MITABLA DONDE mycol en()?");

... y luego ...

preparedStmt.setString (1, [params sus stringged]);

http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html

+6

Esto no funcionará, ya que podría estar creando una consulta como '... WHERE MYCOL IN ('2,3,5,6')' que no es la que está tratando de hacer. – Progman

1

yo sepa, no hay soporte estándar de JDBC para el manejo de las colecciones como parámetros. Sería genial si solo pudieras pasar una Lista y eso se expandiría.

El acceso JDBC de Spring admite el paso de colecciones como parámetros. Puede ver cómo se hace esto para obtener inspiración al codificar esto de forma segura.

Ver Auto-expanding collections as JDBC parameters

(El artículo discute primero Hibernate, a continuación, pasa a examinar JDBC.)

36

Hay de hecho hay manera fácil de hacer esto en JDBC. Algunos Los controladores JDBC parecen admitir PreparedStatement#setArray() en la cláusula IN. No estoy seguro de cuáles son.

Se podía usar un método de ayuda con String#join() y Collections#nCopies() para generar los marcadores de posición para IN cláusula y otro método de ayuda para configurar todos los valores en un bucle con 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]); 
    } 
} 

He aquí cómo se podría utilizar:

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

Tenga en cuenta que algunas bases de datos tienen un límite de la cantidad permitida de los valores en la cláusula IN. Oracle, por ejemplo, tiene este límite en 1000 artículos.

+1

¿Este enfoque lleva a la inyección SQL? –

+3

@Kaylan: ninguna línea de código agrega entrada controlada por el usuario sin formato a la cadena de consulta SQL. Entonces definitivamente no hay medios para un riesgo de inyección SQL. – BalusC

+0

Bien. Gracias por su aclaración –

0

Vea mi versión de prueba y Éxito, Se dice que el tamaño de la lista tiene una limitación potencial. Lista l = Matrices.asList (nuevo Integer [] {12496,12497,12498,12499}); Map 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); 
0

sormula hace que esta simple (ver Example 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()); 
} 
12

Ya que nadie contesta el caso de un gran en la cláusula (más de 100) voy a tirar mi solución a este problema que funciona bien para JDBC. En resumen, reemplazo el IN con un INNER JOIN en una tabla tmp.

Lo que hago es hacer lo que llamo una tabla de ID de lote y, dependiendo del RDBMS, puedo convertirla en una tabla de tmp o en una tabla de memoria.

La tabla tiene dos columnas. Una columna con la identificación de la cláusula IN y otra columna con una identificación por lotes que genero sobre la marcha.

SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ? 

Antes de seleccionar, inserta sus identificaciones en la tabla con una identificación de lote determinada. Luego, simplemente reemplaza su consulta original en la cláusula IN con una unión INNER JOIN en su tabla de ids WHERE batch_id es igual a su lote actual. Después de que haya terminado, elimine las entradas de su lote.

+2

+1 Esto será bastante eficiente para grandes conjuntos de datos, y no romperá su base de datos – jasonk

+0

Hmm, ¿no sería una semi unión ('IN' o' EXISTS' predicado) posiblemente supere la unión interna? –

+0

@LukasEder YMMV con mi pseudo código SQL. Como siempre prueba/punto de referencia. Es una idea interesante. Hablando de lo que debería ir, mire para qué es nuestro SQL real cuando hacemos esto. –

7

La forma estándar de hacer esto es (si está utilizando Spring JDBC) utilizar la clase org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate.

Usando esta clase, es posible definir una Lista como su parámetro de SQL y usar la NamedParameterJdbcTemplate para reemplazar un parámetro con nombre. Por ejemplo:

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; 
} 
0

Existen diferentes enfoques alternativos que podemos utilizar.

  1. Ejecutar individuales Consultas - lentos y no recomendados
  2. El uso de procedimientos almacenados - base de datos específica
  3. Creación PreparedStatement de consulta dinámicamente - un buen rendimiento, pero los beneficios sueltos de almacenamiento en caché y necesita recompilación
  4. usar NULL en PreparedStatement consultas - Creo que este es un buen enfoque con un rendimiento óptimo.

Consulte más detalles acerca de estos here.

2

Tengo la respuesta de docs.spring(19.7.3)

El estándar SQL permite la selección de filas en función de una expresión que incluye una lista de variables de valores. Un ejemplo típico sería seleccionar * de T_ACTOR donde id en (1, 2, 3). Esta lista de variables no se admite directamente para las declaraciones preparadas por el estándar JDBC; no puede declarar una cantidad variable de marcadores de posición. Necesita una serie de variaciones con la cantidad deseada de marcadores de posición preparados, o necesita generar la cadena de SQL de forma dinámica una vez que sepa cuántos marcadores de posición se requieren. El soporte de parámetro nombrado proporcionado en NamedParameterJdbcTemplate y JdbcTemplate toma el último enfoque. Pase los valores como java.util.List de objetos primitivos. Esta lista se usará para insertar los marcadores de posición necesarios y pasar los valores durante la ejecución de la declaración.

Espero que esto te pueda ayudar.

Cuestiones relacionadas