• Jdbi 指南


    1.简介

    在本文中,我们将了解如何使用jdbi查询关系数据库。

    Jdbi 是一个开源 Java 库(Apache 许可),它使用lambda 表达式反射来提供比JDBC更友好、更高级别的接口来访问数据库。

    然而,Jdbi 不是 ORM。即使它有一个可选的 SQL 对象映射模块,它也没有一个带有附加对象的会话、一个数据库独立层,以及典型 ORM 的任何其他花里胡哨。

    2.Jdbi设置

    Jdbi 被组织成一个核心模块和几个可选模块。

    首先,我们只需在依赖项中包含核心模块:

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.jdbigroupId>
    4. <artifactId>jdbi3-coreartifactId>
    5. <version>3.1.0version>
    6. dependency>
    7. dependencies>

    在本文中,我们将展示使用 HSQL 数据库的示例:

    1. <dependency>
    2. <groupId>org.hsqldbgroupId>
    3. <artifactId>hsqldbartifactId>
    4. <version>2.4.0version>
    5. <scope>testscope>
    6. dependency>

    我们可以在 Maven Central 上找到最新版本的jdbi3-coreHSQLDB和其他 Jdbi 模块。

    3. 连接数据库

    首先,我们需要连接到数据库。为此,我们必须指定连接参数。

    起点是Jdbi类:

    Jdbi jdbi = Jdbi.create("jdbc:hsqldb:mem:testDB", "sa", "");

    在这里,我们指定了连接 U​​RL、用户名,当然还有密码。

    3.1。附加参数

    如果我们需要提供其他参数,我们使用接受Properties对象的重载方法:

    1. Properties properties = new Properties();
    2. properties.setProperty("username", "sa");
    3. properties.setProperty("password", "");
    4. Jdbi jdbi = Jdbi.create("jdbc:hsqldb:mem:testDB", properties);

    在这些示例中,我们将Jdbi实例保存在局部变量中。那是因为我们将使用它向数据库发送语句和查询。

    事实上,仅仅调用create并没有建立到数据库的任何连接。它只是保存连接参数以供以后使用。

    3.2. 使用数据源

    如果我们使用DataSource连接到数据库,通常情况下,我们可以使用适当的create重载:

    Jdbi jdbi = Jdbi.create(datasource);

    3.3. 使用句柄

    与数据库的实际连接由Handle类的实例表示。

    使用句柄并自动关闭它们的最简单方法是使用 lambda 表达式:

    1. jdbi.useHandle(handle -> {
    2. doStuffWith(handle);
    3. });

    当我们不必返回值时,我们调用useHandle 。

    否则,我们使用withHandle

    1. jdbi.withHandle(handle -> {
    2. return computeValue(handle);
    3. });

    虽然不推荐,但也可以手动打开连接句柄;在这种情况下,我们必须在完成后关闭它:

    1. Jdbi jdbi = Jdbi.create("jdbc:hsqldb:mem:testDB", "sa", "");
    2. try (Handle handle = jdbi.open()) {
    3. doStuffWith(handle);
    4. }

    幸运的是,正如我们所见,Handle实现了 Closeable,因此它可以与try-with-resources 一起使用。

    4. 简单语句

    现在我们知道了如何获取连接,让我们看看如何使用它。

    在本节中,我们将创建一个我们将在整篇文章中使用的简单表。

    要将create table等语句发送到数据库,我们使用execute方法:

    1. handle.execute(
    2. "create table project "
    3. + "(id integer identity, name varchar(50), url varchar(100))");

    执行返回受语句影响的行数:

    1. int updateCount = handle.execute(
    2. "insert into project values "
    3. + "(1, 'tutorials', 'github.com/eugenp/tutorials')");
    4. assertEquals(1, updateCount);

    实际上,execute 只是一种方便的方法。

    我们将在后面的部分中查看更复杂的用例,但在此之前,我们需要学习如何从数据库中提取结果。

    5.查询数据库

    从数据库中产生结果的最直接的表达式是 SQL 查询。

    要使用 Jdbi 句柄发出查询,我们至少必须:

    1. 创建查询
    2. 选择如何表示每一行
    3. 迭代结果

    我们现在来看看上面的每一点。

    5.1。创建查询

    不出所料,Jdbi 将查询表示为Query类的实例。

    我们可以从句柄中获取一个:

    Query query = handle.createQuery("select * from project");

    5.2. 映射结果

    Jdbi 从 JDBC ResultSet中抽象出来,后者有一个相当繁琐的 API。

    因此,它提供了几种访问由查询或其他返回结果的语句产生的列的可能性。我们现在将看到最简单的。

    我们可以将每一行表示为一张地图:

    query.mapToMap();

    地图的键将是选定的列名。

    或者,当查询返回单个列时,我们可以将其映射到所需的 Java 类型:

    handle.createQuery("select name from project").mapTo(String.class);

    Jdbi 内置了许多常见类的映射器。特定于某些库或数据库系统的那些在单独的模块中提供。

    当然,我们也可以定义和注册我们的映射器。我们将在后面的部分中讨论它。

    最后,我们可以将行映射到 bean 或其他一些自定义类。同样,我们将在专门的部分中看到更高级的选项。

    5.3. 迭代结果

    一旦我们决定如何通过调用适当的方法来映射结果,我们就会收到一个ResultIterable对象。

    然后我们可以使用它来迭代结果,一次一行。

    在这里,我们将看看最常见的选项。

    我们只能将结果累积在一个列表中:

    List> results = query.mapToMap().list();

    或者到另一个Collection类型:

    List results = query.mapTo(String.class).collect(Collectors.toSet());

    或者我们可以将结果作为流进行迭代:

    1. query.mapTo(String.class).useStream((Stream stream) -> {
    2. doStuffWith(stream)
    3. });

    在这里,为了清楚起见,我们显式地键入了变量,但没有必要这样做。

    5.4. 获得单一结果

    作为一种特殊情况,当我们期望或只对一行感兴趣时,我们有几个专用的方法可用。

    如果我们最多想要一个结果,我们可以使用findFirst

    Optional> first = query.mapToMap().findFirst();

    如我们所见,它返回一个Optional值,该值仅在查询返回至少一个结果时才存在。

    如果查询返回多行,则仅返回第一行。

    相反,如果我们想要一个且只有一个 result,我们使用findOnly

    Date onlyResult = query.mapTo(Date.class).findOnly();

    最后,如果结果为零或多个,findOnly会抛出IllegalStateException

    6.绑定参数

    通常,查询具有固定部分和参数化部分。这有几个优点,包括:

    • 安全性:通过避免字符串连接,我们可以防止 SQL 注入
    • 轻松:我们不必记住复杂数据类型(例如时间戳)的确切语法
    • 性能:查询的静态部分可以解析一次并缓存

    Jdbi 支持位置参数和命名参数。

    我们在查询或语句中插入位置参数作为问号:

    1. Query positionalParamsQuery =
    2. handle.createQuery("select * from project where name = ?");

    相反,命名参数以冒号开头:

    1. Query namedParamsQuery =
    2. handle.createQuery("select * from project where url like :pattern");

    在任何一种情况下,要设置参数的值,我们都使用bind方法的变体之一:

    1. positionalParamsQuery.bind(0, "tutorials");
    2. namedParamsQuery.bind("pattern", "%github.com/eugenp/%");

    请注意,与 JDBC 不同,索引从 0 开始。

    6.1。一次绑定多个命名参数

    我们还可以使用一个对象将多个命名参数绑定在一起。

    假设我们有这个简单的查询:

    1. Query query = handle.createQuery(
    2. "select id from project where name = :name and url = :url");
    3. Map params = new HashMap<>();
    4. params.put("name", "REST with Spring");
    5. params.put("url", "github.com/eugenp/REST-With-Spring");

    然后,例如,我们可以使用地图:

    query.bindMap(params);

    或者我们可以以各种方式使用一个对象。例如,在这里,我们绑定一个遵循 JavaBean 约定的对象:

    query.bindBean(paramsBean);

    但我们也可以绑定对象的字段或方法;有关所有支持的选项,请参阅Jdbi 文档

    7. 发布更复杂的声明

    现在我们已经了解了查询、值和参数,我们可以回到语句并应用相同的知识。

    回想一下,我们之前看到的execute方法只是一个方便的快捷方式。

    事实上,与查询类似,DDL 和 DML 语句表示为类Update 的实例。

    我们可以通过在句柄上调用方法createUpdate来获取:

    1. Update update = handle.createUpdate(
    2. "INSERT INTO PROJECT (NAME, URL) VALUES (:name, :url)");

    然后,在Update上,我们拥有Query中的所有绑定方法,因此第 6 节也适用于更新。url

    当我们调用,surprise, execute时执行语句:

    int rows = update.execute();

    正如我们已经看到的,它返回受影响的行数。

    7.1。提取自增列值

    作为一种特殊情况,当我们有一个带有自动生成列(通常是自动增量或序列)的插入语句时,我们可能想要获取生成的值。

    然后,我们不调用execute,而是调用executeAndReturnGeneratedKeys

    1. Update update = handle.createUpdate(
    2. "INSERT INTO PROJECT (NAME, URL) "
    3. + "VALUES ('tutorials', 'github.com/eugenp/tutorials')");
    4. ResultBearing generatedKeys = update.executeAndReturnGeneratedKeys();

    ResultBearing与我们之前看到的Query类实现的接口相同,因此我们已经知道如何使用它:

    1. generatedKeys.mapToMap()
    2. .findOnly().get("id");

    8. 事务

    每当我们必须将多个语句作为单个原子操作执行时,我们都需要一个事务。

    与连接句柄一样,我们通过调用带有闭包的方法来引入事务:

    1. handle.useTransaction((Handle h) -> {
    2. haveFunWith(h);
    3. });

    而且,与句柄一样,当闭包返回时,事务会自动关闭。

    但是,我们必须在返回之前提交或回滚事务:

    1. handle.useTransaction((Handle h) -> {
    2. h.execute("...");
    3. h.commit();
    4. });

    然而,如果闭包抛出异常,Jdbi 会自动回滚事务。

    与句柄一样,如果我们想从闭包中返回一些东西,我们有一个专用方法inTransaction :

    1. handle.inTransaction((Handle h) -> {
    2. h.execute("...");
    3. h.commit();
    4. return true;
    5. });

    8.1。手动事务管理

    虽然在一般情况下不建议这样做,但我们也可以手动开始关闭事务:

    1. handle.begin();
    2. // ...
    3. handle.commit();
    4. handle.close();

    9. 结论与延伸阅读

    在本教程中,我们介绍了 Jdbi 的核心:查询、语句和事务。

    我们遗漏了一些高级功能,例如自定义行和列映射以及批处理。

    我们还没有讨论任何可选模块,尤其是 SQL 对象扩展。

    Jdbi 文档中详细介绍了所有内容。

    所有这些示例和代码片段的实现都可以在GitHub 项目中找到——这是一个 Maven 项目,因此应该很容易导入并按原样运行。

  • 相关阅读:
    【MySQL】MVCC机制(undo log,read view)
    OSPF高级配置——学习OSPF路由协议的高级应用
    离散数学 --- 树 --- 无向树,生成树与最小生成树
    会计制度设计试题及答案
    ESP8266-Arduino网络编程实例-ESP-MESH传感器数据发送与接收
    建立线上思维,创客匠人教你打造线上教学服务生态圈
    前端笔记:Create React App 初始化项目的几个关键文件解读
    Spring Cloud Alibaba —— 服务注册与配置中心
    编程笔记 Golang基础 020 流程控制
    Solidity - 合约结构 - 错误(error)- ^0.8.4版本新增
  • 原文地址:https://blog.csdn.net/allway2/article/details/126908676