0

[Java Backend Zero to Hello] BÀI 4.5: JDBC

Java Backend Zero to Hello

📚 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

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí