0

JDBC bài 10 Kỹ thuật Delete: Xóa dữ liệu an toàn và cơ chế xác nhận

Chúng ta đã đi đến chữ cái cuối cùng trong bộ tứ thao tác cơ sở dữ liệu CRUD (Create, Read, Update, Delete). Việc xóa dữ liệu (Delete) trong JDBC có cách thức thực thi giống hệt như thêm (Insert) và sửa (Update), nhưng lại mang rủi ro nghiệp vụ cao nhất.

1. Cơ chế thực thi lệnh Delete

Tương tự như khi cập nhật dữ liệu ở Bài 9, chúng ta tiếp tục sử dụng phương thức executeUpdate(sql) của đối tượng Statement để gửi lệnh DELETE tới Database.

Giá trị trả về vẫn là Affected Rows (số lượng dòng bị xóa). Dựa vào con số này, ứng dụng Java sẽ xác nhận được thao tác xóa có thực sự diễn ra hay không.

2. Xóa cứng (Hard Delete) và Xóa mềm (Soft Delete)

Trước khi viết code, bạn cần phân biệt hai khái niệm sống còn trong thiết kế hệ thống Backend:

  • Hard Delete (Xóa cứng): Sử dụng lệnh DELETE FROM table WHERE condition. Dữ liệu sẽ bị bốc hơi hoàn toàn khỏi ổ cứng vật lý của Database. Cách này không thể phục hồi (trừ khi có bản backup) và có thể gây lỗi hệ thống nếu dữ liệu này đang được tham chiếu bởi các bảng khác (Foreign Key Constraint).

  • Soft Delete (Xóa mềm): Thực chất là một lệnh UPDATE. Bạn thêm một cột trạng thái vào bảng (ví dụ: is_deleted = boolean hoặc deleted_at = timestamp). Khi cần "xóa", bạn chỉ cập nhật trạng thái này. Dữ liệu vẫn còn đó để phục vụ đối soát, thống kê, nhưng ứng dụng Java sẽ chủ động lọc bỏ chúng (thêm WHERE is_deleted = false) khi hiển thị cho người dùng.

Trong khuôn khổ bài học về JDBC thuần túy, chúng ta sẽ thực hành thao tác Hard Delete.

3. Thực hành: Xóa một bản ghi an toàn

Dưới đây là chương trình thực thi xóa một tài khoản người dùng dựa vào ID, kèm theo cơ chế kiểm tra (xác nhận) để đảm bảo không xóa nhầm dữ liệu.

Java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class DeleteExample {
    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) {
        
        // ID của người dùng cần xóa (thường lấy từ tham số API do frontend gửi lên)
        int targetUserId = 42;
        
        // Cú pháp SQL xóa dữ liệu tĩnh
        String deleteSQL = "DELETE FROM users WHERE id = " + targetUserId;

        try (Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
             Statement stmt = conn.createStatement()) {

            System.out.println("Bắt đầu thực thi lệnh xóa...");
            
            // Thực thi lệnh và nhận về số dòng bị ảnh hưởng
            int affectedRows = stmt.executeUpdate(deleteSQL);
            
            // Cơ chế xác nhận kết quả
            if (affectedRows == 1) {
                System.out.println("Thành công! Đã xóa vĩnh viễn người dùng có ID = " + targetUserId);
            } else if (affectedRows == 0) {
                System.out.println("Từ chối: Không tìm thấy người dùng có ID = " + targetUserId + " trong hệ thống.");
            } else {
                // Tình huống nguy hiểm: Xóa theo ID (khóa chính) thì tối đa chỉ được 1 dòng.
                // Nếu > 1, có nghĩa là cấu trúc bảng sai hoặc điều kiện WHERE đang có vấn đề nghiêm trọng!
                System.err.println("Cảnh báo đỏ: Đã xóa " + affectedRows + " bản ghi! Có điều gì đó không ổn với điều kiện WHERE.");
            }

        } catch (SQLException e) {
            // Xử lý lỗi phổ biến khi xóa: Bị chặn bởi khóa ngoại (Foreign Key)
            if (e.getErrorCode() == 1451) { // Mã lỗi 1451 của MySQL: Cannot delete or update a parent row
                System.err.println("Không thể xóa người dùng này vì họ đang có đơn hàng trong hệ thống!");
            } else {
                System.err.println("Lỗi SQL: " + e.getMessage());
            }
        }
    }
}

4. Kiểm soát rủi ro ở tầng Data Access

Đoạn code trên trình diễn ba chốt chặn an toàn khi thao tác Delete:

  1. Kiểm tra affectedRows == 0: Phản hồi chính xác cho người dùng (ví dụ: trả về HTTP 404) thay vì im lặng hoàn thành.

  2. Kiểm tra affectedRows > 1: Một cơ chế "cảnh báo sớm" (early warning). Khi xóa dựa trên Khóa chính (Primary Key), số dòng xóa tuyệt đối phải là 1 hoặc 0. Việc phát hiện sự bất thường này giúp kỹ sư phát hiện lỗi logic SQL lập tức.

  3. Xử lý ngoại lệ Khóa ngoại (Foreign Key Violation): Cơ sở dữ liệu quan hệ bảo vệ dữ liệu rất chặt chẽ. Nếu bảng users được liên kết với bảng orders, bạn không thể xóa user nếu đơn hàng của họ vẫn còn. Việc bắt mã lỗi (getErrorCode()) cho phép bạn báo cáo chính xác lý do thay vì quăng một cái lỗi SQLException chung chung.

Kết thúc Phần 2: Đến đây, bạn đã hoàn thiện 4 kỹ năng CRUD cơ bản nhất (Thêm, Đọc, Sửa, Xóa) bằng đối tượng Statement. Tuy nhiên, như đã cảnh báo ở Bài 6, cách ghép chuỗi của Statement chứa đựng lỗ hổng bảo mật chết người. Phần 3 của series sẽ là lời giải đáp hoàn hảo cho vấn đề này.


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í