HibernateSession mang đến cho chúng ta nhiều cách khác nhau để truy xuất dữ liệu từ cơ sở dữ liệu. Trong đó, hai phương thức thường được nhắc đến nhiều nhất là get() và load().

Cả hai đều được hỗ trợ dưới dạng nạp chồng (overloading) mà chúng ta có thể sử dụng trong nhiều trường hợp khác nhau. Mặc dù get() và load() đều được dùng để truy vấn dữ liệu từ cơ sở dữ liệu nhưng hai phương thức này khác biệt rõ rệt về cơ chế hoạt động và hành vi xử lý dữ liệu.
Vậy get() và load() khác nhau ở điểm nào? Nên sử dụng cái nào trong trường hợp nào? Hãy cùng khám phá qua một ví dụ đơn giản nhưng rất trực quan để hiểu rõ hơn về cách mà chúng hoạt động!
Khác biệt giữa get() và load() trong Hibernate: Phân tích và ví dụ thực tế
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateGetVsLoad {
public static void main(String[] args) {
//Prep Work
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
//Get Example
Employee emp = (Employee) session.get(Employee.class, new Long(2));
System.out.println("Employee get called");
System.out.println("Employee ID= "+emp.getId());
System.out.println("Employee Get Details:: "+emp+"\\n");
//load Example
Employee emp1 = (Employee) session.load(Employee.class, new Long(1));
System.out.println("Employee load called");
System.out.println("Employee ID= "+emp1.getId());
System.out.println("Employee load Details:: "+emp1+"\\n");
//Close resources
tx.commit();
sessionFactory.close();
}
}
Sau khi thực thi đoạn mã trên, chúng ta sẽ nhận về kết quả như sau:
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee get called
Employee ID= 2
Employee Get Details:: Id= 2, Name= David, Salary= 200.0, {Address= AddressLine1= Arques Ave, City=Santa Clara, Zipcode=95051}
Employee load called
Employee ID= 1
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee load Details:: Id= 1, Name= Pankaj, Salary= 100.0, {Address= AddressLine1= Albany Dr, City=San Jose, Zipcode=95129}
Kết quả trả về ở trên cho thấy get() thực hiện truy vấn trực tiếp từ cơ sở dữ liệu hoặc từ bộ nhớ đệm của Hibernate, sau đó trả về đối tượng thực. Ngược lại, load() chỉ cung cấp một tham chiếu (reference) tới đối tượng. Đối tượng này có thể thực sự không tồn tại. Chỉ khi bạn truy cập đến các thuộc tính khác của đối tượng**,** dữ liệu mới được truy xuất từ cơ sở dữ liệu hoặc cache.
Tiếp theo, chúng ta sẽ thử truy xuất dữ liệu không tồn tại trong cơ sở dữ liệu để quan sát hành vi khác biệt giữa hai phương thức trên:
//Get Example
try{
Employee emp = (Employee) session.get(Employee.class, new Long(200));
System.out.println("Employee get called");
if(emp != null){
System.out.println("Employee GET ID= "+emp.getId());
System.out.println("Employee Get Details:: "+emp+"\\n");
}
}catch(Exception e){
e.printStackTrace();
}
//load Example
try{
Employee emp1 = (Employee) session.load(Employee.class, new Long(100));
System.out.println("Employee load called");
System.out.println("Employee LOAD ID= "+emp1.getId());
System.out.println("Employee load Details:: "+emp1+"\\n");
}catch(Exception e){
e.printStackTrace();
}
Đoạn mã trên tạo ra kết quả như sau.
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee get called
Employee load called
Employee LOAD ID= 100
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [com.journaldev.hibernate.model.Employee#100]
at org.hibernate.internal.SessionFactoryImpl$1$1.handleEntityNotFound(SessionFactoryImpl.java:253)
at org.hibernate.proxy.AbstractLazyInitializer.checkTargetState(AbstractLazyInitializer.java:262)
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:176)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
at com.journaldev.hibernate.model.Employee_$$_jvst407_1.toString(Employee_$$_jvst407_1.java)
at java.lang.String.valueOf(String.java:2847)
at java.lang.StringBuilder.append(StringBuilder.java:128)
at com.journaldev.hibernate.main.HibernateExample.main(HibernateExample.java:36)
Kết quả thực thi cho thấy, khi sử dụng get() để truy xuất một thực thể không tồn tại trong cơ sở dữ liệu, phương thức này sẽ trả về giá trị null. Điều này phản ánh đúng bản chất hoạt động của get(), khi nó thực hiện truy vấn cơ sở dữ liệu ngay khi được gọi.
Ngược lại, khi sử dụng load(), chúng ta vẫn có thể in ra giá trị id, nhưng ngay khi cố gắng truy cập các trường khác, Hibernate sẽ thực hiện truy vấn cơ sở dữ liệu và ném ra ngoại lệ org.hibernate.ObjectNotFoundException. Đây là một loại ngoại lệ thời gian chạy (RuntimeException) riêng của Hibernate, nên không bắt buộc phải xử lý bằng khối try-catch.
Ngoài ra, các phương thức get() và load() cũng có thể được gọi ở các dạng nạp chồng khác nhau, như ví dụ dưới đây.
Employee emp = (Employee) session.get("com.journaldev.hibernate.model.Employee", new Long(2));
Employee emp1 = (Employee) session.load("com.journaldev.hibernate.model.Employee", new Long(1));
Employee emp2 = new Employee();
session.load(emp1, new Long(1));
Một số phiên bản có thể chấp nhận thêm đối số LockOptions nhưng chúng không sử dụng trong ví dụ trên. Lưu ý, khi gọi các phương thức này, tên đầy đủ của lớp (fully qualified class name) phải được truyền vào như một tham số.
Những khác biệt chính giữa get() và load()
Tổng hợp lại, có thể rút ra một số điểm khác biệt cốt lõi giữa get() và load() như sau:
get()thực hiện truy vấn dữ liệu ngay khi được gọi, cònload()chỉ tạo proxy và trì hoãn truy vấn cho đến khi dữ liệu thực sự cần được truy cập. Nhờ đó,load()hỗ trợ hiệu quả cho cơ chế tải chậm (lazy loading) và phù hợp trong các tình huống tối ưu hóa hiệu năng.- Do
load()sẽ ném ra ngoại lệ nếu không tìm thấy bản ghi, nên chúng ta chỉ nên sử dụng khi chắc chắn rằng thực thể cần truy xuất đã tồn tại trong cơ sở dữ liệu. get()phù hợp hơn khi mục tiêu lập trình là kiểm tra sự tồn tại của dữ liệu một cách an toàn và rõ ràng.
Mong rằng phần phân tích và ví dụ minh họa vừa rồi có thể giúp bạn có cái nhìn rõ hơn về cách thức hoạt động của hai phương thức get() và load() trong Hibernate, đồng thời hỗ trợ bạn đưa ra lựa chọn phù hợp với từng tình huống cụ thể khi áp dụng vào thực tế.