Jdbi: Binding List<Integer> or List<Long> by using @Bind annotation throws exception

Created on 27 Dec 2017  ·  16Comments  ·  Source: jdbi/jdbi

Hi,

First of all, thank you for this great library.
The error that I am experiencing is the following:

Binding an empty List or List by using the @Bind annotation throws this:

No argument factory registered for '[]' of type java.util.List

question

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:

Jdbi jdbi = Jdbi.create(...);
...
jdbi.registerArrayType(int.class, "integer");
jdbi.registerArrayType(Integer.class, "integer");

All 16 comments

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.

@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:

  • Add developer guide section for @BindList (and maybe @DefineList while we're at it).
  • Flesh out developer guide section for SQL Arrays.
  • Provide informative error messages when a SQL array type needs to be registered.

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

Was this page helpful?
0 / 5 - 0 ratings