說,我的形式什麼是使用JDBC來參數化IN子句的最佳方法?
SELECT * FROM MYTABLE WHERE MYCOL in (?)
的查詢和我想的參數參數爲IN。
有沒有一種簡單的方法在Java中使用JDBC來做到這一點,在某種程度上,可以在不修改SQL本身的情況下處理多個數據庫?
最近question I've found had to do with C#,我不知道是否有適合的Java/JDBC不同。
說,我的形式什麼是使用JDBC來參數化IN子句的最佳方法?
SELECT * FROM MYTABLE WHERE MYCOL in (?)
的查詢和我想的參數參數爲IN。
有沒有一種簡單的方法在Java中使用JDBC來做到這一點,在某種程度上,可以在不修改SQL本身的情況下處理多個數據庫?
最近question I've found had to do with C#,我不知道是否有適合的Java/JDBC不同。
我用盡可能多的?
構建SQL字符串,因爲我有值,以尋找解決此。
SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?)
首先我搜索了我可以傳入語句的數組類型,但所有JDBC數組類型都是供應商特定的。所以我留在多個?
。我能想到的
一種方法是使用java.sql.PreparedStatement中和位評委的索具
的PreparedStatement preparedStmt = conn.prepareStatement( 「SELECT * FROM MYTABLE WHERE MYCOL在()?」);
...然後...
preparedStmt.setString(1,[您stringged PARAMS]);
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html
這不會起作用,因爲它可能會創建一個查詢,比如'... WHERE MYCOL IN('2,3,5,6')'這不是您正在嘗試執行的操作。 – Progman 2010-05-18 21:50:53
據我所知,有在JDBC處理集合作爲參數沒有標準的支持。如果你能通過一個List並且將被擴展,那將是非常棒的。
Spring的JDBC訪問支持將集合作爲參數傳遞。你可以看看這是如何安全地進行編碼的靈感。
見Auto-expanding collections as JDBC parameters
(文章首先討論了休眠,然後繼續討論JDBC)
有確實JDBC做到這一點沒有直接的方法。 部分 JDBC驅動程序似乎支持IN
子句中的PreparedStatement#setArray()
。我只是不確定哪些是。
你可以只使用一個輔助方法與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個項目有這個限制。
這種方法會導致SQL注入嗎? – 2011-09-22 07:04:47
@Kaylan:沒有單一代碼行將用戶控制的輸入raw添加到SQL查詢字符串中。所以絕對沒有SQL注入風險的手段。 – BalusC 2011-09-22 12:51:56
好。感謝您的澄清 – 2011-10-23 14:50:34
看我的試用和它的成功,據說列表大小有潛在的限制。 列表l =數組。asList(new 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);
sormula使得這個簡單的(見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());
}
由於無人接聽的大IN子句的情況下(超過100)我會扔我解決了這個問題,其很適合用於JDBC。簡而言之,我將IN
替換爲tmp表格上的INNER JOIN
。
我所做的是製作我稱之爲批處理ID表,並根據RDBMS我可以做一個tmp表或內存表。
該表有兩列。一列帶有來自IN子句的ID,另一列帶有我在飛行中生成的批次ID。
SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?
之前您選擇您的推IDS到表中給定的批次ID。 然後,您只需將您的原始查詢IN子句替換爲您的ID表上的INNER JOIN匹配WHERE batch_id等於您當前的批次。完成之後,爲您批量刪除條目。
+1這對於大型數據集非常有效,並且不會破壞數據庫 – jasonk 2012-07-08 11:31:43
嗯,是不是半連接('IN'或'EXISTS'謂詞)可能會勝過內部連接? – 2017-03-30 20:51:06
@LukasEder YMMV與我的SQL僞代碼。一如既往的測試/基準。這是一個有趣的想法。說到這一點,我應該去看看我們的實際SQL是什麼。 – 2017-03-31 12:50:35
執行此操作的標準方法是(如果您使用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;
}
我們可以使用不同的替代方法。
查看有關這些here的更多詳情。
得到的答覆從docs.spring(19.7.3)
SQL標準允許用於選擇基於包括值的變量列表中的表達式的行。一個典型的例子是select * from T_ACTOR,其中id在(1,2,3)中。這個變量列表並不直接支持JDBC標準的預處理語句;你不能聲明可變數目的佔位符。您需要準備好所需數量的佔位符時需要多種變體,或者一旦知道需要多少佔位符,就需要動態生成SQL字符串。 NamedParameterJdbcTemplate和JdbcTemplate中提供的named參數支持採用後一種方法。將這些值作爲基本對象的java.util.List傳遞。此列表將用於插入所需的佔位符並在語句執行過程中傳遞值。
希望這可以幫助你。
這就是我們現在正在做的,但我希望有一個統一的方式來做到這一點沒有自定義SQL ... – Uri 2010-05-18 21:30:14
另外,如果它是像甲骨文一樣,它將不得不重新解析大部分每個語句。 – orbfish 2010-07-15 14:46:41