Hi,
First of all, thank you for this great library.
The error that I am experiencing is the following:
Binding an empty List
No argument factory registered for '[]' of type java.util.List
Can you provide more context? What SQL are you executing? Which database vendor?
Hi @qualidafial ,
Sql: select * from invoice where invoice.id IN :ids
Database vendor: SQL Server 2017
Okay, that makes sense now.
JDBC is a bit weird when working with arrays--you have to know the data type name used by the database vendor. To help Jdbi connect the dots you have to register the array element type name per Java class name:
Jdbi jdbi = Jdbi.create(...);
...
jdbi.registerArrayType(int.class, "integer");
jdbi.registerArrayType(Integer.class, "integer");
With that said, we could improve our exception messages to point users in the right direction.
@qualidafial Bro, this is top notch, thank you very much!
:+1: Just a heads up, we could add a SQL Server plugin to Jdbi if you can point me to some documentation so I know what custom data types the driver supports, and what the mappings are between Java types and the JDBC data type names.
Deal.
Basic data types:
https://docs.microsoft.com/en-us/sql/connect/jdbc/using-basic-data-types
Advanced data types:
https://docs.microsoft.com/en-us/sql/connect/jdbc/using-advanced-data-types
@qualidafial FYI, apparently the IN statement with a prepared SqlArray is not supported by the SQL Server JDBC driver
You could alternatively use @BindList
:
@SqlQuery("select * from invoice where invoice.id IN (<ids>)")
Set<Invoice> getInvoices(@BindList("ids") List<Integer> ids);
Be aware that different database vendors might place limits on the number of items allowed in a comma separated WHERE column IN (a, b, c, ...)
clause.
Apparently SQL server only supports single value parameters, so I created the array manually, however this is unsafe for String arrays and can introduce SQL Injection.
default List<LotInvoiceRef> getInvoices(List<Long> invoiceIds) {
final List<LotInvoiceRef> lotInvoiceRefs = invoiceIds.stream()
.map(String::valueOf)
.reduce((a, b) -> a + "," + b)
.map(this::getAllInvoiceRefs)
.orElse(new ArrayList<>());
return lotInvoiceRefs;
}
@georgerb please see my last comment, which should protect against SQL injection
@qualidafial I love the @BindList aproach but I need a way to allow empty lists for the parameter which @BindList explicitly forbids.
@qualidafial nvm, A quick dig into the annotation and I used this: @BindList(value = "invoiceIds", onEmpty = BindList.EmptyHandling.NULL)
and it worked perfectly
Again, this is top notch. Is there a way that we can contribute? at least helping you guys with the documentation.
Thanks, you're very kind. We :heart: pull requests!
From this conversation alone I gather three possible improvements:
@BindList
(and maybe @DefineList
while we're at it).If you'd be so kind to submit a separate issue targeted to each of the above. Then we can close this one and track each one in isolation.
And if you have some idea for improving Jdbi that you'd like to run with, please do! The JPA, Kotlin, and Vavr plugins, as well as many elements of Core and SQL Object were all submissions from Jdbi users.
@qualidafial done. #993
Thanks! Closing this issue in favor of #993
Most helpful comment
Okay, that makes sense now.
JDBC is a bit weird when working with arrays--you have to know the data type name used by the database vendor. To help Jdbi connect the dots you have to register the array element type name per Java class name: