What is the difference between JOIN and JOIN FETCH when using JPA and Hibernate
JavaHibernateJoinHqlFetchJava Problem Overview
Please help me understand where to use a regular JOIN and where a JOIN FETCH. For example, if we have these two queries
FROM Employee emp
JOIN emp.department dep
and
FROM Employee emp
JOIN FETCH emp.department dep
Is there any difference between them? If yes, which one to use when?
Java Solutions
Solution 1 - Java
In this two queries, you are using JOIN to query all employees that have at least one department associated.
But, the difference is: in the first query you are returning only the Employes for the Hibernate. In the second query, you are returning the Employes and all Departments associated.
So, if you use the second query, you will not need to do a new query to hit the database again to see the Departments of each Employee.
You can use the second query when you are sure that you will need the Department of each Employee. If you not need the Department, use the first query.
I recomend read this link if you need to apply some WHERE condition (what you probably will need): https://stackoverflow.com/questions/5816417/how-to-properly-express-jpql-join-fetch-with-where-clause-as-jpa-2-criteriaq
Update
If you don't use fetch
and the Departments continue to be returned, is because your mapping between Employee and Department (a @OneToMany
) are setted with FetchType.EAGER
. In this case, any HQL (with fetch
or not) query with FROM Employee
will bring all Departments. Remember that all mapping *ToOne (@ManyToOne
and @OneToOne
) are EAGER by default.
Solution 2 - Java
in this link i mentioned before on the comment, read this part :
> A "fetch" join allows associations or collections of values to be > initialized along with their parent objects using a single select. > This is particularly useful in the case of a collection. It > effectively overrides the outer join and lazy declarations of the > mapping file for associations and collections.
this "JOIN FETCH" will have it's effect if you have (fetch = FetchType.LAZY) property for a collection inside entity(example bellow).
And it is only effect the method of "when the query should happen". And you must also know this:
> hibernate have two orthogonal notions : when is the association fetched and how > is it fetched. It is important that you do not confuse them. We use > fetch to tune performance. We can use lazy to define a contract for > what data is always available in any detached instance of a particular > class.
when is the association fetched --> your "FETCH" type
how is it fetched --> Join/select/Subselect/Batch
In your case, FETCH will only have it's effect if you have department as a set inside Employee, something like this in the entity:
@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;
when you use
FROM Employee emp
JOIN FETCH emp.department dep
you will get emp
and emp.dep
. when you didnt use fetch you can still get emp.dep
but hibernate will process another select to the database to get that set of department.
so its just a matter of performance tuning, about you want to get all result(you need it or not) in a single query(eager fetching), or you want to query it latter when you need it(lazy fetching).
Use eager fetching when you need to get small data with one select(one big query). Or use lazy fetching to query what you need latter(many smaller query).
use fetch when :
-
no large unneeded collection/set inside that entity you about to get
-
communication from application server to database server too far and need long time
-
you may need that collection latter when you don't have the access to it(outside of the transactional method/class)
Solution 3 - Java
JOIN
When using JOIN
against an entity associations, JPA will generate a JOIN between the parent entity and the child entity tables in the generated SQL statement.
So, taking your example, when executing this JPQL query:
FROM Employee emp
JOIN emp.department dep
Hibernate is going to generate the following SQL statement:
SELECT emp.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id
> Note that the SQL SELECT
clause contains only the employee
table columns, and not the department
ones. To fetch the department
table columns, we need to use JOIN FETCH
instead of JOIN
.
JOIN FETCH
So, compared to JOIN
, the JOIN FETCH
allows you to project the joining table columns in the SELECT
clause of the generated SQL statement.
So, in your example, when executing this JPQL query:
FROM Employee emp
JOIN FETCH emp.department dep
Hibernate is going to generate the following SQL statement:
SELECT emp.*, dept.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id
> Note that, this time, the department
table columns are selected as well, not just the ones associated with the entity listed in the FROM
JPQL clause.
Also, JOIN FETCH
is a great way to address the LazyInitializationException
when using Hibernate as you can initialize entity associations using the FetchType.LAZY
fetching strategy along with the main entity you are fetching.
Solution 4 - Java
If you have @oneToOne
mapping set to FetchType.LAZY
and you use second query (because you need Department objects to be loaded as part of Employee objects) what Hibernate will do is, it will issue queries to fetch Department objects for every individual Employee object it fetches from DB.
Later, in the code you might access Department objects via Employee to Department single-valued association and Hibernate will not issue any query to fetch Department object for the given Employee.
Remember, Hibernate still issues queries equal to the number of Employees it has fetched. Hibernate will issue same number of queries in both above queries, if you wish to access Department objects of all Employee objects
Solution 5 - Java
Dherik : I'm not sure about what you say, when you don't use fetch the result will be of type : List<Object[ ]>
which means a list of Object tables and not a list of Employee.
Object[0] refers an Employee entity
Object[1] refers a Departement entity
When you use fetch, there is just one select and the result is the list of Employee List<Employee>
containing the list of departements. It overrides the lazy declaration of the entity.
Solution 6 - Java
Difference JOIN and JOIN FETCH in JPQL
TLDR: The FETCH
keyword tells the entityManager to also fetch the associated entities which are joined eagerly (when this is not already the case).
Lets say we an user entity and userInfo entity. The user entity has a @OneToMany
relationship with userInfo as follows:
import javax.persistence.*;
@Entity
@Table(name= "test_user")
public class User {
// ... user properties id etc
@OneToMany(mappedBy = "user" fetch = FetchType.LAZY)
private List<UserInfo> infoList;
}
And suppose we have the following Queries (this Spring data JPA syntax, however does not matter how JPQL is constructed):
@Query("SELECT user FROM User user JOIN user.infoList info")
public List<User> getUsersJoin();
@Query("SELECT user FROM User user JOIN FETCH user.infoList info")
public List<User> getUsersJoinFetch();
The first Query with only the JOIN
keyword will generate the following SQL:
select u.id, u.email, u.name from test_user u
inner join test_user_data on u.id=test_user_data.user_id;
As you can see it will only fetch data from the test_user table and not from the test_user_data table. This can also be seen in the debugger as follows:
As can be seen we do not have the List<userData>
on our User
object because this is not loaded by default.
Now lets check out the generated SQL for the Query with JOIN FETCH
:
select test_user.id, data.id, test_user.email, test_user.name,
data.data, data.user_id, data.user_id, data.id from test_user test_user
inner join test_user_data data on test_user.id=data.user_id
As can be seen we are now fetching the data from both the test_user and the test_user_data table in the join. This can also be seen in the debugger as follows:
As can be observed we now have access to the List<userData>
within the User
Object.