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
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:
And vice-versa:
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.
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
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:
vs
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.