How do you join two entities in a JPQL or HQL query when there is no mapped association between them?
answer
- JOIN EntityName alias ON condition
- Hibernate 5.1+, standard in JPA 3.2
- Only entity join can be LEFT
- Old way: two roots in FROM + WHERE
- Repeating the condition = missing mapping
basics
~20 sUse an entity join with an explicit condition: FROM A a JOIN B b ON b.code = a.code. Hibernate has supported this since 5.1 and Jakarta Persistence 3.2 standardises it. The older portable fallback is listing both entities in FROM and joining in WHERE.
solid answer
~50 sTwo options. **Entity join (preferred).** Name an entity, not a path, on the right of the join and supply your own condition: ``` select a, b from Account a join Ledger b on b.accountCode = a.code ``` Hibernate has allowed this since 5.1, and Jakarta Persistence 3.2 standardises joining an entity with an ON clause. It supports `LEFT JOIN` too, which is the real reason to use it — you can outer-join unrelated tables, which the old trick cannot do. **Cross join in FROM (legacy fallback).** `from Account a, Ledger b where b.accountCode = a.code` produces a Cartesian product restricted in WHERE, which the database turns into an ordinary inner join. It is portable to older JPA versions but cannot express an outer join, and forgetting the WHERE predicate produces a genuine Cartesian product. Before reaching for either, ask whether the relationship deserves a mapping — but ad-hoc joins are legitimate for reporting, for cross-module entities you deliberately do not couple, and for joining on a business key.
code
java · 8 linesem.createQuery("""
select a.code, l.balance
from Account a
left join Ledger l on l.accountCode = a.code
where a.status = :status
""", Object[].class)
.setParameter("status", Status.ACTIVE)
.getResultList();go deeper
Know that JPQL can join unmapped entities with an explicit ON condition, and that native SQL is not the only option.
Show both forms, know that only the entity join supports LEFT, and mention the version support caveat.
Judge when an ad-hoc join is legitimate versus when the mapping is missing, and note the risk of unvalidated join conditions.
Use ad-hoc joins as a deliberate decoupling tool between modules that share only identifiers, weighing query duplication against the coupling a mapped association introduces.
## The problem JPQL joins normally traverse a mapped association. If `Account` and `Ledger` share a business key but no `@ManyToOne` connects them — perhaps deliberately, because they belong to different modules — there is no path to write. Historically this pushed people to native SQL. ## Entity joins (ad-hoc joins) Hibernate 5.1 introduced joining an entity name directly with an arbitrary ON condition, and Jakarta Persistence 3.2 brought the same capability into the specification: ``` select a.id, l.balance from Account a join Ledger l on l.accountCode = a.code where a.status = :status ``` Properties of this form: - The right side is an **entity name**, not an association path. - The ON clause is mandatory in practice — without it you get an unrestricted join. - `LEFT JOIN` works, so you can keep accounts that have no ledger row. This is the capability the old cross-join trick lacks. - The joined alias is a full entity alias: you can select it, filter on it, navigate its own associations from it. ## The legacy alternative: implicit cross join ``` select a, l from Account a, Ledger l where l.accountCode = a.code ``` Listing multiple roots in FROM is a Cartesian product; the WHERE predicate reduces it, and every database planner recognises the shape and executes it as an inner join. This works on any JPA version. Two caveats: it can only express inner-join semantics, and if the predicate is dropped in a refactor you silently get an N×M explosion, which on production-sized tables is a serious incident rather than a slow query. ## When ad-hoc joins are the right answer - **Reporting and analytics queries** that combine entities the domain model has no reason to link. - **Deliberate module decoupling**: two aggregates reference each other only by identifier, so no association exists by design, yet one screen needs both. - **Joining on a business key** rather than a surrogate foreign key — for example matching by an external system's code. - **Temporal or versioned lookups**, where the "association" depends on a date range and cannot be a single foreign key. ## When to map instead If the two entities really do have a stable one-to-many or many-to-one relationship and you find yourself writing the same ON condition everywhere, the mapping is missing. A mapped association gives you type-safe navigation, cascading, fetch control and entity-graph participation; an ad-hoc join gives you none of that. Repeating a join condition across many queries is a design smell, not a JPQL technique. ## Related capability: derived roots and subquery joins Hibernate also allows joining a *subquery* (a derived root) with an ON condition in HQL, which is how you express lateral-style lookups such as "the latest ledger row per account". Jakarta Persistence 3.2 likewise adds subqueries in the FROM clause. This is more advanced and less portable, but it is the natural next step once ad-hoc joins are on the table. ## Practical notes - Nothing about an entity join changes the persistence context: joined entities you select are managed as usual, and joined entities you merely filter on are not loaded. - Because there is no mapping, nothing validates your condition. A typo in the ON clause produces a silently wrong result set instead of a compile-time or bootstrap error — which is a real argument for keeping these queries covered by tests. - Column types must be compatible; joining a string code to a numeric id will either fail or perform terribly due to implicit conversion.
- What is the main capability an entity join has that the multiple-roots-in-FROM form does not?Outer-join semantics. Listing two roots and filtering in WHERE can only express an inner join, so parents without a match are lost. An entity join can be written as LEFT JOIN ... ON, keeping every row of the left side with nulls for the unmatched side — essential for reports that must list all parents.
- When should you add a real @ManyToOne mapping instead of repeating an ad-hoc join?When the relationship is genuinely part of the domain model, stable, and used by many queries. A mapping buys type-safe navigation, fetch and graph control, cascading and lifecycle integration, and moves the join condition out of every query string. Ad-hoc joins are for relationships you deliberately do not model, such as cross-module or business-key matches.
saying these in an interview costs you the question
- Claiming JPQL cannot join unrelated entities so you must use native SQL
- Thinking multiple roots in FROM can produce an outer join
- Omitting the ON/WHERE condition and shipping an accidental Cartesian product
- Assuming an ad-hoc join populates the entities' association fields
- Using ad-hoc joins routinely for a relationship that clearly belongs in the mapping