skip to Main Content

If I use @ManyToOne with fetch type LAZY or any other jpa mapping annotations, will jpa run a query on the mapped entity to fetch the id?

For example.

Department

@Entity
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
    private List<Employee> employees = new ArrayList<>();

    // Constructors, getters, setters, and other properties
}

Employee:

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @ManyToOne
    @JoinColumn(name = "department_id", fetch= FetchType.LAZY)
    private Department department;

    // Constructors, getters, setters, and other properties
}

Now let’s say when I try find by on Employee and try to get id from the department corresponding to the employee, will Hibernate release an extra query to the Department class and fetch id from the department where employee = ‘x’. Or will the department id be stored with the employee entity and I can fetch it without increasing my query time? since the department id is present in the employee table in mysql?

Employee emp = employeeRepository.findByName("John");
String deparmentId = emp.getDeparment().getId(); //Extra query time or not? As Id is stored in the mysql table
emp.getDepartment().getName(); //Does causes extra query

2

Answers


  1. When you are querying with Spring JPA, you are given the instance of the data object(s) that represents the returned query value. This includes relational data as long as you have the proper annotations (which you do).

    JPA is mapping your database tables to an instance of your Entity. Any fields (via getter/setter) will be accessible to you immediately after you retrieve that Entity instance from a query (findBy, etc).

    So, for example. If you query for a single Department like so:

    Optional<Department> aDepartmentOpt = departmentRepository.findById(2);
    
    // assume we checked aDepartmentOpt for isEmpty()
    Department department = aDepartmentOpt.get();
    
    // lets get its employees
    List<Employee> dptEmployees = department.getEmployees();
    
    // do stuff with it... we have access to its data already
    for(Employee e : dptEmployees) {
        System.out.println("Employee " + e.getId() + " - " + e.getName());
    }
    

    And vice-versa:

    Optional<Employee> employeeOpt = employeeRepository.findById(2);
    
    // assume we checked employeeOpt for isEmpty()
    Employee employee = employeeOpt.get();
    
    // lets get the employees department
    Department department = employee.getDepartment();
    
    // do stuff with it... we dont need to query the department cause we have its data right here:
    System.out.println("Department " + department.getId() + " - " + department.getName());
    

    Note that when you use mappedBy, you are giving Spring JPA the field name so it can associate that with the instance of the data object for when you query.

    Hope this helped.

    Login or Signup to reply.
  2. yes, calling getDepartment() is expected to force a fetch to the database on lazy relationships. This is implementation (and configuration) specific on if the getDepartment() call immediately fetches from the database or if you get a proxy object that calling anything on then fetches from the DB, so I wouldn’t rely on it. If you want to have access to the FK value ("department_id") when only fetching the Employee instance without knowing if the department has already been fetched, map it as a basic Long property in the entity directly. You can have multiple mappings to the same "department_id" as long as you specify only one as writable (so all others as updatable=false, insertable=false). Ie

    @Entity
    public class Employee {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
        private String name;
    
        @Column(name = "department_id", insertable=false, updatable=false)
        private Long departmentId;
    
        @ManyToOne
        @JoinColumn(name = "department_id", fetch= FetchType.LAZY)
        private Department department;
    
        // Constructors, getters, setters, and other properties
    }
    

    This allows you to continue to use and reference department in queries to perform joins, but can allow you to use departmentId directly to avoid them. Ie:

      "select employee from Employee employee where employee.department.id =:deptId"
    

    vs

      "select employee from Employee employee where employee.departmentId = :deptId" 
    

    The first query should force an inner join to the department, while the second doesn’t.

    Depending on your application requirements, you can also reverse which property sets the "department_id" column value so that it can be set via the departmentId – this can be useful where you may want to merge in employees via REST without having to build back Department objects and handle reading and merging them in too – only the departmentId long is required for serialization.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search