数据库与持久化

Vert.x JDBC 异步客户端 (vertx-jdbc-client)

Vert.x JDBC 客户端允许应用程序通过统一的 Vert.x 响应式异步 API 与任何兼容标准 JDBC 规范的关系型数据库进行无缝交互。

该客户端的对外统一 API 抽象由 SqlClient 接口定义。

要在项目中使用该组件,请在构建描述文件中添加以下依赖:

  • Maven(在 pom.xml 中):

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-jdbc-client</artifactId>
  <version>5.2.1</version>
</dependency>
  • Gradle(在 build.gradle 文件中):

compile 'io.vertx:vertx-jdbc-client:5.2.1'

统一的 SqlClient API 体系

SqlClient 是 Vert.x 官方设计的用于与 SQL 关系型数据库进行通信的响应式异步 API。针对 MySQL、PostgreSQL、SQL Server 与 IBM DB2 等主流数据库,官方均提供了原生的纯异步网络驱动。然而,业界仍有许多关系型数据库缺乏专用纯异步驱动,Vert.x JDBC 客户端正是通过对底层 JDBC 驱动进行非阻塞 Worker 线程封装,对外暴露完全一致的 SqlClient API,让开发者在异构数据库间无缝平滑切换。

创建数据库连接池

一切始于连接池(Pool):

JDBCConnectOptions connectOptions = new JDBCConnectOptions()
  .setJdbcUrl("jdbc:h2:~/test")
  .setUser("sa")
  .setPassword("");
PoolOptions poolOptions = new PoolOptions()
  .setMaxSize(16);
Pool pool = JDBCPool.pool(vertx, connectOptions, poolOptions);

连接池的双重操作模式

连接池支持两种运行模式:

  1. 全托管连接模式(Managed Connection Mode):直接向 Pool 发起查询,连接的获取、语句执行与归还由底层全自动完成,无须担心连接泄漏。

  2. 手动连接模式(Manual Connection Mode):显式从 Pool 中获取 Connection 对象,适用于需要确保语句执行时序、会话隔离或手动控制事务的场景。

全托管连接模式示例:

pool
  .query("SELECT * FROM user")
  .execute()
  .onFailure(Throwable::printStackTrace)
  .onSuccess(rows -> {
    for (Row row : rows) {
      System.out.println(row.getString("FIRST_NAME"));
    }
  });

// 预编译语句方式:
pool
  .preparedQuery("SELECT * FROM user WHERE emp_id > ?")
  .execute(Tuple.of(1000))
  .onSuccess(rows -> {
    for (Row row : rows) {
      System.out.println(row.getString("FIRST_NAME"));
    }
  });

手动连接模式示例(注意务必归还连接):

pool
  .getConnection()
  .compose(conn -> conn
    .query("SELECT * FROM user")
    .execute()
    // 极其重要:务必在操作终止后归还连接!
    .eventually(conn::close))
  .onSuccess(rows -> {
    for (Row row : rows) {
      System.out.println(row.getString("FIRST_NAME"));
    }
  });

获取自增主键 (Generated Keys)

在执行批量或单条插入后,可以通过特殊属性 JDBCPool.GENERATED_KEYS_LIST 提取由数据库自动生成的自增主键列表:

String sql = "INSERT INTO insert_table (FNAME, LNAME) VALUES (?, ?)";
List<Tuple> batch = Arrays.asList(
  Tuple.of("dave", "developer"),
  Tuple.of("eve", "engineer")
);

pool
  .preparedQuery(sql)
  .executeBatch(batch)
  .onSuccess(rows -> {
    // 生成的主键列表作为附加行集合返回:
    List<Row> lastInsertIds = rows.property(JDBCPool.GENERATED_KEYS_LIST).rows();
    List<Long> newIds = lastInsertIds.stream()
      .map(r -> r.getLong(0)).collect(Collectors.toList());
  });

调用存储过程与数据库函数

传统的 JDBC 调用存储过程需要使用 CallableStatement。Vert.x JDBC 客户端对其进行了优雅的响应式适配,支持通过 SqlOutParam 显式标记输入输出参数:

String sql = "{call customer_lastname(?, ?)}";

pool
  .preparedQuery(sql)
  .execute(Tuple.of("John", SqlOutParam.OUT(JDBCType.VARCHAR)))
  .onSuccess(rows -> {
    if (rows.property(JDBCPool.OUTPUT)) {
      for (Row row : rows) {
        System.out.println(row.getString(0));
      }
    }
  });