1. 引言

Java 应用程序广泛使用 Java 数据库连接 (JDBC) API 来连接数据库并执行查询。ResultSet 是通过这些查询提取数据的表格形式表示。

在这个教程中,我们将学习如何将 JDBC ResultSet 的数据转换为 Map

2. 准备工作

我们将编写一些测试用例来实现我们的目标。我们的数据源将是 H2 数据库。H2 是一个快速、开源、内存数据库,支持 JDBC API。让我们添加相关的 Maven 依赖项

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

一旦数据库连接就绪,我们将编写一个方法来进行测试用例的初始数据设置。首先,我们创建一个 JDBC Statement,然后使用它创建名为 employee 的数据库表。employee 表包含 empId, empNameempCity 列,用于存储员工的 ID、姓名和城市信息。现在,我们可以使用 Statement.execute() 方法向表中插入示例数据:

void initialDataSetup() throws SQLException {
    Statement statement = connection.createStatement();

    String sql = "CREATE TABLE employee ( " +
      "empId INTEGER not null, " +
      "empName VARCHAR(50), " +
      "empCity VARCHAR(50), " +
      "PRIMARY KEY (empId))";

    statement.execute(sql);

    List<String> sqlQueryList = Arrays.asList(
      "INSERT INTO employee VALUES (1, 'Steve','London')", 
      "INSERT INTO employee VALUES (2, 'John','London')", 
      "INSERT INTO employee VALUES (3, 'David', 'Sydney')",
      "INSERT INTO employee VALUES (4, 'Kevin','London')", 
      "INSERT INTO employee VALUES (5, 'Jade', 'Sydney')");
    
    for (String query: sqlQueryList) {
        statement.execute(query);
    }
}

3. ResultSet 转换为 Map

现在,样本数据已存在于数据库中,我们可以查询它进行提取。查询数据库会得到 ResultSet 的输出形式。*我们的目标是将这些 ResultSet 的数据转换成一个 Map,其中键是城市名称,值是该城市的员工姓名列表。*

3.1. 使用 Java 7

首先,从数据库连接创建一个 PreparedStatement 并提供 SQL 查询。然后,我们可以使用 PreparedStatement.executeQuery() 方法获取 ResultSet

接下来,我们可以遍历 ResultSet 的数据并逐个获取列数据。为此,我们可以使用 ResultSet.getString() 方法,传入 employee 表的列名。然后,我们可以使用 Map.containsKey() 方法检查地图是否已经包含该城市名称的条目。如果没有找到对应的城市键,我们将添加一个键值对,城市名称作为键,空的 ArrayList 作为值,然后将员工的姓名添加到该城市的员工姓名列表中:

@Test
void whenUsingContainsKey_thenConvertResultSetToMap() throws SQLException {
    ResultSet resultSet = connection.prepareStatement(
        "SELECT * FROM employee").executeQuery();
    Map<String, List<String>> valueMap = new HashMap<>();

    while (resultSet.next()) {
        String empCity = resultSet.getString("empCity");
        String empName = resultSet.getString("empName");
        if (!valueMap.containsKey(empCity)) {
            valueMap.put(empCity, new ArrayList<>());
        }
        valueMap.get(empCity).add(empName);
    }
    assertEquals(3, valueMap.get("London").size());
}

3.2. 使用 Java 8

Java 8(/java-8-new-features)引入了lambda表达式和默认方法的概念。我们可以在实现中利用它们来简化新键的插入到输出地图中。**我们可以使用 Map 类的方法 *computeIfAbsent()***,它接受两个参数:一个键和一个映射函数。如果找到键,则返回相关值;否则,它将使用映射函数创建默认值,并将其作为新的键值对存储在地图中。之后我们可以将员工的姓名添加到列表中。

这是使用 Java 8 改进的先前测试用例版本:

@Test
void whenUsingComputeIfAbsent_thenConvertResultSetToMap() throws SQLException {
    ResultSet resultSet = connection.prepareStatement(
        "SELECT * FROM employee").executeQuery();
    Map<String, List<String>> valueMap = new HashMap<>();

    while (resultSet.next()) {
        String empCity = resultSet.getString("empCity");
        String empName = resultSet.getString("empName");
        valueMap.computeIfAbsent(empCity, data -> new ArrayList<>()).add(empName);
    }
    assertEquals(3, valueMap.get("London").size());
}

3.3. 使用 Apache Commons DbUtils

Apache Commons DbUtils 是一个第三方库,提供了 JDBC 操作的额外和简化功能。它提供了一个名为 ResultSetHandler 的有趣接口,它接受 JDBC ResultSet 作为输入,并允许我们将其转换为我们期望应用程序期望的任何对象。此外,这个库使用 QueryRunner 类在数据库表上运行 SQL 查询。QueryRunnerquery() 方法接受数据库连接、SQL 查询和 ResultSetHandler 作为输入,直接返回预期格式的数据。

让我们看看如何使用 ResultSetHandlerResultSet 转换为 Map 的示例:

@Test
void whenUsingDbUtils_thenConvertResultSetToMap() throws SQLException {

    ResultSetHandler <Map<String, List<String>>> handler = new ResultSetHandler <Map <String, List<String>>>() {
        public Map<String, List<String>> handle(ResultSet resultSet) throws SQLException {
            Map<String, List<String>> result = new HashMap<>();
            while (resultSet.next()) {
                String empCity = resultSet.getString("empCity");
                String empName = resultSet.getString("empName");
                result.computeIfAbsent(empCity, data -> new ArrayList<>()).add(empName);
            }
            return result;
        }
    };
    QueryRunner run = new QueryRunner();
    Map<String, List<String>> valueMap = run.query(connection, "SELECT * FROM employee", handler);
    assertEquals(3, valueMap.get("London").size());
}

4. 总结

总之,我们探讨了使用 Java 7、Java 8 和 Apache DbUtils 库将 ResultSet 聚合数据并转换为 Map 的几种方法。

如往常一样,本文的完整代码可以在 GitHub 上找到。