1. 概述

在某些场景中,我们可能需要通过API调用将数据库查询结果发送到另一个系统或消息平台。在这种情况下,我们通常使用JSON作为数据交换格式。

在这个教程中,我们将探讨多种方式将JDBCResultSet对象转换为JSON格式。

2. 代码示例

我们将使用H2数据库作为示例代码。我们已使用JDBC将一个CSV样本文件读入一个名为words的表中。以下是样本CSV文件的前三行,第一行为表头:

Username,Id,First name,Last name
doe1,7173,John,Doe
smith3,3722,Dana,Smith
john22,5490,John,Wang

形成ResultSet的代码如下:

ResultSet resultSet = stmt.executeQuery("SELECT * FROM words");

对于JSON处理,我们将使用JSON-Javaorg.json)库。首先,我们需要在我们的pom.xml文件中添加相应的依赖:

<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20240303</version>
</dependency>

3. 不使用外部依赖

JDBC API早于现代Java集合框架,因此我们不能直接使用诸如for-each循环和Stream方法。

相反,我们必须依赖迭代器。此外,我们需要从ResultSet的元数据中提取列的数量和列表。

这导致了一个基本的循环,即为每一行创建一个JSON对象,将对象添加到一个List中,最后将List转换为JSON数组。所有这些功能都在org.json包中提供:

ResultSetMetaData md = resultSet.getMetaData();
int numCols = md.getColumnCount();
List<String> colNames = IntStream.range(0, numCols)
  .mapToObj(i -> {
      try {
          return md.getColumnName(i + 1);
      } catch (SQLException e) {
          e.printStackTrace();
          return "?";
      }
  })
  .collect(Collectors.toList());

JSONArray result = new JSONArray();
while (resultSet.next()) {
    JSONObject row = new JSONObject();
    colNames.forEach(cn -> {
        try {
            row.put(cn, resultSet.getObject(cn));
        } catch (JSONException | SQLException e) {
            e.printStackTrace();
        }
    });
    result.add(row);
}

这里,我们首先运行一个循环来提取每个列的名称。然后我们在构建最终的JSON对象时使用这些列名。

在第二个循环中,我们遍历实际结果,并使用上一步计算出的列名将每个结果转换为JSON对象。然后我们将所有这些对象添加到一个JSON数组中。

我们已经将列名和列计数从循环中移出,以提高执行速度。

生成的JSON看起来像这样:

[
   {
      "Username":"doe1",
      "First name":"John",
      "Id":"7173",
      "Last name":"Doe"
   },
   {
      "Username":"smith3",
      "First name":"Dana",
      "Id":"3722",
      "Last name":"Smith"
   },
   {
      "Username":"john22",
      "First name":"John",
      "Id":"5490",
      "Last name":"Wang"
   }
]

4. 使用jOOQ与默认设置

jOOQ框架(Java面向对象查询)提供了包括处理JDBC和ResultSet对象在内的许多便利工具。首先,我们需要在pom.xml文件中添加jOOQ的依赖

<dependency>
    <groupId>org.jooq</groupId>
    <artifactId>jooq</artifactId>
    <version>3.11.11</version>
</dependency>

添加依赖后,我们可以使用单行解决方案将ResultSet转换为JSON对象:

JSONObject result = new JSONObject(DSL.using(dbConnection)
  .fetch(resultSet)
  .formatJSON());

生成的JSON元素是一个包含两个字段的对象,即fieldsrecords,其中fields包含列的名称和类型,而records包含实际数据。这与之前的JSON对象稍有不同,对于我们示例表的情况,其结构如下:

{
   "records":[
      [
         "doe1",
         "7173",
         "John",
         "Doe"
      ],
      [
         "smith3",
         "3722",
         "Dana",
         "Smith"
      ],
      [
         "john22",
         "5490",
         "John",
         "Wang"
      ]
   ],
   "fields":[
      {
         "schema":"PUBLIC",
         "name":"Username",
         "type":"VARCHAR",
         "table":"WORDS"
      },
      {
         "schema":"PUBLIC",
         "name":"Id",
         "type":"VARCHAR",
         "table":"WORDS"
      },
      {
         "schema":"PUBLIC",
         "name":"First name",
         "type":"VARCHAR",
         "table":"WORDS"
      },
      {
         "schema":"PUBLIC",
         "name":"Last name",
         "type":"VARCHAR",
         "table":"WORDS"
      }
   ]
}

5. 使用jOOQ自定义设置

如果我们不喜欢jOOQ产生的JSON对象的默认结构,可以进行定制。

我们将通过实现RecordMapper接口来做到这一点。这个接口有一个map()方法,它接收一个Record作为输入并返回任意类型的所需对象。

然后,我们将RecordMapper作为输入传递给jOOQ结果类的map()方法:

List json = DSL.using(dbConnection)
  .fetch(resultSet)
  .map(new RecordMapper() {
      @Override
      public JSONObject map(Record r) {
          JSONObject obj = new JSONObject();
          colNames.forEach(cn -> obj.put(cn, r.get(cn)));
          return obj;
      }
  });
return new JSONArray(json);

在这里,我们在map()方法中返回了一个JSONObject

生成的JSON看起来类似于第3节:

[
   {
      "Username":"doe1",
      "First name":"John",
      "Id":"7173",
      "Last name":"Doe"
   },
   {
      "Username":"smith3",
      "First name":"Dana",
      "Id":"3722",
      "Last name":"Smith"
   },
   {
      "Username":"john22",
      "First name":"John",
      "Id":"5490",
      "Last name":"Wang"
   }
]

6. 总结

在这篇文章中,我们探索了将JDBC的ResultSet转换为JSON对象的三种不同方法。

每种方法都有其特定的应用场景。我们选择哪种取决于输出JSON对象所需的结构以及对依赖大小的可能限制,例如。

如往常一样,示例代码的源代码可以在GitHub上找到。