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

  1. Add an explicit on condition: 'from Order o join Customer c on c.id = o.customerId'
  2. Use 'cross join' when you genuinely want no predicate: 'from Order o cross join Customer c'
  3. 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

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


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/00347dbeaa3409b0. Report an issue: GitHub.