什么是使用JDBC来参数化IN子句的最佳方法?
说我有一个forms的查询
SELECT * FROM MYTABLE WHERE MYCOL in (?)
我想参数化参数。
有没有一种简单的方法可以在Java中使用JDBC执行此操作,而且可以在不修改SQL本身的情况下在多个数据库上工作?
我发现最接近的问题与C#有关 ,我想知道是否有什么不同的Java / 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个项目有这个限制。
由于没有人回答一个大的IN子句(超过100)的情况下,我会抛出我的解决scheme,这个问题很好地适用于JDBC。 简而言之,我用tmp表上的INNER JOIN
replaceIN
。
我所做的是使我称之为批ID表,并根据RDBMS我可以做一个TMP表或内存表。
该表有两列。 一列有来自IN子句的ID,另一列有我在飞行中产生的批次ID。
SELECT * FROM MYTABLE M INNER JOIN IDTABLE T ON T.MYCOL = M.MYCOL WHERE T.BATCH = ?
在select之前,用给定的批次ID将您的ID推入表格。 那么你只需要用你的id表中的INNER JOINreplace你的原始查询IN子句WHERE batch_id等于你当前的批处理。 完成之后,为您批量删除条目。
这样做的标准方法是(如果您使用Spring JDBC)是使用org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate类。
使用此类,可以将List定义为SQL参数,并使用NamedParameterJdbcTemplate来replace命名参数。 例如:
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; }
我通过构buildSQLstring来解决这个问题?
因为我有价值观。
SELECT * FROM MYTABLE WHERE MYCOL in (?,?,?,?)
首先我search了一个我可以传入语句的数组types,但所有JDBC数组types都是供应商特定的。 所以我留在多个?
。
我从docs.spring得到了答案(19.7.3)
SQL标准允许根据包含variables列表的expression式来select行。 一个典型的例子是select * from T_ACTOR,其中id在(1,2,3)中。 这个variables列表并不直接支持JDBC标准的预处理语句; 你不能声明可变数目的占位符。 您需要准备好一定数量的占位符,然后在知道需要多less占位符时,需要dynamic生成SQLstring。 NamedParameterJdbcTemplate和JdbcTemplate中提供的named参数支持采用后一种方法。 将这些值作为基本对象的java.util.List传递。 该列表将用于插入所需的占位符,并在语句执行过程中传递值。
希望这可以帮助你。
AFAIK,在JDBC中没有处理集合作为参数的标准支持。 如果你能通过一个列表,这将是很好的,这将是扩大。
Spring的JDBC访问支持作为parameter passing集合。 你可以看看这是如何安全地进行编码的灵感。
请参阅将集合自动扩展为JDBC参数
(本文首先讨论Hibernate,然后继续讨论JDBC。)
看到我的审判和成功,据说名单大小有潜在的限制。 List l = Arrays.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);
我们可以使用不同的替代方法。
- 执行单个查询 – 速度慢,不推荐
- 使用存储过程 – 数据库特定
- dynamic创buildPreparedStatement查询 – 性能良好但cachingfunction松散,需要重新编译
- 在PreparedStatement Query中使用NULL – 我认为这是一个优化性能的好方法。
在这里查看更多细节。
sormula使这很简单(见例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()); }
我能想到的一个方法是使用java.sql.PreparedStatement和一些陪审团索具
PreparedStatement preparedStmt = conn.prepareStatement(“SELECT * FROM MYTABLE WHERE MYCOL in(?)”);
… 接着 …
preparedStmt.setString(1,[你的string参数]);
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html