杰瑞科技汇

Java中ResultSet遍历有几种高效方式?

核心概念

ResultSet 对象代表一个数据表,它通常位于一个指向数据库当前行的光标,这个光标最初位于第一行之前,遍历 ResultSet 的核心就是使用 next() 方法将光标移动到下一行,并判断是否还有数据。

Java中ResultSet遍历有几种高效方式?-图1
(图片来源网络,侵删)

传统的 while 循环(最基础、最常用)

这是最经典、最直接的遍历方式,它适用于所有 JDBC 版本和驱动。

步骤:

  1. 执行查询:通过 StatementPreparedStatementexecuteQuery() 方法获取 ResultSet
  2. 循环遍历:使用 while (resultSet.next()) 循环。next() 方法会将光标移动到下一行,如果成功移动(即存在下一行数据),则返回 true;如果已经到达结果集末尾,则返回 false
  3. 获取数据:在循环体内,使用 getXxx(columnIndex)getXxx(columnLabel) 方法获取当前行中某一列的值。
    • columnIndex:列的索引(从 1 开始,不是从 0 开始)。
    • columnLabel:列的名称(推荐使用,更具可读性,且不受 SELECT 子句中列顺序改变的影响)。
  4. 关闭资源非常重要! 最后必须关闭 ResultSetStatementConnection,以释放数据库资源。

代码示例:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TraditionalJdbcExample {
    // 假设的数据库连接信息
    private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database";
    private static final String USER = "your_username";
    private static final String PASS = "your_password";
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            // 1. 注册 JDBC 驱动 (对于较新版本的JDBC驱动,通常可以省略此步)
            Class.forName("com.mysql.cj.jdbc.Driver");
            // 2. 建立连接
            conn = DriverManager.getConnection(DB_URL, USER, PASS);
            // 3. 创建 Statement
            stmt = conn.createStatement();
            // 4. 执行查询
            String sql = "SELECT id, name, email FROM users";
            rs = stmt.executeQuery(sql);
            // 5. 遍历 ResultSet
            System.out.println("ID\tName\tEmail");
            System.out.println("----------------------");
            while (rs.next()) {
                // 通过列名获取数据 (推荐)
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                // 也可以通过列索引获取数据 (不推荐,因为顺序易变)
                // int id = rs.getInt(1);
                // String name = rs.getString(2);
                // String email = rs.getString(3);
                System.out.println(id + "\t" + name + "\t" + email);
            }
        } catch (SQLException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            // 6. 关闭资源 (顺序很重要:先关闭ResultSet,再关闭Statement,最后关闭Connection)
            try {
                if (rs != null) rs.close();
                if (stmt != null) stmt.close();
                if (conn != null) conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

使用 try-with-resources (Java 7+ 最佳实践)

手动关闭资源 (finally 块) 代码冗长,且容易出错,从 Java 7 开始,引入了 try-with-resources 语句,它可以自动实现资源的关闭。

任何实现了 AutoCloseable 接口的资源都可以在 try-with-resources 中使用。Connection, Statement, 和 ResultSet 都实现了这个接口。

优点:

  • 代码简洁:无需 finally 块来手动关闭资源。
  • 安全:JVM 会确保在 try 块执行完毕后,自动调用资源的 close() 方法,即使发生了异常。

代码示例:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TryWithResourcesExample {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database";
    private static final String USER = "your_username";
    private static final String PASS = "your_password";
    public static void main(String[] args) {
        // try-with-resources 会自动关闭 Connection, Statement, 和 ResultSet
        try (Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT id, name, email FROM users")) {
            System.out.println("ID\tName\tEmail");
            System.out.println("----------------------");
            while (rs.next()) {
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String email = rs.getString("email");
                System.out.println(id + "\t" + name + "\t" + email);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        // conn, stmt, rs 都已经被自动关闭了
    }
}

使用 RowSet (更灵活的 ResultSet)

RowSetResultSet 的一个子接口,它有两个主要特点:

Java中ResultSet遍历有几种高效方式?-图2
(图片来源网络,侵删)
  1. 可滚动:默认情况下,RowSet 对象是可滚动的。
  2. 可断开连接RowSet 可以在关闭数据库连接后仍然存在和使用,因为它通常会将数据缓存在内存中。

这使得 RowSet 特别适合于需要将数据传递给表现层(如 UI 组件)或者进行复杂离线处理的场景。

常用的 RowSet 实现类:

  • CachedRowSet:最常用的 RowSet,它是一个离线的、可滚动的 RowSet
  • JdbcRowSet:一个连接的、可滚动的 RowSet,行为更像一个传统的 ResultSet

代码示例 (CachedRowSet):

import javax.sql.rowset.CachedRowSet;
import javax.sql.rowset.RowSetFactory;
import javax.sql.rowset.RowSetProvider;
import java.sql.SQLException;
public class CachedRowSetExample {
    private static final String DB_URL = "jdbc:mysql://localhost:3306/your_database";
    private static final String USER = "your_username";
    private static final String PASS = "your_password";
    public static void main(String[] args) {
        CachedRowSet crs = null;
        try {
            // 1. 创建 RowSetFactory
            RowSetFactory rsf = RowSetProvider.newFactory();
            // 2. 创建 CachedRowSet
            crs = rsf.createCachedRowSet();
            // 3. 设置连接属性
            crs.setUrl(DB_URL);
            crs.setUsername(USER);
            crs.setPassword(PASS);
            // 4. 设置查询 SQL
            crs.setCommand("SELECT id, name, email FROM users");
            // 5. 执行查询并填充数据 (此时会建立连接,查询完后可以断开)
            crs.execute();
            // 6. 此时可以关闭原始连接了,crs 已经在内存中有了数据副本
            // ... (关闭 Connection 的代码) ...
            // 7. 遍历 CachedRowSet (它和 ResultSet 的遍历方式一样)
            System.out.println("ID\tName\tEmail");
            System.out.println("----------------------");
            while (crs.next()) {
                int id = crs.getInt("id");
                String name = crs.getString("name");
                String email = crs.getString("email");
                System.out.println(id + "\t" + name + "\t" + email);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 8. 关闭 CachedRowSet (会释放其占用的资源)
            if (crs != null) {
                try {
                    crs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

注意:使用 RowSet 需要 JDBC 驱动或额外的库(如 javax.sql.rowset 包,通常在 JDK 中提供)支持。


重要注意事项

  1. 关闭资源:这是 JDBC 编程的黄金法则,忘记关闭 Connection 会导致数据库连接泄漏,最终可能导致数据库服务宕机。try-with-resources 是目前最好的解决方案。
  2. 列名 vs. 列索引强烈推荐使用列名 (rs.getString("name")),因为:
    • 可读性高:代码更容易理解。
    • 健壮性强:即使 SELECT 语句中列的顺序改变,你的代码依然可以正常工作。
  3. 处理 NULL:如果数据库中的列可能为 NULL,直接调用 getInt()getString() 会抛出 SQLException,应该使用 wasNull() 方法来判断。
    int age = rs.getInt("age");
    if (rs.wasNull()) {
        // age 列的值为 NULL
        System.out.println("Age is not available.");
    } else {
        System.out.println("Age: " + age);
    }
  4. 性能考虑
    • *`SELECT **:尽量避免使用SELECT *`,只查询你需要的列,可以减少网络传输和内存消耗。
    • 大数据量ResultSet 包含海量数据,一次性加载到内存可能会导致 OutOfMemoryError,此时应考虑分页查询或使用服务器端游标。
方法 优点 缺点 适用场景
传统 while 循环 简单直观,兼容所有 JDBC 版本 需要手动关闭资源,代码冗长 遗留系统或对 Java 版本有要求的旧项目。
try-with-resources 代码简洁、安全、自动关闭资源 需要 Java 7+ 现代 Java 项目的首选和最佳实践。
RowSet 可断开连接,可滚动,易于传递数据 需要额外内存存储数据,性能可能稍差 需要在离线状态下操作数据,或需要将数据传递给非 JDBC 组件(如 UI)。

对于新的 Java 项目,强烈推荐使用 try-with-resources 来遍历 ResultSet,这是最现代、最安全、也是最简洁的方式。

Java中ResultSet遍历有几种高效方式?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇