Bài viết gốc bạn có thể đọc ở đây.
Cả findById() và getOne() là những phương thức lấy một đối tượng từ kho dữ liệu cơ bản. Nhưng cơ chế để lấy dữ liệu của hai phương thức này có sự khác nhau, trong thực tế getOne() là lazy operation, nó thật chí không tác động vào cơ sở dữ liệu.
Phương thức getOne()
getOne() trả về một entity với số định danh. getOne là yêu cầu nội bộ của phương thức EntityManager.getReference(). Theo tài liệu, phương thức này luôn luôn trả về một proxy mà không tác động vào cơ sở dữ liệu (lazily fetched). Phương thức này sẽ trả EntityNotFoundException tại thời điểm truy cập thực tế nếu entity được yêu cầu không tồn tại trong cơ sở dữ liệu.
Tài liệu EntityManager.getReference(Class, Object)
Khi lấy một yêu cầu, trạng thái của nó có thể là lazily fetched. Nếu yêu cầu không tồn tại trong cơ sở dữ liệu, EntityNotFoundException sẽ được ném ra khi yêu cầu truy cập lần đầu tiên. (Persistence provider runtime được phép ném ra EntityNotFoundException khi getReference được gọi.) Ứng dụng không nên chờ đợi trạng thái phiên bản (instance state) sẽ có sẵn khi tách ra, trừ khi nó được truy cập bởi ứng dụng trong khi mà entity manager vẫn đang mở.
Phương thức findById()
Phương thức này sẽ thực sự tác động vào cơ sở dữ liệu và trả về một đối tượng thực tế được ánh xạ với các hàng ở trong cơ sở dữ liệu. Đó chính là cơ chế tải EAGER, nó sẽ trả null nếu không có bản ghi tồn tại trong cơ sở dữ liệu.
Nên chọn cái nào?
Điều duy nhất khác biệt giữa hai phương thức này là về hiệu năng. Lazily loaded của phương thức getOne() sẽ tránh bị lòng vòng trong cơ sở dữ liệu (database roundtrip) từ JVM chừng nào nó chưa tác động đến cơ sở dữ liệu đến khi đối tượng proxy được trả về thực sự truy cập.
Có những tình huống khi bạn chỉ muốn lấy một entity từ cơ sở dữ liệu và gán nó tham chiếu đến một đối tượng khác, chỉ để duy trì mối quan hệ (OneToOne hoặc ManyToOne). Hãy làm một ví dụ cụ thể:
Chúng ta có một Employee thuộc một Department .
@Entity
@Table(name = "t_departments")
public class Department {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String name;
@Entity
@Table(name = "t_employees")
public class Employee {
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
private String name;
@ManyToOne //Đối tượng Employee cần tham chiếu đến Department
private Department department;
Bây giờ chúng ta hãy xem đoạn code dưới đây, chúng ta muốn tạo một employee mới và gán với một department .
@Service
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class HRService {
@Autowired
private DepartmentRepository departmentRepository;
@Autowired
private EmployeeRepository employeeRepository;
public Department createDepartment() {
Department dept = new Department();
dept.setName("Product & Engg");
return departmentRepository.save(dept);
}
public void createEmployee1(long deptId) {
final Department pne = departmentRepository.getOne(deptId); //Ghi chú 1
Employee employee = new Employee();
employee.setName("Foo 1");
employee.setDepartment(pne);
employeeRepository.save(employee);
}
Ghi chú 1: Chúng ta sẽ truy xuất một tham chiếu của đối tượng Department và gán nó với employee. Sử dụng getOne() sẽ tốt hơn sẽ dụng findById() trong trường hợp này, bởi vì nó sẽ không lấy cụ thể của đối tượng department.
Câu truy vấn SQL sẽ được tạo ra như sau:
insert into t_employees (department_id, name, id)
values (?, ?, ?)
Khi chúng ta sử dụng findById() thay vì getOne(), thì một yêu cầu bổ sung sẽ được gọi đến cơ sở dữ liệu, nó sẽ lấy đối tượng department, điều đó sẽ được thể hiện ở đoạn code dưới đây:
public void createEmployee2(long deptId) {
Optional<Department> pne = departmentRepository.findById(deptId);
Employee employee = new Employee();
employee.setName("Foo 1");
pne.ifPresent(department -> {
employee.setDepartment(department);
});
employeeRepository.save(employee);
}
Câu truy vấn SQL sẽ được sinh ra như sau:
select department0_.id as id1_4_0_, department0_.name
as name2_4_0_
from t_departments department0_
where department0_.id=? /*ghi chú 1*/
insert into t_employees (department_id, name, id) values (?, ?, ?)
Ghi chú 1: Chúng ta có thể thấy đây là một yêu cầu bổ sung đến cơ sở dữ liệu sẽ tạo eagerly fetch đến bản ghi department, và sẽ gán một đại diện đến bản ghi employee.
Tóm tắt so sánh
getOne() | findById() |
---|---|
Lazily loaded tham chiếu đến entity mục tiêu | Thực sự tải entity với id |
Hữu ích khi không yêu cầu truy cập vào thuộc tính của đối tượng | Đối tượng có thể truy cập đến tất cả các thuộc tính |
Ném ra EntityNotFoundException nếu đối tượng thực tế không tồn tại tại thời điểm yêu cầu truy cập | Trả về null nếu đối tượng tương ứng với Id không tồn tại |
Hiệu năng tốt hơn | Bắt buộc sẽ phải đi lòng vòng trong cơ sở dữ liệu (round-trip) |
Lời người dịch:
Các bạn có thể tìm hiểu thêm về lazily fetched và eager ở đây.
Bình luận