NỘI DUNG BÀI VIẾT
1. Hibernate Framework là gì?
1.1. ORM
Như chúng ta đã biết, ORM (Object Relational Mapping) framework là một cơ chế cho phép người lập trình thao tác với database một cách hoàn toàn tự nhiên thông qua các đối tượng. Lập trình viên hoàn toàn không quan tâm đến loại database sử dụng SQL Server, MySQL, PostgreSQL, …
ORM giúp đơn giản hoá việc tạo ra dữ liệu, thao tác dữ liệu và truy cập dữ liệu. Đó là một kỹ thuật lập trình để ánh xạ đối tượng vào dữ liệu được lưu trữ trong cơ sở dữ liệu.
1.2. Peristence layer
Một ứng dụng có thể được chia làm 3 phần như sau: giao diện người dùng (presentation layer), phần xử lý nghiệp vụ (business layer) và phần chứa dữ liệu (data layer). Cụ thể ra, business layer có thể được chia nhỏ thành 2 layer con là business logic layer và persistence layer.
- Business logic layer: các tính toán logic nhằm thỏa mãn yêu cầu người dùng.
- Persistence layer: chịu trách nhiệm giao tiếp với data layer (thường là một hệ quản trị cơ sở dữ liệu quan hệ – Relational DBMS). Persistence sẽ đảm nhiệm các nhiệm vụ mở kết nối, truy xuất và lưu trữ dữ liệu vào các Relational DBMS.
1.3. Hibernate
Hibernate là một trong những ORM Framework. Hibernate framework là một framework cho persistence layer. Như vậy, nhờ có Hibernate framework mà giờ đây khi bạn phát triển ứng dụng bạn chỉ còn chú tâm vào những layer khác mà không phải bận tâm nhiều về persistence layer nữa.
Hibernate giúp lập trình viên viết ứng dụng Java có thể map các object (POJO) với hệ quản trị cơ sở dữ liệu quan hệ (database), và hỗ trợ thực hiện các khái niệm lập trình hướng đối tượng với cơ dữ liệu quan hệ.
Hibernate giúp lưu trữ và truy vấn dữ liệu quan hệ mạnh mẽ và nhanh. Hibernate cho phép bạn truy vẫn dữ liệu thông qua Java Persistence API (JPA) hoặc bằng ngôn ngữ SQL mở rộng của Hibernate (HQL) hoặc bằng SQL thuần (Native SQL).
Hibernate vốn là một thư viện sinh ra để làm việc với mọi loại database, nó không phụ thuộc vào bạn chọn loại database nào. Nếu Java là “Viết 1 lần chạy mọi nơi” thì Hibernate là “Viết 1 lần chạy trên mọi loại database”.
1.3.1. Lợi ích của Hibernate Framework
Hibernate Framework có các lợi ích như dưới đây:
- Mã nguồn mở và nhẹ: Hibernate Framework là mã nguồn mở có giấy phép LGPL và nhẹ.
- Hiệu suất nhanh: Hiệu suất của Hibernate Framework là nhanh bởi vì bộ nhớ cache được sử dụng trong nội bộ Hibernate Framework. Có hai loại bộ nhớ cache trong Hibernate Framework, gồm bộ nhớ cache cấp một (first level cache) và bộ nhớ cache cấp hai (second level cache). Bộ nhớ cache cấp một được enable mặc định.
- Truy vấn cơ sở dữ liệu độc lập: HQL (Hibernate Query Language) là phiên bản hướng đối tượng của SQL. Nó tạo ra các truy vấn cơ sở dữ liệu độc lập. Vì vậy, bạn không cần phải viết các truy vấn cơ sở dữ liệu cụ thể. Trước Hibernate, nếu dự án có cơ sở dữ liệu bị thay đổi, chúng ta cần phải thay đổi truy vấn SQL dẫn đến risk và dễ gây lỗi.
- Tạo bảng tự động: Hibernate framework cung cấp phương tiện để tạo ra các bảng cơ sở dữ liệu một cách tự động. Vì vậy, không cần phải tốn công sức tạo ra các bảng trong cơ sở dữ liệu thủ công.
- Đơn giản lệnh join phức tạp: Có thể lấy dữ liệu từ nhiều bảng một cách dễ dàng với Hibernate framework.
- Cung cấp thống kê truy vấn và trạng thái cơ sở dữ liệu: Hibernate hỗ trợ bộ nhớ cache truy vấn và cung cấp số liệu thống kê về truy vấn và trạng thái cơ sở dữ liệu.
1.3.2. Database
Hibernate hỗ trợ hầu hết tất cả RDBMS chính, chẳng hạn: Oracle, Microsoft SQL Server, PostgreSQL, MySQL, …
2. Kiến trúc Hibernate
Kiến trúc Hibernate bao gồm nhiều đối tượng như đối tượng persistent, session factory, transaction factory, connection factory, session, transaction, …
2.1. Persistence object
Chính là các POJO object map với các table tương ứng của cơ sở dữ liệu quan hệ. Nó như là những container chứa dữ liệu từ ứng dụng để lưu xuống database, hay chứa dữ liệu tải lên ứng dụng từ database.
2.2. Configuration
Là đối tượng Hibernate đầu tiên bạn tạo trong bất kỳ ứng dụng Hibernate nào và chỉ cần tạo một lần trong quá trình khởi tạo ứng dụng. Nó đại diện cho một tập tin cấu hình hoặc thuộc tính yêu cầu của Hibernate. Đối tượng Configuration cung cấp hai thành phần chính:
- Database Connection: Thao tác này được xử lý thông qua một hoặc nhiều tệp cấu hình được Hibernate hỗ trợ. Các tệp này là hibernate.properties và hibernate.cfg.xml.
- Class Mapping Setup: Thành phần này tạo ra kết nối giữa các lớp Java và các bảng cơ sở dữ liệu.
2.3. Session Factory
Là một interface giúp tạo ra session kết nối đến database bằng cách đọc các cấu hình trong Hibernate configuration.
SessionFactory là đối tượng nặng (heavy weight object) nên thường nó được tạo ra trong quá trình khởi động ứng dụng và lưu giữ để sử dụng sau này.
SessionFactory là một đối tượng luồng an toàn (Thread-safe) và được sử dụng bởi tất cả các luồng của một ứng dụng.
Mỗi một database phải có một session factory. Vì vậy, nếu bạn đang sử dụng nhiều cơ sở dữ liệu thì bạn sẽ phải tạo nhiều đối tượng SessionFactory. Giả sử ta sử dụng MySQL và Oracle cho ứng dụng Java của mình thì ta cần có một session factory cho MySQL, và một session factory cho Oracle.
2.4. Hibernate Session
Một session được sử dụng để có được một kết nối vật lý với một cơ sở dữ liệu. Đối tượng Session là nhẹ và được thiết kế để được tạo ra instance mỗi khi tương tác với cơ sở dữ liệu. Các đối tượng liên tục được lưu và truy xuất thông qua một đối tượng Session.
Các đối tượng Session không nên được mở trong một thời gian dài bởi vì chúng thường không phải là luồng an toàn (thread-unsafe) và chúng cần được tạo ra và được đóng khi cần thiết.
Mỗi một đối tượng session được Session factory tạo ra sẽ tạo một kết nối đến database.
2.5. Transation
Một Transaction đại diện cho một đơn vị làm việc với cơ sở dữ liệu và hầu hết các RDBMS hỗ trợ chức năng transaction. Các transaction trong Hibernate được xử lý bởi trình quản lý transaction và transaction (từ JDBC hoặc JTA).
Transaction đảm bảo tính toàn vẹn của phiên làm việc với cớ sở dữ liệu. Tức là nếu có một lỗi xảy ra trong transaction thì tất cả các tác vụ thực hiện sẽ thất bại.
Transaction là một đối tượng tùy chọn và các ứng dụng Hibernate có thể chọn không sử dụng interface này, thay vào đó quản lý transaction trong code ứng dụng riêng.
2.6. Query
Các đối tượng Query sử dụng chuỗi truy vấn SQL (Native SQL) hoặc Hibernate Query Language (HQL) để lấy dữ liệu từ cơ sở dữ liệu và tạo các đối tượng.
2.7. Criteria
Đối tượng Criteria được sử dụng để tạo và thực hiện các tiêu chí truy vấn để lấy các đối tượng từ database.
3. Tại sao nên dùng Hibernate thay vì JDBC
3.1. Object Mapping
Với JDBC ta phải map các trường trong bảng với các thuộc tính của Java object một cách “thủ công”. Với Hibernate sẽ hỗ trợ ta map một cách “tự động” thông qua các file cấu hình map XML hay sử dụng các anotation.
JDBC sẽ map Java object với table như sau:
//rs là ResultSet trả về từ câu query get dữ liệu bảng user.
List<User> users = new ArrayList<>();
while(rs.next()) {
User user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setCreatedDate(rs.getDate("createdDate"));
users.add(user);
}
Cũng với table user đó sử dụng các anotaion để Hibernate có thể map một cách “tự động” như sau.
@Entity
@Table(name = "user")
@Data
public class UserModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@NotEmpty
@Column(name = "username")
private String username;
@NotEmpty
@Column(name = "password")
private String password;
@NotEmpty
@Column(name = "createdDate")
private Date createdDate;
}
3.2. HQL
Hibernate cung cấp các câu lệnh truy vấn tương tự SQL, HQL của Hibernate hỗ trợ đầy đủ các truy vấn đa hình như, HQL “hiểu” các khái niệm như kế thừa (inheritance), đa hình (polymorphysm), và liên kết (association)
3.3. Database Independent
Code sử dụng Hibernate là độc lập với hệ quản trị cơ sở dữ liệu, nghĩa là ta không cần thay đổi câu lệnh HQL khi ta chuyển từ hệ quản trị CSDL MySQL sang Oracle, hay các hệ quản trị CSDL khác… Do đó rất dễ để ta thay đổi CSDL quan hệ, đơn giản bằng cách thay đổi thông tin cấu hình hệ quản trị CSDL trong file cấu hình.
//used MySQL
com.mysql.jdbc.Driver
// used Oracle
oracle.jdbc.driver.OracleDriver
Ví dụ khi ta muốn lấy 10 bản ghi dữ liệu của một table từ 2 CSDL khác nhau.
Với JDBC ta có câu truy vấn như sau.
# MySQL
SELECT column_name FROM table_name ORDER BY column_name ASC LIMIT 10;
# SQL Server
SELECT TOP 10 column_name FROM table_name ORDER BY column_name ASC;
Với Hibernate câu truy vấn không thay đổi với cả 2 CSDL.
Session.CreateQuery("SELECT E.id FROM Employee E ORDER BY E.id ASC").SetMaxResults(10).List();
3.4. Minimize Code Changes
Khi ta thay đổi (thêm) cột vào bảng, với JDBC ta phải thay đổi những gì:
- Thêm thuộc tính vào POJO class.
- Thay đổi method chứa câu truy vấn “select”, “insert”, “update” để bổ sung cột mới.
- Có thể có rất nhiều method, nhiều class chứa các câu truy vấn như trên.
Với Hibernate ta chỉ cần:
- Thêm thuộc tính vào Entity class.
- Cập nhật Hibernate Annotation để map column – property..
3.5. Lazy Loading
Với những ứng dụng Java làm việc với cơ sở dữ liệu lớn hàng trăm triệu bản ghi, việc có sử dụng Lazy loading trong truy xuất dữ liệu từ database mang lại lợi ích rất lớn.
Ví dụ những file tài liệu do người dùng upload được lưu ở bảng document. Bảng user có quan hệ một-nhiều với bảng document. Trong trường hợp này class User là class cha, class Document là class con. Bảng document nhanh chóng đầy lên theo thời gian. Mỗi khi ta lấy thông tin user và document tương ứng từ database giả sử dữ liệu document là rất lớn, để ứng dụng không bị chậm vì phải mất nhiều bộ nhớ để chứa toàn bộ document của toàn bộ user, ta áp dụng Lazy loading cho từng user như sau.
// Declaring fetch type for one to many association in your POJO
@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private Set documents = new HashSet();
// To fetch user with document use initialize() method as follows
User user = (User)session.get(User.class, new Integer(100));
// This code will fetch all products for user 100 from database 'NOW'
documents = user.getDocuments();
3.6. Loại bỏ Try-Catch Blocks
Sử dụng JDBC nếu lỗi xảy khi tao tác với database thì sẽ có exception SQLexception ném ra. Bởi vậy ta phải sử dụng try-catch block để xử lý ngoại lệ.
Hibernate xử lý việc này giúp bạn bằng cách nó override toàn bộ JDBC exception thành Unchecked Exception và ta không cần viết try-catch trong code của mình nữa.
3.7. Quản lý commit/ rollback Transaction
Transaction là nhóm các hoạt động (với database) của một tác vụ. Nếu một hoạt động không thành công thì toàn bộ tác vụ không thành công.
Với JDBC lập trình viên phải chủ động thực hiện commit khi toàn bộ hoạt động của tác vụ thành công, hay phải rollback khi có một hoạt động không thành công để kết thúc tác vụ.
Với Hibernate thì ta không cần quan tâm đến commit hay rollback, Hibernate đã quản lý nó giúp ta rồi.
3.8. Hibernate Caching
Hibernate cung cấp một cơ chế bộ nhớ đệm, giúp giảm số lần truy cập vào database của ứng dụng càng nhiều càng tốt. Điều này sẽ có tác dụng tăng performance đáng kể cho ứng dụng của bạn.
Hibernate lưu trữ các đối tượng trong session khi transation được kích hoạt. Khi một query được thực hiện liên tục, giá trị được lưu trữ trong session được sử dụng lại. Khi một transaction mới bắt đầu, dữ liệu được lấy lại từ database và được lưu trữ session. Hibernate cung cấp hai cấp độ Cache: bộ nhớ cache cấp một (first level cache) và bộ nhớ cache cấp hai (second level cache).
Chúng ta sẽ tìm hiểu chi tiết hơn về Cache trong Hibernate ở các bài viết kế tiếp.
3.9. Associations
Thật dễ dàng để tạo một liên kết giữa các bảng bằng Hibernate như quan hệ một-một, một-nhiều, nhiều-một và nhiều-nhiều trong Hibernate bằng cách sử dụng các Annotation để ánh xạ đối tượng của bảng.
Chẳng hạn, chúng ta có bảng Person quan hệ 1-1 với bảng PersonDetail. Với JDBC, chúng ta phải viết SQL để thực hiện INNER JOIN giữa 2 bảng. Với Hibernate, chỉ đơn giản thêm Annotation @OneToOne như sau:
3.10. JPA Annotation Support
Hibernate implement đặc tả JPA, do đó chúng ta có thể sử dụng các Annotation của JPA như @Entity, @Table, @Column, … Nhờ đặc điểm này, chúng ta có thể dễ dàng chuyển đổi giữa các ORM Framework mà không cần phải sử đổi code.
3.11. Connection Pooling
Như chúng ta đã biết, Connection Pooling giúp tăng performance nhờ vào sử dụng lại các kết nối khi có yêu cầu thay vì việc tạo kết nối mới.
Một số Connection Pooling được hỗ trợ bởi Hibernate: C3p0, Apache DBCP.
Chúng ta có thể dễ dàng tích hợp với các thư viện Connection Pooling có sẵn chỉ với vài dòng cấu hình như sau:
<property name="hibernate.c3p0.min_size">5</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">300</property>
<property name="hibernate.c3p0.max_statements">50</property>
<property name="hibernate.c3p0.idle_test_period">3000</property>
Trên đây là một số khái niệm cơ bản về Hibernate mà tôi muốn giới thiệu đến các bạn. Trong các bài viết tiếp theo chúng ta sẽ cùng tìm hiểu cách cài đặt và sử dụng các API của Hibernate.