最近我正在研究一些更像 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。它肯定会进入我的工具箱。