Menu

Java: Singleton Pattern và kết nối với cơ sở dữ liệu trong các ứng dụng Java

Singleton pattern
Bài viết này mình sẽ nói đến hai phần i)thứ nhất là Singleton Pattern và ii) thứ hai là kết nối với cơ sở dữ liệu trong các ứng dụng Java đơn giản. Cuối cùng, mình trình bày lý do tại sao mình lại áp dụng Singleton Pattern vào việc kết nối với cơ sở dữ liệu trong các ứng dụng Java.

Singleton Pattern

Đầu tiên ta đi tìm hiểu khái niệm Singleton Pattern. Ta chỉ cần hiểu đơn giản như sau: Bạn muốn viết một chương trình trò chơi có một chiếc xe tăng đi bắn tất cả các vật cản trên mặt đất. Nhưng tại một thời điểm bạn chỉ muốn có một chiếc xe tăng duy nhất tồn tại mà thôi. Khi xe tăng bị phá hỏng thì bạn mới được phép tạo ra cái mới. Singleton Pattern sẽ giúp bạn làm điều trên. Singleton sẽ chỉ tạo ra một đối tượng duy nhất trong suốt chương trình của bạn, nếu đối tượng đã tạo ra bị hủy thì lớp Singleton mới được phép tạo ra đối tượng mới.
Vậy, nếu bạn đã từng sử dụng ngôn ngữ lập trình Java thì bạn đã bao giờ sử dụng Singleton Pattern chưa? Câu trả lời là rồi đấy, bạn đã dùng rồi. Mình có thể lấy một ví dụ cho bạn đó là lớp Runtime trong package java.lang. Đúng không nào, chỉ có duy nhất một đối tượng của lớp Runtime được tạo ra mà thôi.
Bây giờ mình sẽ hướng dẫn tạo một lớp Singleton nhé. Mình sẽ đặt tên lớp là Singleton và chúng ta sẽ chỉ tạo ra một đối tượng duy nhất mà thôi. Bạn xem đoạn code sau:
Một số điêm lưu ý trong đoạn code trên như sau:
  • Dòng 17: mình có khai báo một trường tĩnh (static) và có kiểu dữ liệu chính là kiểu của lớp mà mình đang tạo ra.
  • Dòng 27-29: mình khai báo hàm dựng (contructor) của lớp nhưng lưu ý hàm dựng này có modifier là private. Làm điều này bạn sẽ ngăn chặn được việc tạo ra các đối tượng tùy ý.
  • Dòng 37-43: là một hàm getter bình thường của trường instance mà thôi. Ở đây, ta không có hàm setter của trường instance đâu nhé. Trong đoạn này, nếu instance == null thì ta sẽ tạo đối tượng mới điều này đảm bảo cho việc ta luôn có duy nhất một đối tượng tồn tại trong trường trình của mình. 
Như vậy là ta đã khai báo xong một lớp Singleton. Bây giờ ta sẽ thực hiện một ví dụ để xem có đúng là chỉ có duy nhất một đối tượng trong suốt thời gian sống của chương trình không nhé. Bạn xem đoạn code sau:

Mình có hai đối tượng là singleton1singleton2. Trong đó, mình sử dụng phương thức setValue của đối tượng singleton1 để đặt giá trị cho trường value. Đối tượng singleton2 mình không sử dụng phương thức setValue. Nhưng khi in ra màn hình giá trị của trường value thì cả hai đối tượng singleton1singleton2 đều cho cùng một giá trị. Bạn có thể in ra địa chỉ bộ nhớ của hai đối tượng này để biết chúng là một.

Kết nối cơ sở dữ liệu

Có nhiều công nghệ giúp chúng ta thực hiện kết nối từ một ứng dụng đến một cơ sở dữ liệu. Trong đó, JDBC là một công nghệ phổ biến. Chính vì lý do đó, trong bài này, mình sẽ hướng dẫn sử dụng API JDBC để kết nối với cơ sở dữ liệu.
Đầu tiên, ta sẽ tạo ra một cơ sở dữ liệu. Ở đây, bạn có thể chọn cơ sở dữ liệu bất kỳ mà bạn hay sử dụng (MySQL, SQL Server, Postgresql, DB2, Oracle, ...). Trong hướng dẫn này mình sẽ sử dụng CSDL Postgresql. Mình tạo ra cơ sở dữ liệu có tên là singletondb trong cơ sở dữ liệu này mình tạo một bảng (quan hệ) là Student có các cột là :
  • Id- kiểu int (khóa chính tự động tăng)
  • code - kiểu chuỗi (8 ký tự) (mã sinh viên)
  • name - kiểu chuỗi (50 ký tự) (họ và tên)
  • birthday - kiểu ngày tháng (date) (ngày sinh) và 
  • gender kiểu boolean (bit) (giới tính).
Bây giờ ta sẽ viết một lớp lưu trữ các thông tin như: địa chỉ của máy lưu trữ CSDL, tên CSDL, tên người dùng, mật khẩu. Lớp này sẽ có một phương thức trả lại cho ta một đối tượng lớp Connection với các thông tin trên.

Ta lưu ý một số dòng sau đây:
  • Dòng 16: trường này để lưu trữ đường dẫn đầy đủ đến lớp driver. Lớp driver được cung cấp bởi bên thứ ba cho phép ta kết nối vào cơ sở dữ liệu tương ứng.
  • Dòng 17: trường này lưu thông tin đường dẫn đến máy đang lưu trữ cơ sở dữ liệu
  • Dòng 19-20: trường này lưu tên và mật khẩu truy cập cơ sở dữ liệu.
  • Ở hàm dựng dòng 24 - 32: ta khởi tạo giá trị cho các biến.
    • Dòng 27: giá trị của trường driverClassName là "org.postgresql.Driver" như vậy ta phải thêm thư viện chứa lớp org.postgresql.Driver vào chương trình. Bạn có thể tại driver cho postgesql tại đây. Thông tin về thư viện driver của các CSDL khác bạn có thể tham khảo tại đây.
    • Dòng 28: gán đường dẫn đến cơ sở dữ liệu cho biến url. Trong giá trị này, ta thấy có 3 thành phần: i)jdbc:postgresql cho biết ta sẽ sử dụng cơ sở dữ liệu nào, ii) localhost:5432 địa chỉ (địa chỉ IP hoặc domain) và số cổng và iii) singletondb tên cơ sở dữ liệu.
    • Dòng 30-31: là thông tin về username và password của người dùng cơ sở dữ liệu.
  • Dòng 34-40: là một phương thức trả về một kết nối đến cơ sở dữ liệu. Hay nói cách khác, phương thức này sẽ mở một kết nối cơ sở dữ liệu.
Sau đây là chương trình ví dụ sử dụng các đối tượng của lớp DBConnection để mở kết nối và truy vấn dữ liệu:

Ta thấy có hai phương thức là printAllRecordcountAllRecord sử dụng lớp DBConnection. Như vậy, khi chương trình của chúng ta lớn lên thì rất có thể chúng ta sẽ mở ra rất nhiều kết nối đến cơ sở dữ liệu cùng một lúc. Và đồng thời, thao tác kết nối và hủy kết nối giữa ứng dụng và cơ sở dữ liệu cũng khá mất thời gian nhất là trong trường hợp máy lưu trữ cơ sở dữ liệu ở xa so với máy chạy chương trình phần mềm. Chính vì lẽ đó, ta lên duy trình một kết nối (hoặc một tập hữu hạn, sẽ giới thiệu ở post khác) thường xuyên đến cơ sở dữ liệu để tránh viêc phải liên tục kết nối và hủy kết nối. Chính vì vậy, ta nghĩ đến việc tạo một lớp Singleton để duy trì kết nối đến cơ sở dữ liệu, nếu chẳng may kết nối bị gián đoạn hoặc hủy một lý do nào đó, chương trình sẽ tự động mở lại kết nối và duy trì nó. Phần tiếp theo ta sẽ áp dụng Singleton Pattern để mở duy nhất một kết nối tại một thời điểm và duy trì kết nối đó để đảm bảo hiệu suất của ứng dụng.

Áp dụng Singleton Pattern để kết nối cơ sở dữ liệu

Từ hai phần trên ta đã biết cách tạo ra một lớp Singleton và cách kết nối cơ sở dữ liệu việc mở và hủy nhiều kết nối đến cơ sở dữ liệu không tốt chính vì thế ta sẽ duy trì một (hoặc một tập) kết nối đến cơ sở dữ liệu để đảm bảo hiệu năng của chương trình.
Bây giờ ta sẽ viết lớp DBSingletonConnection để thể hiện cho một lớp Singleton và đồng thời có một phương thức trả về kết nối đến cơ sở dữ liệu.

Thay vì sử dụng trường value như ở phần một ta sử dụng trường connection để lưu lại kết nối đã mở. Kết nối chỉ được mở trong hàm dựng mà thôi. Chính vị thế chỉ có lần đầu tiên kết nối được thực hiện và các lần sau, nếu kết nối bị đóng thì nó sẽ tự động được mở lại. Còn nếu không bị đóng, chương trình luôn duy trì một kết nối đến cơ sở dữ liệu và luôn sẵn dùng cho ứng dụng của chúng ta.
Sau đây là phương thức để test với trường hợp sử dụng DBSingletonConnection.

Nhận xét của tác giả

  • Post này chỉ nhằm mục đích giúp đỡ nhưng người mới bắt đầu lập trình Java làm quen với việc kết nối đến cơ sở dữ liệu, nếu đào sâu hơn thì ta có rất nhiều công cụ hộ trợ chứ không phải làm thủ công như thế này.
  • Việc áp dụng các Pattern vào các ứng dụng là rất tốt và dễ đọc cho đồng nghiệp.
  • Tuy nhiên, để đạt được hiệu suất cao hơn nữa ta phải tham khảo thêm khái niêm Pooling (tập các kết nối). Mình sẽ chỉ cho bạn ở các bài viết sau.
Bạn có thể tại code đầy đủ tại đây.

4 nhận xét:

  1. Bài viết kiểu này rất hay.Mình muốn đọc được nhiều bài kiểu này hơn nữa.Thanks

    Trả lờiXóa
    Trả lời
    1. Cảm ơn, nếu có thể bạn cho mình xin một vài chủ đề mà bạn hoặc những người xung quanh cảm thấy thú vị để minh có ý tưởng viết các bài có chất lượng hơn!

      Xóa
    2. viết SPRING với struts đi a

      Xóa
    3. Cảm ơn Hùng, mìn sẽ xem xét lại khi nào có thời gian mình sẽ viết các post về Spring và Struts để phục vụ bạn đọc. Cảm ơn bạn đã góp ý!

      Xóa