✅ Short Answer
In HQL (and JPQL), you join tables by navigating entity associations using JOIN
, LEFT JOIN
, and their FETCH
variants — using entity relationships, not table names.
🔎 Detailed Explanation
Types of joins you can use in HQL:
1️⃣ JOIN
→ inner join: returns only records with matching related entities.
2️⃣ LEFT JOIN
→ left outer join: returns all parent entities, even if related entity is null.
3️⃣ FETCH JOIN
→ eager loads related entities in a single query.
🔹 Inner Join Example
Imagine:
@Entity
public class Order {
@ManyToOne
private Customer customer;
}
HQL join:
String hql = "SELECT o FROM Order o JOIN o.customer c WHERE c.name = :name";
List<Order> orders = session.createQuery(hql, Order.class)
.setParameter("name", "Alice")
.getResultList();
🔹 Left Join Example
Fetch orders with or without a customer:
String hql = "SELECT o FROM Order o LEFT JOIN o.customer c";
List<Order> orders = session.createQuery(hql, Order.class).getResultList();
🔹 Fetch Join Example
Fetch orders with customers loaded immediately (avoids lazy loading N+1 problem):
String hql = "SELECT o FROM Order o JOIN FETCH o.customer c WHERE c.status = :status";
List<Order> orders = session.createQuery(hql, Order.class)
.setParameter("status", "ACTIVE")
.getResultList();
📊 Quick Comparison Table
Join Type | Returns | Use Case |
---|---|---|
JOIN | Only parent entities with related | Filtered data needing matching children |
LEFT JOIN | All parent entities | Need parents even if no children exist |
JOIN FETCH | Parent + child fully loaded | Avoid lazy loading (N+1 issue) |
💡 Important Points
✅ HQL joins navigate entity relationships, not database table names or foreign keys directly.
✅ Joins happen via mapped fields (e.g., o.customer
), not explicit ON clauses like raw SQL.
✅ JOIN FETCH
is great to optimize performance when you know you’ll need related data.
📌 Key Takeaways
✅ Use JOIN
in HQL to connect related entities based on entity associations.
✅ Prefer JOIN FETCH
for eager loading when you want to load associations in one query.
✅ HQL doesn’t use SQL-style ON clauses → joins happen through object navigation.