Difference between getOne and findById in Spring Data JPA?

Upasana | December 01, 2019 | 3 min read | 31,866 views | spring-boot


Both findById() and getOne() methods are used to retrieve an object from underlying datastore. But the underlying mechanism for retrieving records is different for both these methods, infact getOne() is lazy operation which does not even hit the database.

getOne() method

getOne() returns a reference to the entity with the given identifier. getOne internally invokes EntityManager.getReference() method. As per docs, this method will always return a proxy without hitting the database (lazily fetched). This method will throw EntityNotFoundException at the time of actual access if the requested entity does not exist in the database.

EntityManager.getReference(Class, Object) Docs

Get an instance, whose state may be lazily fetched. If the requested instance does not exist in the database, the EntityNotFoundException is thrown when the instance state is first accessed. (The persistence provider runtime is permitted to throw the EntityNotFoundException when getReference is called.) The application should not expect that the instance state will be available upon detachment, unless it was accessed by the application while the entity manager was open.

findById() method

This method will actually hit the database and return the real object mapping to a row in the database. It is EAGER loaded operation that returns null if no record exists in database.

Which one to choose?

The only real difference between these methods is about the performance. Lazily loaded getOne() method avoids database roundtrip from the JVM as it never hits the database until the properties of returned proxy object are actually accessed.

There are scenarios when you just want to get retrieve an entity from database and assign it as a reference to another object, just to maintain the relationship (OneToOne or ManyToOne). Lets take a concrete example:

We have a Employee that belong to 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  (1)
    private Department department;
1 Employee object needs a reference of Department.

Now lets consider the following code where we want to create a new employee and assign it to a 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); (1)
        Employee employee = new Employee();
        employee.setName("Foo 1");
        employee.setDepartment(pne);
        employeeRepository.save(employee);
    }
1 We are retrieving a reference of Department object and assigning it to employee. Using getOne() is better than using findById() in this particular case, because we need not to fetch the details of department object.
generated SQL traces
insert into t_employees (department_id, name, id) values (?, ?, ?)

When we use findById() instead of getOne(), then an additional call to database is made to retrieve the department object, as shown in the below code:

    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);
    }
generated SQL traces
select department0_.id as id1_4_0_, department0_.name as name2_4_0_ from t_departments department0_ where department0_.id=? (1)
insert into t_employees (department_id, name, id) values (?, ?, ?)
1 We can see here that an additional call to database is made to eagerly fetch the department record, and then assign it as a refernce in employee record.
Comparison Summary
getOne() findById()

Lazily loaded reference to target entity

Actually loads the entity for the given id

Useful only when access to properties of object is not required

Object is eagerly loaded so all attributes can be accessed

Throws EntityNotFoundException if actual object does not exist at the time of access invocation

Returns null if actual object corresponding to given Id does not exist

Better performance

An additional round-trip to database is required


Top articles in this category:
  1. Hibernate & Spring Data JPA interview questions
  2. Top 50 Spring Interview Questions
  3. Difference between method overloading and overriding in Java
  4. What is difference between Primary key and Unique Key
  5. Java Concurrency Interview Questions
  6. Cracking core java interviews - question bank
  7. Multi-threading Java Interview Questions for Investment Bank

Recommended books for interview preparation:

Find more on this topic:
Buy interview books

Java & Microservices interview refresher for experienced developers.