Để xây dựng một ứng dụng quản lý một cái gì đó cần lưu trữ dữ liệu trong Java bạn sẽ cần đến sự trợ giúp của JDBC. Nó giúp bạn kết nối với các hệ quản trị cơ sở dữ liệu quan hệ để làm việc hiệu quả. Để chuẩn bị tài liệu cho các bài viết tiếp theo, tôi sẽ hướng dẫn các bạn sử dụng JDBC để kết nối cơ sở dữ liệu trong bài viết này.
Hướng dẫn cấu hình JDBC

JDBC là gì?

JDBC (Java Database Connectivity) là một Standard API dùng để kết nối và thực thi truy vấn với cơ sơ dữ liệu. Nó Sử dụng các JDBC Driver để kết nối với database. Tùy thuộc vào mỗi loại cơ sở dữ liệu mà bạn sẽ chọn các thư viện phù hợp để sử dụng trong dự án của mình.
Ví dụ:
- ojdbc6.jar: Được sử dụng để kết nối với Oracle
- mysql-connector-java-5.1.18-bin.jar: Dùng cho hệ quản trị CSDL MySQL
- jtds-1.3.1.jar: Dùng cho SQL Server
- sqljdbc4.jar: Dùng cho SQL Server

Đối với các dự án sử dụng Maven như những bài viết hướng dẫn của tôi thì sẽ cấu hình dependencies tương ứng trong file POM.xml

Về cơ bản, các thành phần của một JDBC API bao gồm:
  • DriverManager: là class dùng để quản lý danh sách các Driver (database drivers)
  • Driver: là interface dùng để liên kết với các kết nối đến cơ sở dữ liệu, điều khiển các kết nối với database. Một khi Driver được load lên, người dùng sẽ không cần phải gọi nó một cách cụ thể
  • Connection: Là interface bao gồm các method cho phép kết nối đến database. Nó mô tả nội dung của connection, tất cả các thông tin của một connection tới cơ sở dữ liệu chỉ cần thông qua đối tượng Connection được khởi tạo để làm việc
  • Statement: là interface được sử dụng để gói một câu lệnh SQL sau đó gửi tới database và thực thi
  • ResultSet: được sử dụng để trả về một tập các kết quả sau khi thực hiện các câu lệnh truy vấn dưới database

Nguyên tắc hoạt động của JDBC như thế nào thì mình xin phép không đề cập đến trong nội dung của bài viết này. Các bạn có thể tìm hiểu thêm trên Internet, vì nội dung của bài viết là tập trung vào hướng dẫn các bạn cách sử dụng JDBC qua một example.

Cách cấu hình để sử dụng JDBC

Đối với những dự án không sử dụng Maven
Công việc của các bạn phải làm là tải về các gói jar tương ứng như mình đã đề cập ở trên và sau đó add chúng vào Libraries của ứng dụng bạn đang phát triển.
Từ cửa sổ của Eclipse: Nhấp chuột phải vào tên dự án (Project) và chọn Properties -> Java Build Path -> Libraries -> Add JARs.. -> Chọn gói jar tương ứng để thêm chúng vào dự án.

Đối với các dự án sử dụng Maven
Các bạn thêm vào Dependency tương ứng với hệ quản trị cơ sở dữ liệu mà bạn đang sử dụng trong file POM.xml. Ví dụ như mình dùng MySQL thì sẽ add vào Dependency như sau:
<dependency>
 <groupId>mysql</groupId>
 <artifactId>mysql-connector-java</artifactId>
 <version>6.0.2</version>
</dependency>

1. Tạo database cho dự án

Hiện tại tôi đang sử dụng MySQL, tùy thuộc vào hệ quản trị cơ sở dữ liệu mà bạn đang sử dụng, hãy viết đoạn script để tạo bảng phù hợp nhé. Ở đây tôi sẽ thực hiện với database name: StudentManagement, dưới đây là đoạn script để tạo bảng Student và Clazz.
CREATE TABLE IF NOT EXISTS Clazz(
 id BIGINT NOT NULL AUTO_INCREMENT,
 name VARCHAR(50) NOT NULL,
 PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS Student(
 id BIGINT NOT NULL AUTO_INCREMENT,
 firstName VARCHAR(50) NOT NULL,
 lastName VARCHAR(50) NOT NULL,
 address VARCHAR(512) NOT NULL,
 clazz_id BIGINT NOT NULL,
 PRIMARY KEY (id),
 FOREIGN KEY (clazz_id) REFERENCES Clazz(id)
);

2. Tạo lớp cấu hình JDBC trong dự án

Chắc hẳn khi đã tìm hiểu đến JDBC trong Java thì các bạn đã có được 1 lượng kiến thức nhất định về OOP trong Java rồi phải không nào. Chính vì vậy, tôi sẽ áp dụng Abstract và Generic vào bài viết này để làm cho nó trở nên hay hơn. Nếu như bạn không hiểu thì đừng ngại ngần comment bên dưới để được tôi giải đáp thắc mắc nhé.

Các bạn hãy tạo cấu trúc dự án tương tự như hình ảnh bên dưới đây:
hướng dẫn JDBC chi tiết

Đầu tiên ta cần có 2 lớp model như sau:
Student.java
package com.vanthuong.tutorial.model;

public class Student {
 private Long id;
 private String firstName;
 private String lastName;
 private String address;
 private Long clazzId;

 public Student() {
 }

 public Student(Long id, String firstName, String lastName, String address, Long clazzId) {
  this.id = id;
  this.firstName = firstName;
  this.lastName = lastName;
  this.address = address;
  this.clazzId = clazzId;
 }

 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }

 public String getAddress() {
  return address;
 }

 public void setAddress(String address) {
  this.address = address;
 }

 public Long getClazzId() {
  return clazzId;
 }

 public void setClazzId(Long clazzId) {
  this.clazzId = clazzId;
 }

 @Override
 public String toString() {
  return "Student [id=" + id + ", firstName=" + firstName + ", lastName=" + lastName + ", address=" + address
    + ", clazzId=" + clazzId + "]";
 }
}

Clazz.java
package com.vanthuong.tutorial.model;

public class Clazz {
 private Long id;
 private String name;

 public Clazz() {
 }

 public Clazz(Long id, String name) {
  this.id = id;
  this.name = name;
 }

 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}

Chúng ta sẽ bắt đầu với lớp DBConfig.java trước, nội dung của nó sẽ chứa các constant phục vụ cho việc kết nối Database như sau:
package com.vanthuong.tutorial.dao;

public class DBConfig {
    public static String DRIVER = "com.mysql.cj.jdbc.Driver"; // Driver dành cho bản 6 trở lên
    public static String CON_STR = "jdbc:mysql://localhost:3306/studentmanagement";
    public static String USER = "vanthuong";
    public static String PWD = "password";
}

Lớp BaseDAO.java là lớp cơ sở dùng để tạo các phương thức như lấy kết nối, đóng các câu lệnh kết nối, Statement, ResultSet,...
package com.vanthuong.tutorial.dao;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BaseDAO {
 private static final Logger logger = LoggerFactory.getLogger(BaseDAO.class);
 private Connection conn = null;

    protected Connection connectDb() {
        return connectDb(DBConfig.DRIVER, DBConfig.CON_STR, DBConfig.USER,
                DBConfig.PWD);
    }

    protected Connection connectDb(String driver, String conStr, String user, String pwd) {
        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(conStr, user, pwd);
        } catch (ClassNotFoundException e) {
         logger.error("Error: unable to load driver class!");
            System.exit(1);
        } catch (SQLException e) {
         logger.error("Error: unable connecting SQL Server."
           + " Please check your connection carefully include:"
           + " DRIVER, CON_STR, USER, PASSWORD", e);
            System.exit(2);
        }
        return conn;
    }

    protected void close(AutoCloseable autoCloseable) {
        try {
   autoCloseable.close();
  } catch (Exception e) {
   logger.error("Error: could not close Autocloseable");
  }
    }
}
OK, chúng ta cần giải thích chút xíu qua lớp này nhỉ, để kết nối đến Database. Đầu tiên chúng ta phải sử dụng phương thức forName() để load Driver đăng ký kết nối đến Database Server. Tiếp theo, để lấy được kết nối chúng ta sẽ phải gọi DriverManager.getConnection(String connectionString, String username, String password) để trả về một connection đến Database và chúng ta có thể thực hiện các thao tác trên connection này.

Ở đây các bạn có thể thấy rằng phương thức connectDb() sẽ trả về kết nối bằng cách gọi đến lớp đã được overload bên dưới nó. Tham số đầu vào là các Constants mà chúng ta đã cấu hình trong lớp DBConfig.java ở trên. Các phương thức trong lớp này được khai báo là protected, ngụ ý rằng lớp này chỉ sử dụng được bởi các lớp con của nó trong package: com.vanthuong.tutorial.dao mà thôi. Trông nó cũng có vẻ đơn giản phải không nào, chưa có gì phức tạp cả :D

Tiếp đến là lớp GenericDAO.java
package com.vanthuong.tutorial.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class GenericDAO extends BaseDAO {
    public abstract T convertToObject(ResultSet rs);

    public abstract T getById(Long id) throws CustomException;

    public abstract int update(T entity)  throws CustomException;

    public abstract Long insert(T entity)  throws CustomException;

    public abstract int delete(Long id)  throws CustomException;

    public List getAll(String tableName)  throws CustomException{
        String query = "SELECT * FROM " + tableName;

        List entities = new ArrayList();
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            connection = connectDb();
            statement = connection.prepareStatement(query);
            resultSet = statement.executeQuery();
            while (resultSet.next()) {
                T entity = convertToObject(resultSet);
                entities.add(entity);
            }
        } catch (SQLException e) {
            logger.error("Error: could not retrieve entities from table " + tableName, e);
        } finally {
            if (statement != null) {
                close(statement);
            }
            if (connection != null) {
                close(connection);
            }

            if (resultSet != null) {
                close(resultSet);
            }
        }
        return entities;
    }
}

GenericDAO là một Abstract class được viết ra để chuẩn bị trước những phương thức mà các lớp con của nó, cụ thể là StudentDAO, ClazzDAO, sẽ phải implement những phương thức này. Đồng thời, một số phương thức có thể sử dụng chung, ví dụ như chúng ta muốn sử dụng câu lệnh SELECT * FROM tableName trong phương thức getAll() chẳng hạn sẽ được viết ở đây.

Chi tiết về phương thức getAll().
  • Chúng ta cần lấy tất cả entities từ bảng tableName và trả về danh sách là một List (Trong ví dụ này T là Student hoặc Clazz. Các bạn hãy tìm hiểu thêm về Generic để biết được kiểu T sử dụng như thế nào nhé)
  • Để thực hiện truy vấn, insert, update,... với database chúng ta cần một chuỗi query. Như trong phương thức này, chúng ta có chuỗi "SELECT * FROM " + tableName.
  • Sau khi mở kết nối, tôi gọi phương thức prepareStatment() của connection, sau đó truyền vào tham số là câu query đã chuẩn bị trước đó.
  • Kết quả trả về của phương thức preparedStatement.excuteQuery() sẽ đổ vào một ResultSet. Cuối cùng, sử dụng vòng lặp và gọi phương thức convertToObject() để lấy ra dữ liệu.

Ta có, lớp StudentDAO.java sẽ extends lớp GenericDAO ở trên và truyền vào kiểu T là Student. Trong lớp này, chúng ta sẽ Override các phương thức đã chuẩn bị từ GenericDAO. Việc đầu tiên chúng ta cần làm là implement phương thức convertToObject() để lấy được kết quả từ ResultSet.
package com.vanthuong.tutorial.dao;

import java.sql.ResultSet;
import java.sql.SQLException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vanthuong.tutorial.model.Student;

public class StudentDAO extends GenericDAO {
    private static final Logger logger = LoggerFactory.getLogger(StudentDAO.class);

    @Override
    public Student convertToObject(ResultSet rs) {
        Long id;
        String firstName;
        String lastName;
        String address;
        Long clazzId;
        try {
            id = rs.getLong("id");
            firstName = rs.getString("firstName");
            lastName = rs.getString("lastName");
            address = rs.getString("address");
            clazzId = rs.getLong("clazz_id");
            return new Student(id, firstName, lastName, address, clazzId);
        } catch (SQLException e) {
            logger.error("Error: could not convert ResultSet to Student object", e);
            return null;
        }
    }

 @Override
 public Student getById(Long id) {
  // TODO Auto-generated method stub
  return null;
 }

 @Override
 public int update(Student entity) {
  // TODO Auto-generated method stub
  return 0;
 }

 @Override
 public int insert(Student entity) {
  // TODO Auto-generated method stub
  return 0;
 }

 @Override
 public int delete(Long id) {
  // TODO Auto-generated method stub
  return 0;
 }
}

Ở đây mình chỉ implement phương thức convertToObject(), các phương thức khác được để trống để các bạn tự mình implement xem như là một bài tập thực hành khi làm việc với JDBC. Hi vọng rằng các bạn sẽ tìm hiểu thêm về PrepareStatment và áp dụng cho các phương thức getById, insert, updatedelete này.

Chuyển sang lớp App.java và chúng ta sẽ viết lại hàm main() như sau để lấy dữ liệu của bảng Student dưới database lên nhé.
package com.vanthuong.tutorial;

import java.util.List;

import com.vanthuong.tutorial.dao.StudentDAO;
import com.vanthuong.tutorial.model.Student;

public class App {
 public static void main(String[] args) {
  StudentDAO dao = new StudentDAO();
  List<Student> students = dao.getAll("Student");
  
  System.out.println("List of students");
  System.out.println("=========================");
  for(Student student : students) {
   System.out.println(student);
  }
 }
}

OK, bây giờ trong database StudentManagement, bạn hãy thêm vào một vài record và run chương trình để xem nó in ra màn hình chính xác không nhé.

Dữ liệu trong database
Hướng dẫn sử dụng JDBC để kết nối cơ sở dữ liệu

Kết quả in ra màn hình
Hướng dẫn sử dụng JDBC

Như vậy là mình vừa giới thiệu sơ qua cho các bạn về JDBC được sử dụng trong Java như thế nào. Đồng thời cũng đã có hướng dẫn cơ bản về cách cấu hình, sử dụng để select all 1 bảng dữ liệu từ database thông qua JDBC. Trong bài viết mình đã có tạo ra khá nhiều phương thức khác nhưng không implement hoàn toàn mà dành để challenge các bạn. Đây có thể xem làm 1 bài tập nhỏ để bạn thực hành ngay với JDBC. Các bạn có thể tải về dự án này từ 1 liên kết bên dưới đây. 1 bài còn dang dở như ở trên để thực hành tiếp, 1 bài đã được hoàn thành giúp các bạn tham khảo. Bản đầy đủ không hẳn đã hay nên các bạn hãy thử improve nó lên nhé.