如何在执行查询时在JDBC中的preparedStatement中设置in子句的值。
示例:
connection.prepareStatement("Select * from test where field in (?)");
如果这个in子句可以包含多个值,我该怎么办呢。有时我事先知道参数列表,有时我事先不知道。如何处理这种情况?
答案 0 :(得分:82)
我做的是添加“?”为每个可能的价值。
例如:
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
}
答案 1 :(得分:43)
您可以使用下面javadoc中提到的setArray
方法:
代码:
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();
答案 2 :(得分:14)
您可能需要查看此链接:
http://www.javaranch.com/journal/200510/Journal200510.jsp#a2
它解释了使用PreparedStatement
子句创建in
的不同方法的优缺点。
编辑:
一个明显的方法是动态生成'?'部分在运行时,但我不想仅仅建议这种方法,因为取决于你使用它的方式,它可能是低效的(因为PreparedStatement
每次使用时都需要'编译')
答案 3 :(得分:7)
您无法使用任意数量的值替换查询中的?
。每个?
只是一个值的占位符。要支持任意数量的值,您必须动态构建一个包含?, ?, ?, ... , ?
的字符串,其中问号的数量与您在in
子句中所需的值的数量相同。
答案 4 :(得分:3)
你需要jdbc4然后你可以使用setArray!
在我的情况下它没有用,因为postgres中的UUID数据类型似乎仍有其弱点,但对于通常的类型它可以工作。
ps.setArray(1, connection.createArrayOf("$VALUETYPE",myValuesAsArray));
当然用正确的值替换$ VALUETYPE和myValuesAsArray。
备注Marks评论:
您的数据库和驱动程序需要支持此功能!我试过Postgres 9.4,但我认为这是之前介绍过的。你需要一个jdbc 4驱动程序,否则setArray将无法使用。我使用了带弹簧启动的postgresql 9.4-1201-jdbc41驱动程序
答案 5 :(得分:2)
只要您知道需要在IN子句中放置多少个值,您可以通过简单的for循环动态构建选择字符串('IN(?)'部分)。然后,您可以实例化PreparedStatement。
答案 6 :(得分:2)
您不希望将PreparedStatment与使用IN子句的动态查询一起使用,至少您确定总是在5变量或类似的小值下,但即便如此,我认为这是一个坏主意(不可怕,但不好)。由于元素的数量很大,它会更糟(也很糟糕)。
想象一下你的IN子句中有一百或几千种可能性:
它适得其反,你失去了性能和内存,因为每次新请求都会缓存,而PreparedStatement不只是用于SQL注入,而是关于性能。在这种情况下,Statement更好。
您的游泳池有一个PreparedStatment限制(-1 defaut但你必须限制它),你将达到这个限制!如果你没有限制或非常大的限制你有一些内存泄漏的风险,并在极端情况下OutofMemory错误。因此,如果它适用于3个用户使用的小型个人项目,那么它并不具有戏剧性,但如果您是一家大公司并且您不想要这样做,那么您就不会想要这样做了。应用程序被千人和百万请求使用。
一些阅读。 IBM : Memory utilization considerations when using prepared statement caching
答案 7 :(得分:2)
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;
}
}
答案 8 :(得分:1)
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);
}
}
答案 9 :(得分:1)
目前,MySQL不允许在一次方法调用中设置多个值。 所以你必须由你自己控制。我通常会为预定义数量的参数创建一个预准备语句,然后根据需要添加尽可能多的批次。
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,则可以修改代码以构建两个查询和两个预准备语句。第一个是相同的,但是余数(模数)的第二个陈述。 在这个特定的例子中,这将是10个参数的一个查询和8个参数的一个查询。您必须为第一个查询添加3个批次(前30个参数),然后为第二个查询添加一个批次(8个参数)。
答案 10 :(得分:1)
您可以使用:
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();
答案 11 :(得分:0)
许多DB都有临时表的概念,即使您没有临时表,也可以始终生成具有唯一名称的临时表,并在完成后将其删除。虽然创建和删除表的开销很大,但对于非常大的操作,或者在将数据库用作本地文件或内存(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+");");
请注意,我的表使用的字段是动态创建的。
如果您能够重用该表,这将更有效。
答案 12 :(得分:0)
尝试使用此代码
String ids[] = {"182","160","183"};
StringBuilder builder = new StringBuilder();
for( int i = 0 ; i < ids.length; i++ ) {
builder.append("?,");
}
String sql = "delete from emp where id in ("+builder.deleteCharAt( builder.length() -1 ).toString()+")";
PreparedStatement pstmt = connection.prepareStatement(sql);
for (int i = 1; i <= ids.length; i++) {
pstmt.setInt(i, Integer.parseInt(ids[i-1]));
}
int count = pstmt.executeUpdate();
答案 13 :(得分:-1)
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();
}
}
答案 14 :(得分:-1)
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(")");