PreparedStatement with list of parameters in a IN clause [duplicate]
What I do is to add a "?" for each possible value.
var stmt = String.format("select * from test where field in (%s)",
values.stream()
.map(v -> "?")
.collect(Collectors.joining(", ")));
Alternative using StringBuilder
(which was the original answer 10+ years ago)
List values = ...
StringBuilder builder = new StringBuilder();
for( int i = 0 ; i < values.size(); i++ ) {
builder.append("?,");
}
String placeHolders = builder.deleteCharAt( builder.length() -1 ).toString();
String stmt = "select * from test where field in ("+ placeHolders + ")";
PreparedStatement pstmt = ...
And then happily set the params
int index = 1;
for( Object o : values ) {
pstmt.setObject( index++, o ); // or whatever it applies
}
You could use setArray
method as mentioned in the javadoc below:
Code:
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();
You can't replace ?
in your query with an arbitrary number of values. Each ?
is a placeholder for a single value only. To support an arbitrary number of values, you'll have to dynamically build a string containing ?, ?, ?, ... , ?
with the number of question marks being the same as the number of values you want in your in
clause.
You don't want use PreparedStatment with dynamic queries using IN clause at least your sure you're always under 5 variable or a small value like that but even like that I think it's a bad idea ( not terrible, but bad ). As the number of elements is large, it will be worse ( and terrible ).
Imagine hundred or thousand possibilities in your IN clause :
-
It's counter-productive, you lost performance and memory because you cache every time a new request, and PreparedStatement are not just for SQL injection, it's about performance. In this case, Statement is better.
-
Your pool have a limit of PreparedStatment ( -1 defaut but you must limit it ), and you will reach this limit ! and if you have no limit or very large limit you have some risk of memory leak, and in extreme case OutofMemory errors. So if it's for your small personnal project used by 3 users it's not dramatic, but you don't want that if you're in a big company and that you're app is used by thousand people and million request.
Some reading. IBM : Memory utilization considerations when using prepared statement caching
You need jdbc4 then you can use setArray!
In my case it didn't worked, as the UUID Datatype in postgres seems to still have its weak spots, but for the usual types it works.
ps.setArray(1, connection.createArrayOf("$VALUETYPE",myValuesAsArray));
Of course replace $VALUETYPE and myValuesAsArray with the correct values.
Remark following Marks comment:
Your database and the driver needs to support this! I tried Postgres 9.4 but I think this has been introduced earlier. You need a jdbc 4 driver, otherwise setArray won't be available. I used the postgresql 9.4-1201-jdbc41 driver that ships with spring boot