JDBC bài 11 PreparedStatement là gì? Sự khác biệt cốt lõi so với Statement
Ở các bài trước của Phần 2, bạn đã sử dụng đối tượng Statement để thực thi các lệnh CRUD. Mặc dù nó hoạt động tốt, nhưng trong môi trường Production thực tế, các Backend Developer cực kỳ hạn chế sử dụng Statement cho các thao tác chứa dữ liệu người dùng. Thay vào đó, tiêu chuẩn công nghiệp bắt buộc là PreparedStatement.
1. PreparedStatement là gì?
PreparedStatement là một interface kế thừa trực tiếp từ Statement (nghĩa là nó có tất cả các hàm của Statement như executeQuery, executeUpdate). Chữ "Prepared" mang ý nghĩa là "được chuẩn bị trước".
Thay vì ghép nối các biến dữ liệu trực tiếp vào câu lệnh SQL bằng phép cộng chuỗi, bạn sẽ viết một câu lệnh SQL cố định, trong đó các vị trí cần truyền dữ liệu sẽ được thay thế bằng các dấu chấm hỏi (?) (gọi là placeholders/bind variables). Sau đó, bạn sử dụng các hàm setter để "bơm" giá trị thực tế vào các dấu chấm hỏi này trước khi ra lệnh thực thi.
2. Sự khác biệt cốt lõi: Cơ chế Biên dịch trước (Pre-compilation)
Sự khác biệt lớn nhất giữa hai giao diện này nằm ở cách hệ quản trị cơ sở dữ liệu (Database Engine) xử lý câu lệnh dưới tầng đáy.
Với Statement:
Mỗi lần bạn gọi stmt.executeUpdate("INSERT INTO users VALUES ('hieu', '123')"), Database phải làm đầy đủ 3 bước:
- Parse: Phân tích cú pháp xem câu SQL có đúng không.
- Compile: Biên dịch và tính toán đường dẫn thực thi tối ưu nhất (Execution Plan).
- Execute: Tiến hành ghi dữ liệu.
Nhược điểm: Nếu bạn dùng Statement để insert 10,000 user, Database phải lặp lại cả 3 bước này 10,000 lần, gây lãng phí tài nguyên CPU cực kỳ lớn.
Với PreparedStatement:
Khi bạn khởi tạo lệnh conn.prepareStatement("INSERT INTO users VALUES (?, ?)"):
- Chuỗi SQL (chứa các dấu
?) lập tức được gửi xuống Database. Database sẽ Parse và Compile chỉ một lần duy nhất, sau đó lưu Execution Plan này vào bộ nhớ đệm (Cache). - Ở những lần gọi hàm thực thi tiếp theo, bạn chỉ việc gán dữ liệu vào các dấu
?. Database sẽ bỏ qua bước 1 và 2, tiến thẳng tới bước 3 (Execute) với tốc độ chớp nhoáng.
3. So sánh chi tiết: Statement vs PreparedStatement
| Tiêu chí | Statement | PreparedStatement |
|---|---|---|
| Cú pháp | Nối chuỗi bằng toán tử + |
Sử dụng tham số vị trí ? (Placeholders) |
| Thời điểm biên dịch | Biên dịch lại mỗi lần gọi hàm execute | Biên dịch sẵn một lần ngay lúc khởi tạo đối tượng |
| Hiệu năng | Rất chậm khi chạy một logic nhiều lần | Cực kỳ nhanh khi tái sử dụng hoặc chạy Batch |
| Xử lý kiểu dữ liệu | Phải tự bọc dấu nháy đơn ' ' cho String/Date |
Tự động xử lý hoàn toàn qua các hàm setString(), setDate() |
| Bảo mật | Chứa lỗ hổng chí mạng (SQL Injection) | An toàn tuyệt đối trước SQL Injection |
4. Hình dung sự thay đổi trong Code
Để thấy rõ sự thanh lịch của PreparedStatement, hãy nhìn vào cách chúng ta truy vấn thông tin user:
Cách cũ (Statement): Code rối mắt và dễ lỗi cú pháp
// Phải cẩn thận từng dấu nháy đơn, nháy kép
String sql = "SELECT * FROM users WHERE email = '" + userEmail + "' AND status = " + statusCode;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
Cách mới (PreparedStatement): Code sạch sẽ và an toàn
// SQL tĩnh hoàn toàn, cấu trúc rõ ràng
String sql = "SELECT * FROM users WHERE email = ? AND status = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
// Gán giá trị vào các dấu ? (Index bắt đầu từ 1, đếm từ trái sang phải)
pstmt.setString(1, userEmail);
pstmt.setInt(2, statusCode);
// Gọi thực thi KHÔNG truyền tham số sql vào nữa
ResultSet rs = pstmt.executeQuery();
Quy tắc vàng cho Backend: Bất cứ khi nào câu lệnh SQL có chứa biến số (tham số truyền vào từ người dùng, từ hàm khác...), Hiếu bắt buộc phải dùng
PreparedStatement. Chỉ giữ lạiStatementcho các câu lệnh định nghĩa cấu trúc (DDL) không chứa tham số nhưCREATE TABLE,DROP VIEW.
All Rights Reserved