6

TLDR; The JDBI @BindBean annotation generates an IllegalAccessException with AutoValue generated types because the generated types are package private and by default can't be accessed by default using reflection.

Is JDBI inflexible or is there a workaround via AutoValue? (Full questions below)

Quick Background

I'm attempting to use the JDBI @BindBean annotation with a type whose source is generated using AutoValue.

package com.example;

@AutoValue
public abstract class Foo {
  public String getBar();
}

The issue is that the generated code looks like:

package com.example;

@AutoValue
class AutoValue_Foo extends Foo {
  private final String bar;

  @Override
  public String getBar() {
    return this.bar;
  }

  // toString, equals, hashCode
}

Notice the class is package private!

Now if I attempt to use @BindBean, for example:

@SqlQuery("select * from baz where bar = :foo.bar")
Condition find(@BindBean("foo") Foo foo);

Because AutoValue_Foo is package private, and BindBeanFactory uses reflection, if an attempt is made to call find with an AutoValue_Foo type, the result is:

java.lang.IllegalAccessException: ... can not access a member of class com.example.Foo with modifiers "public"

The relevant JDBI code is here. I understand from a Java reflection perspective, this could be resolved using setAccessible(true) but that would require a PR to JDBI.

So the questions are as follow:

  1. Is there a way to restructure my code where I can bind a Foo of type AutoValue_Foo using @BindBean without creating a new JDBI mapper?

  2. Is there a way to have @AutoValue generate classes that are public. I understand why this would generally not be desirable (push people to use the interface and not the implementation).

  3. Is the BindBeanFactory too inflexible? Should it utilize setAccessible(true) on methods that are otherwise available outside of their originating package?

vpiTriumph
  • 3,116
  • 2
  • 27
  • 39
  • You could use Lombok's `@Data` (https://projectlombok.org/features/Data.html) instead of `@AutoValue`. It generates the boilerplate inside your class, instead of making an implementation type. Might not be exactly what you're looking for, but it's working great for me. – Jorn Nov 30 '15 at 22:38
  • @Jorn interesting to know Lombok works. I'm unfortunately locked into AutoValue but this may be useful for other engineers who have more flexibility. – vpiTriumph Dec 01 '15 at 20:26
  • Please submit a PR to JDBI. I will shepherd it through. – qualidafial Dec 02 '15 at 17:35

1 Answers1

5

Version 2.71 of JDBI will include the ability to specify a type token to @BindBean using the type field. This type token will allow for specifying the type used to make the reflective call against the provided argument.

@SqlQuery("select * from baz where bar = :foo.bar") Condition find(@BindBean(value="foo", type=Foo.class) Foo foo);

Using this technique you can eliminate the IllegalAccessException described above.

vpiTriumph
  • 3,116
  • 2
  • 27
  • 39