下面我将从最基础的手动转换到使用第三方库的高级自动化转换,全面地为你讲解 ResultSet 的转换方法,并提供详细的代码示例。

手动转换
这是最基础也是最核心的方法,理解它有助于你掌握转换的原理,并且在处理复杂逻辑或特殊需求时非常灵活。
将单行 ResultSet 转换为一个 Java 对象
假设我们有一个数据库表 users:
| id | name | created_at | |
|---|---|---|---|
| 1 | Alice | alice@example.com | 2025-01-15 10:00:00 |
| 2 | Bob | bob@example.com | 2025-01-16 11:30:00 |
我们创建一个对应的 Java 实体类 User:
// User.java
import java.time.LocalDateTime;
public class User {
private int id;
private String name;
private String email;
private LocalDateTime createdAt;
// 构造方法、Getter 和 Setter
public User() {}
public User(int id, String name, String email, LocalDateTime createdAt) {
this.id = id;
this.name = name;
this.email = email;
this.createdAt = createdAt;
}
// Getters and Setters...
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", createdAt=" + createdAt +
'}';
}
}
我们编写一个转换方法:

import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.LocalDateTime;
public class UserMapper {
/**
* 将 ResultSet 的当前行映射为一个 User 对象
* @param rs ResultSet 对象,指针必须在目标行上
* @return User 对象
* @throws SQLException 如果数据库访问出错
*/
public static User mapRow(ResultSet rs) throws SQLException {
// 1. 从 rs 中获取每一列的值
int id = rs.getInt("id");
String name = rs.getString("name");
String email = rs.getString("email");
// 注意:如果数据库是 TIMESTAMP,可以使用 getObject 并指定类型
LocalDateTime createdAt = rs.getObject("created_at", LocalDateTime.class);
// 2. 创建并填充 User 对象
User user = new User();
user.setId(id);
user.setName(name);
user.setEmail(email);
user.setCreatedAt(createdAt);
// 3. 返回对象
return user;
}
}
使用方法:
import java.sql.*;
public class Main {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/your_database";
String user = "your_username";
String password = "your_password";
String sql = "SELECT id, name, email, created_at FROM users WHERE id = ?";
try (Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, 1); // 查询 id=1 的用户
ResultSet rs = pstmt.executeQuery();
if (rs.next()) { // 将指针移动到第一行
User userObj = UserMapper.mapRow(rs);
System.out.println(userObj);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
将多行 ResultSet 转换为一个 List<User>
这非常常见,我们只需要在场景一的基础上增加一个循环即可。
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class UserRepository {
public List<User> findAllUsers() {
String sql = "SELECT id, name, email, created_at FROM users";
List<User> users = new ArrayList<>();
try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/your_database", "user", "password");
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
// 遍历 ResultSet 的每一行
while (rs.next()) {
// 对每一行都调用映射方法
User user = UserMapper.mapRow(rs);
users.add(user);
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
}
使用方法:
public class Main {
public static void main(String[] args) {
UserRepository repo = new UserRepository();
List<User> allUsers = repo.findAllUsers();
allUsers.forEach(System.out::println);
}
}
使用第三方库(推荐)
手动转换虽然灵活,但存在以下问题:

- 样板代码多:
rs.getXXX()和obj.setXXX()重复性高。 - 容易出错:列名和属性名拼写错误、类型不匹配等问题难以发现。
- 维护成本高:数据库表结构或 Java 类结构一旦变动,修改起来很麻烦。
为了解决这些问题,优秀的 ORM(Object-Relational Mapping)框架应运而生,它们可以自动完成 ResultSet 到 Java 对象的映射。
推荐库
- JDBI:轻量级、无依赖的 SQL 查询和对象映射库。
- Spring JDBC Template / Spring Data JPA:Spring 生态的一部分,提供了强大的数据访问抽象。
- MyBatis:非常流行的持久层框架,灵活性高。
- jOOQ:类型安全的 SQL 查询构建器,代码生成,功能强大。
这里我们以 JDBI 为例,因为它非常直观地展示了自动化映射的威力。
使用 JDBI
-
添加依赖 (Maven):
<dependency> <groupId>org.jdbi</groupId> <artifactId>jdbi3-core</artifactId> <version>3.41.0</version> <!-- 请使用最新版本 --> </dependency> -
定义接口:JDBI 通过接口定义 SQL 查询和结果映射。
import org.jdbi.v3.sqlobject.config.RegisterBeanMapper; import org.jdbi.v3.sqlobject.customizer.Bind; import org.jdbi.v3.sqlobject.statement.SqlQuery; import java.util.List; // 告诉 JDBI 将 User 类的实例映射到查询结果 @RegisterBeanMapper(User.class) public interface UserDao { // 直接返回一个 List<User>,JDBI 会自动处理映射 @SqlQuery("SELECT id, name, email, created_at FROM users") List<User> findAll(); // 通过参数绑定查询 @SqlQuery("SELECT id, name, email, created_at FROM users WHERE id = :id") User findById(@Bind("id") int id); } -
使用 JDBI:
import org.jdbi.v3.core.Jdbi; public class Main { public static void main(String[] args) { String url = "jdbc:mysql://localhost:3306/your_database"; // 创建 JDBI 实例 Jdbi jdbi = Jdbi.create(url, "user", "password"); // 获取 DAO 接口的代理实现 UserDao userDao = jdbi.onDemand(UserDao.class); // 调用方法,无需手动处理 ResultSet User user = userDao.findById(1); System.out.println("Found by ID: " + user); List<User> allUsers = userDao.findAll(); System.out.println("\nAll Users:"); allUsers.forEach(System.out::println); } }
可以看到,使用 JDBI 后,代码变得极其简洁,你只需要关注 SQL 语句和业务逻辑,繁琐的映射工作都由框架完成了。
其他常见转换
转换为 Map<String, Object>
当你不想创建对应的 Java 类,或者需要处理动态表结构时,将 ResultSet 转换为 Map 会很方便。
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.Map;
public class ResultSetToMap {
public static Map<String, Object> convertRowToMap(ResultSet rs) throws SQLException {
Map<String, Object> row = new LinkedHashMap<>(); // LinkedHashMap 保持列顺序
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnLabel(i); // 使用 getColumnLabel 可以获取别名
Object value = rs.getObject(i);
row.put(columnName, value);
}
return row;
}
}
使用方法:
// ... 在 try-with-resources 块中
while (rs.next()) {
Map<String, Object> rowMap = ResultSetToMap.convertRowToMap(rs);
System.out.println(rowMap);
// 输出: {id=1, name=Alice, email=alice@example.com, created_at=2025-01-15T10:00}
}
转换为 List<Map<String, Object>>
同样,只需增加一个循环即可。
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ResultSetToListOfMaps {
public static List<Map<String, Object>> convertToList(ResultSet rs) throws SQLException {
List<Map<String, Object>> result = new ArrayList<>();
while (rs.next()) {
result.add(convertRowToMap(rs)); // 复用上面的方法
}
return result;
}
// ... convertRowToMap 方法同上 ...
}
总结与建议
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 手动转换 | 无需额外依赖 灵活性最高,可处理复杂逻辑 原理清晰,易于学习 |
样板代码多 极易出错 维护成本高 |
学习 JDBC 基础 项目非常小,不想引入依赖 有非常特殊的、框架无法满足的映射需求 |
| 第三方库 (如 JDBI) | 代码简洁,可读性强 减少错误,类型安全 维护成本低,表/类结构变动影响小 提供了高级功能(如延迟加载、缓存等) |
引入外部依赖 需要学习框架 API |
绝大多数 Java 项目,是现代 Java 开发的最佳实践。 |
转换为 Map/List |
非常灵活,无需定义 Java 类 适合处理动态或未知的表结构 |
类型不安全,运行时才能发现错误 IDE 无法提供代码提示 无法利用面向对象的特性(如方法) |
报表、数据导出等场景 处理元数据或系统表 快速原型开发 |
最终建议:
对于任何生产级的 Java 应用,强烈推荐使用像 JDBI、MyBatis 或 Spring Data JPA 这样的持久层框架,它们能极大地提升开发效率、代码质量和可维护性,手动转换方式仅适合在学习或极特殊情况下使用。
