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êuThự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ậpTrả 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ơnBắ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.