hibernate/hibernate-orm · error · SemanticException
Entity join did not specify a join condition [" + sqmJoin +
Error message
Entity join did not specify a join condition [" + sqmJoin + "] (specify a join condition with 'on' or use 'cross join')
What it means
An explicit entity join (a join targeting an entity type rather than an associated path) reached translation with a null join predicate while the resolved SQL join type is not CROSS. HQL requires such joins to carry an 'on' condition, because unlike association joins there is no implicit foreign-key predicate to fall back on.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/query/sqm/sql/spi/BaseSqmToSqlAstConverter.java:3924
null,
this
);
final var auxiliaryMapping = entityDescriptor.getAuxiliaryMapping();
if ( auxiliaryMapping != null ) {
auxiliaryMapping.applyPredicate( tableGroupJoin, loadQueryInfluencers );
}
final var joinPredicate = sqmJoin.getJoinPredicate();
if ( joinPredicate != null ) {
final var oldJoin = currentlyProcessingJoin;
currentlyProcessingJoin = sqmJoin;
tableGroupJoin.applyPredicate( visitNestedTopLevelPredicate( joinPredicate ) );
currentlyProcessingJoin = oldJoin;
}
else if ( correspondingSqlJoinType != SqlAstJoinType.CROSS ) {
// TODO: should probably be a SyntaxException
throw new SemanticException( "Entity join did not specify a join condition [" + sqmJoin + "]"
+ " (specify a join condition with 'on' or use 'cross join')" );
}
if ( transitive ) {
consumeExplicitJoins( sqmJoin, tableGroupJoin.getJoinedGroup() );
}
return tableGroup;
}
private TableGroup consumeDerivedJoin(SqmDerivedJoin<?> sqmJoin, TableGroup parentTableGroup, boolean transitive) {
if ( !sqmJoin.isLateral() ) {
// Temporarily push an empty FromClauseIndex to disallow access to aliases from the top query
// Only lateral subqueries are allowed to see the aliases
fromClauseIndexStack.push( new FromClauseIndex( null ) );
}
final var statement = (SelectStatement) sqmJoin.getQueryPart().accept( this );
if ( !sqmJoin.isLateral() ) {
fromClauseIndexStack.pop();View on GitHub (pinned to fad1729dce)
Solutions
- Add an explicit on condition: 'from Order o join Customer c on c.id = o.customerId'
- Use 'cross join' when you genuinely want no predicate: 'from Order o cross join Customer c'
- If the entities are associated, join through the association instead ('join o.customer c') so the predicate is implicit
Example fix
// before select o, c from Order o join Customer c // after select o, c from Order o join Customer c on c.id = o.customerId
Defensive patterns
Strategy: validation
Validate before calling
// Lint: explicit entity joins must have an on-condition or be cross joins
// crude HQL check - every 'join EntityName' not preceded by 'cross' must be followed by ... on ...
java.util.regex.Pattern p = java.util.regex.Pattern.compile("(?i)\\bjoin\\s+(?!cross\\b)[A-Z]\\w*\\s+(?!on\\b)");
if (p.matcher(hql).find()) {
throw new IllegalArgumentException("Entity join without on-condition; add 'on ...' or use 'cross join'");
} Try / catch
try {
return session.createQuery(hql).getResultList();
} catch (org.hibernate.query.SemanticException e) {
log.error("Join rejected: {}", hql, e);
throw e;
} Prevention
- Prefer association joins (join o.customer) over entity joins whenever a mapping exists
- Always pair an entity join with an on-clause in code review checklists
- Use 'cross join' explicitly when no predicate is intended so intent is visible
When it happens
Trigger: HQL 'from Order o join Customer c' - joining an unrelated entity with no on-clause and no cross join keyword; criteria code creating JpaEntityJoin (SqmRoot.join(Class)) and never calling .on(...); switching a join from cross to inner without adding a predicate.
Common situations: Writing SQL-style joins between unrelated entities in HQL; migrating native SQL joins to HQL; criteria queries that build joins dynamically and skip the on-clause on some code path; refactoring that deletes the on-predicate but keeps the join.
Related errors
- Insert conflict 'do update' clause with constraint name is n
- Summarization is not supported by DBMS
- Insert conflict 'do update' clause with constraint name is n
- Insert conflict 'do update' clause with constraint name is n
- Insert conflict 'do update' clause with constraint name is n
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/00347dbeaa3409b0.
Report an issue: GitHub.