JDBC bài 7 Kỹ thuật Read: Khai thác dữ liệu với ResultSet và các vòng lặp nhé
Khi bạn thực thi một câu lệnh SELECT thông qua phương thức executeQuery(), cơ sở dữ liệu không trả về một cục dữ liệu thô. Thay vào đó, JDBC gói gọn toàn bộ kết quả dạng bảng vào một đối tượng đặc biệt có tên là ResultSet.
1. Cơ chế Con trỏ (Cursor) của ResultSet
Hãy tưởng tượng ResultSet như một bảng tính. Để đọc từng dòng, ResultSet sử dụng một "con trỏ" (cursor).
-
Khi mới được tạo ra, con trỏ không chỉ vào dòng dữ liệu đầu tiên, mà nó đứng ở vị trí trước dòng đầu tiên (before first row).
-
Bạn phải gọi phương thức
rs.next()để đẩy con trỏ xuống dòng tiếp theo. -
Phương thức
rs.next()trả vềtruenếu có dữ liệu ở dòng hiện tại, và trả vềfalsenếu đã duyệt hết danh sách.
Đó là lý do vòng lặp while luôn là cấu trúc đi kèm bắt buộc để duyệt qua toàn bộ ResultSet.
2. Các phương thức Getter (Trích xuất dữ liệu)
Khi con trỏ đang đứng ở một dòng cụ thể, bạn dùng các hàm get...() để lấy giá trị của từng cột. JDBC hỗ trợ hai cách tham chiếu đến cột:
-
Theo tên cột (Column Name): Dễ đọc, code không bị hỏng nếu thay đổi thứ tự cột trong Database. (Khuyên dùng)
-
Theo vị trí cột (Column Index): Nhanh hơn một chút ở tầng xử lý nền, nhưng cực kỳ dễ nhầm lẫn. Lưu ý quan trọng: Index trong ResultSet bắt đầu từ 1, không phải từ 0 như mảng trong Java!
Các hàm trích xuất phổ biến:
-
getInt("id")hoặcgetInt(1) -
getString("username")hoặcgetString(2) -
getDouble("price") -
getBoolean("is_active")
3. Thực hành: Đọc danh sách User từ Database
Dưới đây là đoạn mã chuẩn mực kết hợp Try-with-Resources để lấy toàn bộ dữ liệu ra khỏi bảng users và in ra console.
Java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class ResultSetExample {
private static final String URL = "jdbc:mysql://localhost:3306/shop_db";
private static final String USER = "root";
private static final String PASSWORD = "password123";
public static void main(String[] args) {
String sql = "SELECT id, username, email FROM users";
// Khai báo cả 3 tài nguyên (Connection, Statement, ResultSet) trong khối Try
try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
System.out.println("--- DANH SÁCH NGƯỜI DÙNG ---");
// Đẩy con trỏ đi từng dòng cho đến khi hết dữ liệu
while (rs.next()) {
// Lấy dữ liệu theo tên cột
int id = rs.getInt("id");
String username = rs.getString("username");
String email = rs.getString("email");
System.out.println("ID: " + id + " | Tên: " + username + " | Email: " + email);
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
4. Bẫy dữ liệu (Data Trap): Vấn đề với giá trị NULL
Database cho phép một ô chứa giá trị NULL. Tuy nhiên, kiểu dữ liệu nguyên thủy trong Java (như int, double, boolean) lại không thể chứa null.
Nếu bạn gọi rs.getInt("age") và trong Database cột tuổi đang bị rỗng (NULL), JDBC sẽ ngầm định trả về số 0. Điều này cực kỳ nguy hiểm về mặt logic nghiệp vụ vì bạn không thể phân biệt được người dùng nhập 0 tuổi hay là họ chưa cập nhật tuổi.
Cách giải quyết: Sử dụng hàm wasNull() ngay sau khi lấy dữ liệu để kiểm tra xem giá trị vừa đọc có thực sự là NULL hay không.
Java
int age = rs.getInt("age");
if (rs.wasNull()) {
System.out.println("Khách hàng chưa cung cấp tuổi.");
} else {
System.out.println("Tuổi của khách hàng là: " + age);
}
All rights reserved