PreparedStatement和IN子句中的参数列表

如何在执行查询时在JDBC的preparedStatement中为in子句设置值。

例:

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

如果这个子句可以保存多个值,我该怎么做。 有时我事先知道参数列表,有时候我事先不知道。 如何处理这种情况?

我所做的是添加一个“?” 为每个可能的价值。

例如:

 List possibleValues = ... StringBuilder builder = new StringBuilder(); for( int i = 0 ; i < possibleValue.size(); i++ ) { builder.append("?,"); } String stmt = "select * from test where field in " + builder.deleteCharAt( builder.length() -1 ).toString(); PreparedStatement pstmt = ... 

然后愉快地设置参数

 int index = 1; for( Object o : possibleValue ) { pstmt.setObject( index++, o ); // or whatever it applies } 

您可以使用下面的javadoc中提到的setArray方法:

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

码:

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

你可能想检查这个链接:

http://www.javaranch.com/journal/200510/Journal200510.jsp#a2

它解释了用in子句创buildPreparedStatement的不同方法的优缺点。

编辑:

一个显而易见的方法是dynamic生成“?” 部分在运行时,但我不想只是build议这种方法,因为取决于你使用它的方式,它可能是低效率的(因为PreparedStatement将需要每次被使用“编译”)

你不能替代? 在您的查询与任意数量的值。 每个? 是一个只有一个值的占位符。 为了支持任意数量的值,你必须dynamic地build立一个包含?, ?, ?, ... , ?的string?, ?, ?, ... , ? 问号的数量与in子句中要求的值的数量相同。

你需要jdbc4然后你可以使用setArray!

在我的情况下,它没有奏效,因为postgres中的UUID数据types似乎仍然有它的弱点,但通常的types它的工作原理。

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

当然用正确的值replace$ VALUETYPE和myValuesAsArray。

备注以下标记评论:

您的数据库和驱动程序需要支持! 我试过Postgres 9.4,但我认为这是早些时候介绍的。 您需要一个jdbc 4驱动程序,否则setArray将不可用。 我使用了弹簧引导的postgresql 9.4-1201-jdbc41驱动程序

你可以做的是只要知道需要在IN子句中放置多less个值,就可以通过一个简单的for循环dynamic地构buildselectstring('IN(?)'部分)。 然后可以实例化PreparedStatement。

你不希望使用PreparedStatmentdynamic查询使用IN子句,至less你确定你总是在5以下的variables或一个小的值,但即使这样,我认为这是一个坏主意(不可怕,但不好)。 因为元素的数量很大,所以会变得更糟(也是可怕的)。

想象一下你的IN子句有上百种可能性:

  1. 这是适得其反的,你失去了性能和内存,因为每当新请求caching时,PreparedStatement就不仅仅是SQL注入,而是性能问题。 在这种情况下,声明更好。

  2. 你的游泳池有一个PreparedStatment的限制(-1默认,但你必须限制它),你会达到这个限制! 如果没有限制或非常大的限制,则存在内存泄漏的风险,在极端情况下会出现OutofMemory错误。 所以,如果是为3个用户使用的小型个人项目,这并不是什么戏剧性的,但是如果你在一家大公司,而且你的应用被千人和百万的请求所使用,那么你就不需要这么做了。

一些阅读。 IBM:使用预准备语句caching时的内存使用注意事项

 public static ResultSet getResult(Connection connection, List values) { try { String queryString = "Select * from table_name where column_name in"; StringBuilder parameterBuilder = new StringBuilder(); parameterBuilder.append(" ("); for (int i = 0; i < values.size(); i++) { parameterBuilder.append("?"); if (values.size() > i + 1) { parameterBuilder.append(","); } } parameterBuilder.append(")"); PreparedStatement statement = connection.prepareStatement(queryString + parameterBuilder); for (int i = 1; i < values.size() + 1; i++) { statement.setInt(i, (int) values.get(i - 1)); } return statement.executeQuery(); } catch (Exception d) { return null; } } 

许多数据库都有一个临时表的概念,即使假设你没有一个临时表,你也可以使用一个唯一的名字来生成一个临时表,然后在完成时删除它。 虽然创build和删除表的开销很大,但对于非常大的操作,或者在将数据库用作本地文件或内存(SQLite)的情况下,这可能是合理的。

我正在使用Java / SqlLite的一个例子:

 String tmptable = "tmp" + UUID.randomUUID(); sql = "create table " + tmptable + "(pagelist text not null)"; cnn.createStatement().execute(sql); cnn.setAutoCommit(false); stmt = cnn.prepareStatement("insert into "+tmptable+" values(?);"); for(Object o : rmList){ Path path = (Path)o; stmt.setString(1, path.toString()); stmt.execute(); } cnn.commit(); cnn.setAutoCommit(true); stmt = cnn.prepareStatement(sql); stmt.execute("delete from filelist where path + page in (select * from "+tmptable+");"); stmt.execute("drop table "+tmptable+");"); 

如果你能够重新使用这个表格,这将会更加高效。

目前,MySQL不允许在一个方法调用中设置多个值。 所以你必须有自己的控制权。 我通常为预定义的参数创build一个准备好的语句,然后根据需要添加尽可能多的批次。

  int paramSizeInClause = 10; // required to be greater than 0! String color = "FF0000"; // red String name = "Nathan"; Date now = new Date(); String[] ids = "15,21,45,48,77,145,158,321,325,326,327,328,329,330,331,332,333,334,335,336,337,338,339,340,341,342,343,344,345,346,347,348,349,350,351,358,1284,1587".split(","); // Build sql query StringBuilder sql = new StringBuilder(); sql.append("UPDATE book SET color=? update_by=?, update_date=? WHERE book_id in ("); // number of max params in IN clause can be modified // to get most efficient combination of number of batches // and number of parameters in each batch for (int n = 0; n < paramSizeInClause; n++) { sql.append("?,"); } if (sql.length() > 0) { sql.deleteCharAt(sql.lastIndexOf(",")); } sql.append(")"); PreparedStatement pstm = null; try { pstm = connection.prepareStatement(sql.toString()); int totalIdsToProcess = ids.length; int batchLoops = totalIdsToProcess / paramSizeInClause + (totalIdsToProcess % paramSizeInClause > 0 ? 1 : 0); for (int l = 0; l < batchLoops; l++) { int i = 1; pstm.setString(i++, color); pstm.setString(i++, name); pstm.setTimestamp(i++, new Timestamp(now.getTime())); for (int count = 0; count < paramSizeInClause; count++) { int param = (l * paramSizeInClause + count); if (param < totalIdsToProcess) { pstm.setString(i++, ids[param]); } else { pstm.setNull(i++, Types.VARCHAR); } } pstm.addBatch(); } } catch (SQLException e) { } finally { //close statement(s) } 

如果不想在没有更多参数的情况下设置NULL,则可以修改代码来构build两个查询和两个准备好的语句。 第一个是相同的,但其余(模数)的第二个声明。 在这个特定的例子中,将是一个10个参数和一个8个参数的查询。 您将不得不为第一个查询(前30个参数)添加3个批次,然后为第二个查询(8个参数)添加一个批次。

 public class Test1 { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub System.out.println("helow"); String where="where task in "; where+="("; // where+="'task1'"; int num[]={1,2,3,4}; for (int i=0;i<num.length+1;i++) { if(i==1){ where +="'"+i+"'"; } if(i>1 && i<num.length) where+=", '"+i+"'"; if(i==num.length){ System.out.println("This is last number"+i); where+=", '"+i+"')"; } } System.out.println(where); } } 

您可以使用 :

 for( int i = 0 ; i < listField.size(); i++ ) { i < listField.size() - 1 ? request.append("?,") : request.append("?"); } 

然后 :

 int i = 1; for (String field : listField) { statement.setString(i++, field); } 

例如:

 List<String> listField = new ArrayList<String>(); listField.add("test1"); listField.add("test2"); listField.add("test3"); StringBuilder request = new StringBuilder("SELECT * FROM TABLE WHERE FIELD IN ("); for( int i = 0 ; i < listField.size(); i++ ) { request = i < (listField.size() - 1) ? request.append("?,") : request.append("?"); } DNAPreparedStatement statement = DNAPreparedStatement.newInstance(connection, request.toString); int i = 1; for (String field : listField) { statement.setString(i++, field); } ResultSet rs = statement.executeQuery(); 

public static void main(String arg []){

  Connection connection = ConnectionManager.getConnection(); PreparedStatement pstmt = null; //if the field values are in ArrayList List<String> fieldList = new ArrayList(); try { StringBuffer sb = new StringBuffer(); sb.append(" SELECT * \n"); sb.append(" FROM TEST \n"); sb.append(" WHERE FIELD IN ( \n"); for(int i = 0; i < fieldList.size(); i++) { if(i == 0) { sb.append(" '"+fieldList.get(i)+"' \n"); } else { sb.append(" ,'"+fieldList.get(i)+"' \n"); } } sb.append(" ) \n"); pstmt = connection.prepareStatement(sb.toString()); pstmt.executeQuery(); } catch (SQLException se) { se.printStackTrace(); } } 
 Using Java 8 APIs, List<Long> empNoList = Arrays.asList(1234, 7678, 2432, 9756556, 3354646); List<String> parameters = new ArrayList<>(); empNoList.forEach(empNo -> parameters.add("?")); //Use forEach to add required no. of '?' String commaSepParameters = String.join(",", parameters); //Use String to join '?' with ',' StringBuilder selectQuery = new StringBuilder().append("SELECT COUNT(EMP_ID) FROM EMPLOYEE WHERE EMP_ID IN (").append(commaSepParameters).append(")");