[Java Backend Zero to Hello] BÀI 4.5: JDBC
📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 4: Database & SQL | Bài 40/86
BÀI 4.5: JDBC
Mục tiêu
- Hiểu JDBC và kiến trúc
- Kết nối database với Java
- Thực hiện CRUD với JDBC
- Sử dụng PreparedStatement chống SQL Injection
1. JDBC LÀ GÌ?
JDBC (Java Database Connectivity) là API chuẩn để kết nối và thao tác với database từ Java.
Kiến trúc
Java App
↓
JDBC API
↓
JDBC Driver Manager
↓
JDBC Driver (MySQL, PostgreSQL, ...)
↓
Database
2. CÁC LOẠI DRIVER
| Loại | Mô tả |
|---|---|
| Type 1 | JDBC-ODBC Bridge (deprecated) |
| Type 2 | Native API (cần thư viện native) |
| Type 3 | Network Protocol |
| Type 4 | Pure Java (phổ biến nhất) |
3. CÁC THÀNH PHẦN CHÍNH
| Interface | Mô tả |
|---|---|
DriverManager |
Quản lý driver |
Connection |
Kết nối đến DB |
Statement |
Thực thi SQL tĩnh |
PreparedStatement |
Thực thi SQL có tham số |
ResultSet |
Kết quả truy vấn |
SQLException |
Exception liên quan đến SQL |
4. KẾT NỐI DATABASE
4.1 Dependency
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.2.0</version>
</dependency>
4.2 Code kết nối
import java.sql.*;
public class DatabaseConnection {
private static final String URL = "jdbc:mysql://localhost:3306/mydb";
private static final String USER = "root";
private static final String PASSWORD = "password";
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
}
4.3 Try-with-resources
try (Connection conn = DatabaseConnection.getConnection()) {
System.out.println("Connected!");
} catch (SQLException e) {
e.printStackTrace();
}
5. CRUD VỚI JDBC
5.1 CREATE - INSERT
public void insertUser(User user) {
String sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
pstmt.setString(1, user.getName());
pstmt.setString(2, user.getEmail());
pstmt.setInt(3, user.getAge());
int affectedRows = pstmt.executeUpdate();
System.out.println("Inserted " + affectedRows + " row(s)");
// Lấy ID tự tăng
try (ResultSet generatedKeys = pstmt.getGeneratedKeys()) {
if (generatedKeys.next()) {
user.setId(generatedKeys.getLong(1));
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
5.2 READ - SELECT
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setLong(1, id);
try (ResultSet rs = pstmt.executeQuery()) {
if (rs.next()) {
return mapRow(rs);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
private User mapRow(ResultSet rs) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
user.setAge(rs.getInt("age"));
return user;
}
5.3 READ ALL
public List<User> findAll() {
List<User> users = new ArrayList<>();
String sql = "SELECT * FROM users";
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery()) {
while (rs.next()) {
users.add(mapRow(rs));
}
} catch (SQLException e) {
e.printStackTrace();
}
return users;
}
5.4 UPDATE
public boolean update(User user) {
String sql = "UPDATE users SET name = ?, email = ?, age = ? WHERE id = ?";
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setString(1, user.getName());
pstmt.setString(2, user.getEmail());
pstmt.setInt(3, user.getAge());
pstmt.setLong(4, user.getId());
return pstmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
5.5 DELETE
public boolean delete(Long id) {
String sql = "DELETE FROM users WHERE id = ?";
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setLong(1, id);
return pstmt.executeUpdate() > 0;
} catch (SQLException e) {
e.printStackTrace();
}
return false;
}
6. PREPAREDSTATEMENT VS STATEMENT
6.1 Statement (KHÔNG khuyến nghị)
// ❌ SQL Injection risk
String name = request.getParameter("name");
String sql = "SELECT * FROM users WHERE name = '" + name + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
6.2 PreparedStatement (KHUYẾN NGHỊ)
// ✅ An toàn
String sql = "SELECT * FROM users WHERE name = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, name);
ResultSet rs = pstmt.executeQuery();
6.3 Lợi ích PreparedStatement
- Chống SQL Injection
- Performance: SQL được compile 1 lần, execute nhiều lần
- Dễ đọc với nhiều tham số
7. BATCH UPDATE
Thực thi nhiều câu lệnh cùng lúc - hiệu quả hơn.
public void batchInsert(List<User> users) {
String sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
for (User user : users) {
pstmt.setString(1, user.getName());
pstmt.setString(2, user.getEmail());
pstmt.setInt(3, user.getAge());
pstmt.addBatch();
}
int[] results = pstmt.executeBatch();
System.out.println("Inserted " + results.length + " users");
} catch (SQLException e) {
e.printStackTrace();
}
}
8. TRANSACTION
public void transferMoney(Long fromId, Long toId, double amount) {
String debitSql = "UPDATE accounts SET balance = balance - ? WHERE id = ?";
String creditSql = "UPDATE accounts SET balance = balance + ? WHERE id = ?";
try (Connection conn = DatabaseConnection.getConnection()) {
conn.setAutoCommit(false); // Bắt đầu transaction
try (PreparedStatement debit = conn.prepareStatement(debitSql);
PreparedStatement credit = conn.prepareStatement(creditSql)) {
debit.setDouble(1, amount);
debit.setLong(2, fromId);
debit.executeUpdate();
credit.setDouble(1, amount);
credit.setLong(2, toId);
credit.executeUpdate();
conn.commit(); // Commit nếu thành công
} catch (SQLException e) {
conn.rollback(); // Rollback nếu lỗi
throw e;
}
} catch (SQLException e) {
e.printStackTrace();
}
}
9. METADATA
9.1 DatabaseMetaData
DatabaseMetaData meta = conn.getMetaData();
System.out.println("Database: " + meta.getDatabaseProductName());
System.out.println("Version: " + meta.getDatabaseProductVersion());
// Liệt kê tables
ResultSet tables = meta.getTables(null, null, "%", new String[]{"TABLE"});
while (tables.next()) {
System.out.println(tables.getString("TABLE_NAME"));
}
9.2 ResultSetMetaData
ResultSet rs = pstmt.executeQuery();
ResultSetMetaData rsMeta = rs.getMetaData();
int columnCount = rsMeta.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
System.out.println(rsMeta.getColumnName(i) + " - " + rsMeta.getColumnTypeName(i));
}
10. BÀI TẬP THỰC HÀNH
Bài 1: UserDAO
Tạo class UserDAO với đầy đủ CRUD.
Bài 2: Pagination
public List<User> findWithPagination(int page, int size) {
int offset = (page - 1) * size;
String sql = "SELECT * FROM users LIMIT ? OFFSET ?";
try (Connection conn = DatabaseConnection.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, size);
pstmt.setInt(2, offset);
// ...
}
}
Bài 3: Transaction
Viết method chuyển tiền giữa 2 tài khoản với transaction.
11. TÓM TẮT
| Khái niệm | Mô tả |
|---|---|
| JDBC | API kết nối database |
| Connection | Kết nối đến DB |
| PreparedStatement | SQL có tham số, an toàn |
| ResultSet | Kết quả truy vấn |
| Batch | Thực thi nhiều lệnh |
| Transaction | ACID operations |
| SQL Injection | Lỗ hổng bảo mật |
Bài tiếp theo: 4.6 Connection Pool
🧭 Điều Hướng Series
⬅️ Bài trước: BÀI 4.4: MYSQL/POSTGRESQL THỰC HÀNH
📋 Lộ trình tổng quan: Xem Toàn Bộ Series
➡️ Bài tiếp theo: BÀI 4.6: CONNECTION POOL
All rights reserved