Jdbi: Developer guide: v2 to v3 migration

Created on 25 Jan 2017  ·  15Comments  ·  Source: jdbi/jdbi

Possibly use JDBI - Evolving An Open Source Project presentation for some material

doc

Most helpful comment

Notes from migrating a small project:

Renamed classes (so not quite as simple as deleting imports and letting the IDE fix it):

  • DBI -> Jdbi
  • IDBI -> Jdbi
  • DBIException -> JdbiException

The constructors for Jdbi have been replaced with a create() factory method.

ResultSetMapper is replaced with RowMapper and the map method no longer has the row index. A class named ResultSetMapper exists in Jdbi 3, but it serves a different purpose. @Mapper is replaced with @UseRowMapper. registerMapper() on Jdbi is replaced with registerRowMapper().

@BindIn is replaced with @BindList and no longer requires StringTemplate.

With the default Jdbi templating, angle brackets are not quoted, which means that IntelliJ understands the syntax after you configure the Parameter Pattern under Tools -> Database -> User Patterns.

Query no longer has a default type of Map and thus list() cannot be called on it directly. Call mapToMap() before calling list().

TransactionStatus no longer exists.

TransactionConsumer.useTransaction() only takes a Handle now, so the TransactionStatus argument needs to be removed when using this with the useTransaction() methods on Jdbi or Handle.

TransactionCallback.inTransaction() only takes a Handle now, so the TransactionStatus argument needs to be removed when using this with the inTransaction() methods on Jdbi or Handle.

CallbackFailedException no longer exists. The various functional interfaces such as HandleConsumer, HandleCallback, TransactionalConsumer, and TransactionalCallback, can now throw any exception type (but restricted using generics to avoid needless checked exception handling).

SQL Object support is no longer available by default. It must be registered every created Jdbi instance.

IntelliJ's Migrate Refactor was helpful to kickstart the migration.

All 15 comments

Notes from migrating a small project:

Renamed classes (so not quite as simple as deleting imports and letting the IDE fix it):

  • DBI -> Jdbi
  • IDBI -> Jdbi
  • DBIException -> JdbiException

The constructors for Jdbi have been replaced with a create() factory method.

ResultSetMapper is replaced with RowMapper and the map method no longer has the row index. A class named ResultSetMapper exists in Jdbi 3, but it serves a different purpose. @Mapper is replaced with @UseRowMapper. registerMapper() on Jdbi is replaced with registerRowMapper().

@BindIn is replaced with @BindList and no longer requires StringTemplate.

With the default Jdbi templating, angle brackets are not quoted, which means that IntelliJ understands the syntax after you configure the Parameter Pattern under Tools -> Database -> User Patterns.

Query no longer has a default type of Map and thus list() cannot be called on it directly. Call mapToMap() before calling list().

TransactionStatus no longer exists.

TransactionConsumer.useTransaction() only takes a Handle now, so the TransactionStatus argument needs to be removed when using this with the useTransaction() methods on Jdbi or Handle.

TransactionCallback.inTransaction() only takes a Handle now, so the TransactionStatus argument needs to be removed when using this with the inTransaction() methods on Jdbi or Handle.

CallbackFailedException no longer exists. The various functional interfaces such as HandleConsumer, HandleCallback, TransactionalConsumer, and TransactionalCallback, can now throw any exception type (but restricted using generics to avoid needless checked exception handling).

SQL Object support is no longer available by default. It must be registered every created Jdbi instance.

IntelliJ's Migrate Refactor was helpful to kickstart the migration.

Thanks @electrum for putting this together! This will be a big help

Some additional notes from reviewing my presentation:

  • Artifacts renamed org.jdbi:jdbi -> org.jdbi:jdbi3, :jdbi3-sqlobject, :jdbi3-guava, etc
  • Core package changed: org.skife.jdbi.v2 -> org.jdbi.v3. This means that v2 and v3 can coexist on a project while you migrate
  • Renamed: GetHandle -> SqlObject
  • v3 argument and mapper factories use java.lang.reflect.Type, whereas v2 used java.lang.Class. This means that v3 is capable of handling complex generic type signatures that v2 could not.
  • v3 argument and mapper factories and single-method functional interfaces that return an optional. v2 had separate accepts() and build() methods.
  • SQL Object types in v3 must only be public interfaces--no classes. Method return types must likewise be public. This is due to SQL Object implementation switching from CGLIB to java.lang.reflect.Proxy, which only supports interfaces.
  • @Bind annotations on SQL Object method parameters can be made optional, by compiling your code with the -parameters flag enabled.
  • On-demand SQL objects don't play nice with methods that return Iterables. On-demand objects strictly close the handle after each method call, and no longer "hold the door open" for you to finish consuming the iterable as they did in v2. This forecloses a major source of connection leaks.
  • SQL objects are no longer closeable -- they are either on-demand, or their lifecycle is tied to the lifecycle of the handle they were attached to.
  • StatementLocator interface is removed from core. All core statements expect to receive the actual SQL now. A similar concept, SqlLocator was added but only in the realm of SQL Objects.

Another one: StatementRewriter has been refactored into TemplateEngine and SqlParser.

Very important to add to the list is the behavior of select. It went from this:

List<Map<String, Object>> rs = h.select("select id, name from something");

To this:

List<Map<String, Object>> rs = h.select("select id, name from something").mapToMap().list();

I wish the extra power and flexibility and safety of v3 didn't come with the price of verbosity.

Hi @javajosh , we didn't consider that particular one to be too bad because we are trying to discourage mapping to Map objects. If nothing else, the rules around case sensitivity for the keys are confusing. Is there some reason you must emit a Map rather than creating your own defined type? I personally will always write little result classes (using constructor / field / property mappers and binders) and always avoid using Map. Do you have some use case where it is very convenient?

Hi @stevenschlansker - I am coming at it from the perspective of "developers first experience". The above code is taken from you're own v2 tutorial, which I think is admirably minimalist. And indeed, with a JVM-hosted language like Groovy, which has a javascript-like map builtin, you'd probably see a non-typed result more often.

@javajosh what do you think of https://github.com/jdbi/jdbi/pull/925 ?

@stevenschlansker Did you mean #928 ?

@stevenschlansker Definitely an improvement! But it's still more verbose than v2. :) This is in addition to the greater verbosity in maven, etc. I just don't want to see JDBI go the way of JUnit which struggled to get people to adopt v4 because it had some nice features, but was far more complicated than v3 and v3 was "good enough". I have that same feeling about the "withHandle" syntax (although I understand your instinct to eliminate all dangling handles).

@javajosh You still have Jdbi.open() if you want greater control, although we do recommend you use it inside a try-with-resources block for safety.

Starting work on the v2 to v3 migration docs.

@javajosh Coming back to the method ResultBearing.list() that returns a List<Map<String,Object>>: I'm not sure adding this was a good idea, especially this late in the release cycle.

I'm empathetic to your concerns about verbosity, but that verbosity serves the purpose of clarifying developer intent:

handle.createQuery(sql)
    .mapToMap()
    .list()

is clearer to the reader than:

handle.createQuery(sql)
    .list()

It's a delicate thing, and I don't envy your position having to choose. JDBI is a nice library, and I'm glad to simply have registered my concerns about verbosity; whether it makes sense to add a convenience method in this specific case, I'm not sure and would tend to trust your judgement over mine, since I'm not seriously involved in the project! Thanks for listening!

Please let us know if you come up with a concrete example of how it's better with a shorter name, or a showcase of how we might improve it 😄

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dhardtke picture dhardtke  ·  3Comments

electrum picture electrum  ·  3Comments

qualidafial picture qualidafial  ·  3Comments

anjeyy picture anjeyy  ·  3Comments

Romqa picture Romqa  ·  5Comments