JDBI,一个不错的 Spring JDBC 替代品

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

最近我正在研究一些更像 Java EE 的 Spring Boot 替代品,例如 WildFly Swarm 和 Dropwizard。在查看 Dropwizard 时,我注意到他们正在使用我以前从未遇到过的 JDBC 访问库:JDBI。通常,我对普通 JDBC 访问的第一反应是使用 Spring 提供的 JdbcTemplate 类,但最近我遇到了一些小问题(例如,它无法以简单的方式处理获取批量插入生成的密钥).我一直对尝试其他解决方案很感兴趣,因此我开始了一个使用 JDBI 的小型 PoC 项目。我很惊喜。

JDBI 是 JDBC 之上的一个抽象层,很像 JdbcTemplate。它共享 JdbcTemplate 提供的大部分(如果不是全部)功能。有趣的是除此之外它还提供了什么。我会谈谈其中的几个。

内置支持 SQL 中的命名参数和索引参数

大多数人都知道您有 JdbcTemplate NamedParameterJdbcTemplate 。前者支持索引参数(使用 ? ),而后者支持命名参数(使用 :paramName )。 JDBI 实际上内置了对这两种机制的支持,不需要对这两种机制进行不同的实现。 JDBI 在执行查询时使用参数绑定的概念,您可以绑定到索引或名称。这使得 API 非常容易学习。

流畅的 API 功能

JDBI 有一个非常流畅的 API。例如,使用 JdbcTemplate 进行这个简单的查询:


 Map<String, Object> params = new HashMap<>();
params.put("param", "bar");
return jdbcTemplate.queryForObject("SELECT bar FROM foo WHERE bar = :param", params, String.class);

对于 JDBI,这将导致(jdbi 是 JDBI 类的一个实例):


 Map<String, Object> params = new HashMap<>();
params.put("param", "bar");
return jdbcTemplate.queryForObject("SELECT bar FROM foo WHERE bar = :param", params, String.class);

准确地说,这两种方法在功能上并不完全等同。如果没有结果或返回多个结果,JdbcTemplate 版本将抛出异常,而 JBDI 版本将在相同情况下返回 null 或第一个项目。因此,要在功能上等效,如果您想要相同的行为,则必须添加一些逻辑,但您明白了。

另请注意,您需要在完成处理后关闭 Handle 实例。如果您不想这样做,则需要使用回调行为。由于闭包,这个 hwover 在使用 Java 8 时非常干净:


 Map<String, Object> params = new HashMap<>();
params.put("param", "bar");
return jdbcTemplate.queryForObject("SELECT bar FROM foo WHERE bar = :param", params, String.class);

自定义参数绑定

我们都在 JdbcTemplate 中看到过这个:


 Map<String, Object> params = new HashMap<>();
params.put("param", "bar");
return jdbcTemplate.queryForObject("SELECT bar FROM foo WHERE bar = :param", params, String.class);

参数类型非常限于普通 JDBC 中默认支持的那些,这意味着简单类型、String 和 java.sql 类型。使用 JDBI,您可以通过实现自定义 Argument 类来绑定自定义参数类。在上面的例子中,这看起来像这样:


 Map<String, Object> params = new HashMap<>();
params.put("param", "bar");
return jdbcTemplate.queryForObject("SELECT bar FROM foo WHERE bar = :param", params, String.class);

使用 JBDI,您可以执行以下操作:


 Map<String, Object> params = new HashMap<>();
params.put("param", "bar");
return jdbcTemplate.queryForObject("SELECT bar FROM foo WHERE bar = :param", params, String.class);

但是,您也可以注册一个 ArgumentFactory ,它在需要时构建所需的 Argument 类,这样您就可以直接绑定 LocalDateTime 值。

自定义 DAO

我们这些有幸使用 Spring Data 的人都知道它支持一个非常好的特性:存储库。但是,如果您使用的是 JDBC,那您就倒霉了,因为此功能不适用于普通 JDBC。

然而,JDBI 有一个非常相似的特性。您可以像 Spring Data 一样编写接口并注释方法。例如,您可以创建这样的界面:


 Map<String, Object> params = new HashMap<>();
params.put("param", "bar");
return jdbcTemplate.queryForObject("SELECT bar FROM foo WHERE bar = :param", params, String.class);

然后,您可以使用 JDBI 创建该接口的具体实例并使用这些方法,就像它们已经实现一样。


 Map<String, Object> params = new HashMap<>();
params.put("param", "bar");
return jdbcTemplate.queryForObject("SELECT bar FROM foo WHERE bar = :param", params, String.class);

onDemand 创建实例时,您不需要在完成后关闭存储库实例。这也意味着您可以将它作为 Spring Bean 重用!

其他事情和结论

还有无数的特性我还没有接触过,比如对象绑定、简单的批处理、mixins 和外部化的命名查询。这些特性与我之前提到的特性相结合,使 JDBI 成为 JdbcTemplate 的一个引人注目的替代品。由于它与 Spring 的事务系统结合使用效果很好,因此开始使用它几乎不需要付出任何努力。 JDBI 中的大多数对象都是线程安全的,并且可以作为单例重用,因此可以将它们定义为 Spring bean 并在需要的地方注入。

如果您正在研究做普通的 JDBC 工作并且需要不同于 JDBC 模板的东西,请查看 JDBI。它肯定会进入我的工具箱。