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.