Jdbi指南
A Guide to Jdbi
1.简介
在本文中,我们将研究如何使用jdbi查询关系数据库。
Jdbi是一个开源Java库(Apache许可),它使用lambda表达式和反射来提供比JDBC更友好,更高级的接口来访问数据库。
但是,Jdbi不是ORM。 即使它具有可选的SQL Object映射模块,也没有与附加对象,数据库独立层以及典型ORM的所有其他细节的会话。
2. Jdbi设置
Jdbi被组织成一个核心和几个可选模块。
首先,我们只需要在依赖项中包含核心模块即可:
|
1
2 3 4 5 6 7 |
<dependencies>
<dependency> <groupId>org.jdbi</groupId> <artifactId>jdbi3-core</artifactId> <version>3.1.0</version> </dependency> </dependencies> |
在本文的整个过程中,我们将展示使用HSQL数据库的示例:
|
1
2 3 4 5 6 |
<dependency>
<groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <version>2.4.0</version> <scope>test</scope> </dependency> |
我们可以在Maven Central上找到最新版本的jdbi3-core,HSQLDB和其他Jdbi模块。
3.连接到数据库
首先,我们需要连接到数据库。 为此,我们必须指定连接参数。
起点是Jdbi类:
|
1
|
Jdbi jdbi = Jdbi.create("jdbc:hsqldb:mem:testDB","sa","");
|
在这里,我们指定了连接URL,用户名,当然还有密码。
3.1。 附加参数
如果需要提供其他参数,则可以使用接受Properties对象的重载方法:
|
1
2 3 4 |
Properties properties = new Properties();
properties.setProperty("username","sa"); properties.setProperty("password",""); Jdbi jdbi = Jdbi.create("jdbc:hsqldb:mem:testDB", properties); |
在这些示例中,我们已将Jdbi实例保存在本地变量中。 那是因为我们将使用它来将语句和查询发送到数据库。
实际上,仅调用create并不会建立与数据库的任何连接。 它只是保存连接参数供以后使用。
3.2。 使用数据源
如果像通常那样使用数据源连接到数据库,则可以使用适当的创建重载:
|
1
|
Jdbi jdbi = Jdbi.create(datasource);
|
3.3。 处理手柄
到数据库的实际连接由Handle类的实例表示。
使用句柄并使其自动关闭的最简单方法是使用lambda表达式:
|
1
2 3 |
jdbi.useHandle(handle -> {
doStuffWith(handle); }); |
当我们不必返回值时,我们调用useHandle。
否则,我们使用withHandle:
|
1
2 3 |
jdbi.withHandle(handle -> {
return computeValue(handle); }); |
尽管不建议这样做,也可以手动打开连接句柄。 在这种情况下,我们必须在完成后将其关闭:
|
1
2 3 4 |
Jdbi jdbi = Jdbi.create("jdbc:hsqldb:mem:testDB","sa","");
try (Handle handle = jdbi.open()) { doStuffWith(handle); } |
幸运的是,如我们所见,Handle实现了Closeable,因此可以与try-with-resources一起使用。
4.简单的陈述
现在我们知道了如何获得连接,让我们看看如何使用它。
在本节中,我们将创建一个简单的表,我们将在整篇文章中使用该表。
要将诸如create table之类的语句发送到数据库,我们使用execute方法:
|
1
2 3 |
handle.execute(
"create table project" +"(id integer identity, name varchar(50), url varchar(100))"); |
execute返回受语句影响的行数:
|
1
2 3 4 5 |
int updateCount = handle.execute(
"insert into project values" +"(1, 'tutorials', 'github.com/eugenp/tutorials')"); assertEquals(1, updateCount); |
实际上,执行只是一种方便的方法。
在后面的部分中,我们将介绍更复杂的用例,但在此之前,我们需要学习如何从数据库中提取结果。
5.查询数据库
从数据库产生结果的最直接的表达式是SQL查询。
要使用Jdbi句柄发出查询,我们至少必须:
-
创建查询
-
选择如何代表每一行
-
遍历结果
现在,我们将研究以上各点。
5.1。 创建查询
毫不奇怪,Jdbi将查询表示为Query类的实例。
我们可以从句柄中获得一个:
|
1
|
Query query = handle.createQuery("select * from project");
|
5.2。 映射结果
Jdbi从JDBC ResultSet中抽象出来,后者具有相当繁琐的API。
因此,它提供了几种访问查询或返回结果的其他语句所产生的列的可能性。 现在,我们将看到最简单的那些。
我们可以将每一行表示为地图:
|
1
|
query.mapToMap();
|
映射的键将是选定的列名称。
或者,当查询返回单个列时,我们可以将其映射到所需的Java类型:
|
1
|
Jdbi具有许多常见类的内置映射器。 某些库或数据库系统专用的模块在单独的模块中提供。
当然,我们也可以定义和注册我们的映射器。 我们将在后面的部分中讨论它。
最后,我们可以将行映射到bean或其他自定义类。 同样,我们将在专用部分中看到更高级的选项。
5.3。 遍历结果
一旦决定了如何通过调用适当的方法来映射结果,我们将收到一个ResultIterable对象。
然后,我们可以使用它遍历结果,一次一行。
在这里,我们将介绍最常见的选项。
我们只能将结果累积在一个列表中:
|
1
|
或到另一个Collection类型:
|
1
|
或者我们可以将结果作为流进行迭代:
|
1
2 3 |
在这里,为了清楚起见,我们显式地键入了流变量,但这不是必须的。
5.4。 获得单一结果
作为一种特殊情况,当我们只希望或只对一行感兴趣时,可以使用几种专用方法。
如果我们最多想要一个结果,则可以使用findFirst:
|
1
|
如我们所见,它返回一个Optional值,仅当查询返回至少一个结果时才存在。
如果查询返回多个行,则仅返回第一行。
如果相反,我们只需要一个结果,则使用findOnly:
最后,如果结果为零或不止一个,则findOnly抛出IllegalStateException。
6.绑定参数
通常,查询具有固定部分和参数化部分。 这具有几个优点,包括:
-
安全性:通过避免字符串连接,我们可以防止SQL注入
-
轻松:我们不必记住复杂数据类型(例如时间戳)的确切语法
-
性能:查询的静态部分可以解析一次并缓存
Jdbi支持位置参数和命名参数。
我们在查询或语句中插入位置参数作为问号:
|
1
2 |
Query positionalParamsQuery =
handle.createQuery("select * from project where name = ?"); |
命名参数改为以冒号开头:
|
1
2 |
Query namedParamsQuery =
handle.createQuery("select * from project where url like :pattern"); |
在任何一种情况下,要设置参数的值,我们使用bind方法的一种变体:
|
1
2 |
positionalParamsQuery.bind(0,"tutorials");
namedParamsQuery.bind("pattern","%github.com/eugenp/%"); |
请注意,与JDBC不同,索引从0开始。
6.1。 一次绑定多个命名参数
我们还可以使用一个对象将多个命名参数绑定在一起。
假设我们有一个简单的查询:
|
1
2 3 4 5 |
Query query = handle.createQuery(
"select id from project where name = :name and url = :url"); Map<String, String> params = new HashMap<>(); params.put("name","REST with Spring"); params.put("url","github.com/eugenp/REST-With-Spring"); |
然后,例如,我们可以使用地图:
|
1
|
query.bindMap(params);
|
或者我们可以通过多种方式使用对象。 例如,在这里,我们绑定一个遵循JavaBean约定的对象:
|
1
|
query.bindBean(paramsBean);
|
但是我们也可以绑定对象的字段或方法。 有关所有受支持的选项的信息,请参见Jdbi文档。
7.发布更复杂的声明
既然我们已经看到了查询,值和参数,那么我们可以回到语句并应用相同的知识。
回想一下我们前面看到的execute方法只是一个方便的快捷方式。
实际上,类似于查询,DDL和DML语句表示为类Update的实例。
我们可以通过在句柄上调用方法createUpdate获得一个:
|
1
2 |
Update update = handle.createUpdate(
"INSERT INTO PROJECT (NAME, URL) VALUES (:name, :url)"); |
然后,在Update上,我们具有查询中具有的所有绑定方法,因此第6节同样适用于更新。
当我们调用,惊讶并执行时,将执行语句:
|
1
|
int rows = update.execute();
|
正如我们已经看到的,它返回受影响的行数。
7.1。 提取自动增量列值
作为一种特殊情况,当我们有一个包含自动生成的列(通常是自动递增或序列)的insert语句时,我们可能想要获取生成的值。
然后,我们不调用execute,而是executeAndReturnGeneratedKeys:
|
1
2 3 4 |
Update update = handle.createUpdate(
"INSERT INTO PROJECT (NAME, URL)" +"VALUES ('tutorials', 'github.com/eugenp/tutorials')"); ResultBearing generatedKeys = update.executeAndReturnGeneratedKeys(); |
ResultBearing与我们之前看到的Query类实现的接口相同,因此我们已经知道如何使用它:
|
1
2 |
generatedKeys.mapToMap()
.findOnly().get("id"); |
8.交易
每当我们必须将多个语句作为单个原子操作执行时,就需要进行事务处理。
与连接句柄一样,我们通过调用带有闭包的方法来引入事务:
|
1
2 3 |
handle.useTransaction((Handle h) -> {
haveFunWith(h); }); |
并且,与句柄一样,当关闭返回时,事务将自动关闭。
但是,我们必须在返回之前提交或回滚事务:
|
1
2 3 4 |
handle.useTransaction((Handle h) -> {
h.execute("..."); h.commit(); }); |
但是,如果从闭包中抛出异常,Jdbi将自动回滚事务。
与句柄一样,如果要从闭包中返回某些内容,我们有一个专用的方法inTransaction:
|
1
2 3 4 5 |
handle.inTransaction((Handle h) -> {
h.execute("..."); h.commit(); return true; }); |
8.1。 手动交易管理
尽管通常不建议这样做,但我们也可以手动开始和关闭事务:
|
1
2 3 4 |
handle.begin();
// ... handle.commit(); handle.close(); |
9.结论和进一步阅读
在本教程中,我们介绍了Jdbi的核心:查询,语句和事务。
我们遗漏了一些高级功能,例如自定义行和列映射以及批处理。
我们也没有讨论任何可选模块,尤其是SQL Object扩展。
Jdbi文档中详细介绍了所有内容。
所有这些示例和代码段的实现都可以在GitHub项目中找到–这是一个Maven项目,因此应该很容易直接导入和运行。

浙公网安备 33010602011771号