Parameterized IN clause with JDBI and PostgreSQL
I had a problem when trying to bind a Java array to an IN clause with JDBI. Here are the solutions I found working.
The problem
I was hoping the following code snipet would work with JDBI. It truns out to be not working.
try (Handle handle = dbi.open()) {
return handle.createQuery("SELECT COUNT(1) FROM company WHERE name IN ( :company_names )");
.bind("company_names", new String[]{"aol", "msft", "yahoo"})
.map(IntegerColumnMapper.PRIMITIVE)
.first();
}The reason, I guess, is that the array type is not equivalent to a table
expression, which is conceptually a set. Furthermore, for the JDBI array
binding to work, the parameter value to be bound needs to be of the type
java.sql.Array.
The solutions
Following the above discussions, it is easier to understand why the following code works.
try (Handle handle = dbi.open()) {
return handle.createQuery("SELECT COUNT(1) FROM company WHERE name IN (SELECT * FROM UNNEST(:company_names))");
.bind("company_names", handle.getConnection().createArrayOf("text", new String[]{"aol", "msft", "yahoo"}))
.map(IntegerColumnMapper.PRIMITIVE)
.first();
}Note the SELECT * FROM UNNEST(:company_names) part to convert the sql array
passed in into a table expression, and the
handle.getConnection().createArrayOf("text", new String[]{"aol", "msft", "yahoo"})
part to convert a normal Java array into a java.sql.Array of text type.
As an alternative, this also works:
try (Handle handle = dbi.open()) {
return handle.createQuery("SELECT COUNT(1) FROM company WHERE ARRAY[name] && :company_names");
.bind("company_names", handle.getConnection().createArrayOf("text", new String[]{"aol", "msft", "yahoo"}))
.map(IntegerColumnMapper.PRIMITIVE)
.first();
}The ARRAY[name] part converts the name column value into an array with a
single element, and then tests for intersection with the array passed in as a
parameter.
The above workarounds only work for PostgreSQL. A more generic (and complex) solution would be to generate the sql string dynamically (e.g., using a template engine), then bind the array elements one by one in a loop.