10

Does JDBI support binding of enum types via annotation?

For example, assuming a DAO that included a method:

@SqlQuery("select count(*) from answer a where a.foo = :foo")
Long someSqlQuery(@Bind("foo") Foo foo);

And, foo equal to Foo.BAR, could I expect a query:

select count(*) from answer a where a.foo = 'BAR'

If so, is toString() utilized to determine what is substituted?

Further, Does JDBI allow for using @Bind with any type that extends Object? And again, if so, is toString() used?

vpiTriumph
  • 3,116
  • 2
  • 27
  • 39
  • As a follow on comment, my experience using 2.5.1 is that `Enum` does behave in the way I describe above. That said, I've also seen some strange behavior, around this, possibly because of conflicting JDBI versions locally. I was hoping someone can point me to the documentation related to this feature (I only see documentation demonstrating `Strings`, primitive types, and `Date`) and an indication of which version allowed for using `@Bind` with `enum`. – vpiTriumph Apr 29 '15 at 19:36

2 Answers2

10

According to the source code Enum.name() is used, not toString()

If an object is bound, jdbi uses setObject(Object object) of the jdbc driver you're using. In my experience with PostgreSQL for example, it successfully binds Maps to hstore and arrays to postgreSQL arrays because that's what PostgreSQL's jdbc driver does.

If you want to handle a specific kind of objects in a specific way, you can implement ArgumentFactorys (for which there seems to be no documentation but an example here in Stack Overflow) or BinderFactorys (which I've just discovered now.)

Community
  • 1
  • 1
Natan
  • 2,816
  • 20
  • 37
  • Quick note: You'll need JDBI 2.56 or later to successfully bind Enums. It wasn't until this point that `EnumArgument` was added as one of the default `Argument` types. So prior to this you'll need to create the functionality yourself be creating an `EnumArgument` and an `EnumArgumentFactory`. – vpiTriumph Nov 24 '15 at 16:57
  • Ah yes. Actually, before that I had a lot of `getEnumName()` hacks to get around it which I have removed later. – Natan Nov 24 '15 at 20:32
1

To address your "arbitrary types" question, you can implement a BinderFactory to bind to anything you want.

@BindingAnnotation(UserBinder.UserBinderFactory.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
public @interface UserBinder {
    public static class UserBinderFactory implements BinderFactory {

        @Override
        public Binder build(Annotation annotation) {
            return new Binder<UserBinder, User>() {
                @Override
                public void bind(SQLStatement<?> q, UserBinder bind, User arg) {
                    q.bind("userId", arg.getUserId());
                    q.bind("uuid", arg.getUuid());
                    q.bind("openId", arg.getOpenId());
                    q.bind("givenName", arg.getGivenName());
                    // etc.
                }
            };
        }
    }
}

This is most useful if you have complex types that you want stored in a particular way, e.g. binary data, or collections that you want turned into CSVs or only stored in separate mapping tables. Then you use your new fancy binder like so:

@SqlUpdate(
        "INSERT INTO user (openId,givenName,uuid," +
        // etc.
        ") VALUES (:openId,:givenName,:uuid" +
        // etc.
        ")"
)
public abstract int createUser(
        @UserBinder User user
);

You can also use an @BindBean annotation, which will automatically look up fields on an object by the bind parameter name (e.g. it can map a query placeholder ":userId" to the getUserId() method).

Patrick M
  • 10,547
  • 9
  • 68
  • 101
  • 1
    Nice answer, but providing an example that doesn't actually do anything beyond what the default `BindBean` implementation does is a bit confusing. – Madbreaks Jul 30 '15 at 21:41