Những lý do khiến ta chọn Hibernate thay vì JDBC
Bài đăng này đã không được cập nhật trong 3 năm
JDBC là công cụ thô sơ nhất, mộc mạc nhất giúp ta kết nối CSDL trong ứng dụng Java. Và rồi Hibernate ra đời, nó mang trong mình nhiều công cụ hữu ích giúp cho việc kết nối với CSDL một cách thuận tiện, dễ dàng hơn.
Trước tiên tôi xin liệt kê một số "vấn đề" gặp phải khi sử dụng JDBC.
- Ta phải lặp đi lặp lại những dòng code giống nhau trong ứng dụng chỉ để lấy dữ liệu từ database.
- Chúng ta phải vất vả vởi việc map giữa Object Java với các table tương ứng trong database.
- Tốn nhiều công sức để thay đổi từ hệ quản trị CSDL này (MySQL) sang một hệ quản trị CSDL khác (Oracle).
- Khó khăn trong việc tạo các giao tiếp/liên hệ giữa các table, lập trình OOPs.
Nhưng với Hibernate ta có thể hóa giải được các "vấn đề" trên. Nhưng trước tiên ta xem lại các khái niệm cơ bản JDBC và Hibernate là gì?
1. JDBC là gì?
JDBC là viết tắt của Java Database Connectivity, nó là ứng dụng mã nguồn mở cho Java, giúp ứng dụng Java thực hiện kết nối, làm việc với CSDL. Nó cho phép ta thực hiện các thao tác chuy xuất, update dữ liệu với CSDL quan hệ bằng việc sử dụng các câu lệnh SQL.
JDBC Workflow Ứng dụng Java sử dụng JDBC làm việc với CSDL thông qua trình tự 7 bước như sau.
- Tạo kết nối đến database
- Gửi SQL query đến database sử dụng JDBC driver tương ứng
- JDBC driver kết nối đến database
- Thực thi câu lệnh query để lấy kết quả trả về (số bản ghi lấy được, số bản ghi được update/delete)
- Gửi dữ liệu đến ứng dụng thông qua Driver Manager
- Xử lý dữ liệu trả về
- Đóng (giải phóng) kế nối đến database
2. Hibernate là gì?
Hibernate là một thư viện ORM (Object Relational Mapping) mã nguồn mở giúp lập trình viên viết ứng dụng Java có thể map các objects (pojo) với hệ quản trị cơ sở dữ liệu quan hệ, 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 Workflow 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 "thùng xe" chứa dữ liệu từ ứng dụng để ghi xuống database, hay chứa dữ liệu tải lên ứng dụng từ database. 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. Mỗi một database phải có một session factory. Ví dụ nếu 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. File cấu hình hibernate.cfg.xml có như sau.
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://192.168.10.13:3306/data
</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">root</property>
<property name="hibernate.connection.pool_size">10</property>
<property name="show_sql">true</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.current_session_context_class">thread</property>
</session-factory>
</hibernate-configuration>
Hibernate Session 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. Transation Là 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. Query Hibernate cung cấp các câu chuy vấn HQL (Hibernate Query Language) tới database và map kết quả trả về với đối tượng tương ứng của ứng dụng Java.
3. Sau đây là các lý do khiến ta chọn Hibernate thay vì JDBC.
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<User>();
while(rs.next()) {
User user = new User();
user.setUserId(rs.getString("UserId"));
user.setName(rs.getString("FirstName"));
user.setEmail(rs.getString(“Email”));
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")
public class UserModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private BigInteger id;
@NotEmpty
@Column(name = "email", unique = true)
private String email;
@NotEmpty
@Column(name = "name")
private String name;
public BigInteger getId() {
return this.id;
}
public void setId(BigInteger id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
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). Sau đây là ví dụ về câu lệnh HQL.
...
Session session = null;
try {
session = sessionFactory.openSession();
Query query = session.createQuery("select s.empId, s.name, s.salary from Salary s ");
Iterator sal = query.iterate();
System.out.println("EmpId \t Name \t Salary");
while(sal.hasNext()) {
Object[] obj = (Object[]) sal.next();
System.out.println(obj[0]+" \t "+ obj[1]+ " \t "+ obj[2]);
}
}
catch(Exception e) {
System.out.println(e.getMessage());
}
finally {
session.close();
}
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
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
// used Oracle
<property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
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();
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 POJO class.
- Cập nhật Hibernate XML mapping file để thêm map column - property. Ta chỉ thay đổi duy nhất 2 file trên.
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. Nó giống như việc ta có thể bẻ từng chiếc đũa của bó đũa to thay vì bẻ cả bó đũa. 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à docment 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<Document> documents = new HashSet<Document>();
// 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();
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
bắn 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 Uncheck xeption, và ta không cần viết try-catch trong code của mình nữa.
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.
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 độ Cach, mình sẽ có bài chi tiết hơn về Cach trong Hibernate.
Tài liệu tham khảo
All rights reserved