hibernate/hibernate-orm · error · IllegalQueryOperationException

Locking with joins is not supported

Error message

Locking with joins is not supported

What it means

Informix does not allow FOR UPDATE on queries that contain joins. InformixSqlAstTranslator.determineLockingStrategy() therefore checks the locking clause strategy for joins: if follow-on locking is DISALLOWED there is no legal way to apply the lock, and it throws IllegalQueryOperationException('Locking with joins is not supported').

Source

Thrown at hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixSqlAstTranslator.java:351

	@Override
	protected void visitUpdateStatementOnly(UpdateStatement statement) {
		if ( hasNonTrivialFromClause( statement.getFromClause() ) ) {
			visitUpdateStatementEmulateMerge( statement );
		}
		else {
			super.visitUpdateStatementOnly( statement );
		}
	}

	@Override
	protected LockStrategy determineLockingStrategy(QuerySpec querySpec, Locking.FollowOn followOnStrategy) {
		final LockStrategy lockStrategy = super.determineLockingStrategy( querySpec, followOnStrategy );
		final LockingClauseStrategy lockingClauseStrategy = getLockingClauseStrategy();
		if ( lockingClauseStrategy != null && lockingClauseStrategy.containsJoins() ) {
			// Informix does not allow FOR UPDATE when the query also contains joins
			if ( followOnStrategy == Locking.FollowOn.DISALLOW ) {
				throw new IllegalQueryOperationException( "Locking with joins is not supported" );
			}
			else if ( followOnStrategy == Locking.FollowOn.IGNORE ) {
				return LockStrategy.NONE;
			}
			else {
				return LockStrategy.FOLLOW_ON;
			}
		}
		else {
			return lockStrategy;
		}
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Split the query: lock the root entity in a join-free statement, then load associated data separately
  2. Allow follow-on locking: remove lock-timeout hints that force single-statement locking so Hibernate can lock rows after fetching
  3. Switch the affected flows to optimistic (@Version) locking, which has no join restriction

Example fix

// before
Query<A> q = session.createQuery(
    "select a from Auction a join a.bids b where b.amount > :min", Auction.class);
q.setLockMode(LockModeType.PESSIMISTIC_WRITE); // -> IllegalQueryOperationException

// after - lock in a join-free statement, then load relations
Auction a = session.createQuery("select a from Auction a where a.id = :id", Auction.class)
        .setParameter("id", id)
        .unwrap(org.hibernate.query.Query.class)
        .setLockOptions(new LockOptions(LockMode.PESSIMISTIC_WRITE))
        .getSingleResult();
a.getBids().size(); // load collection after the root is locked
Defensive patterns

Strategy: validation

Validate before calling

boolean hasJoins = hql.toLowerCase().matches("(?s).*\\bjoin\\b.*");
boolean pessimistic = lockMode != LockMode.NONE && lockMode != LockMode.OPTIMISTIC
        && lockMode != LockMode.OPTIMISTIC_FORCE_INCREMENT;
if ( session.getJdbcServices().getDialect() instanceof InformixDialect && hasJoins && pessimistic ) {
    // lock a join-free root query instead, or allow follow-on locking
}

Type guard

static boolean isInformix(Dialect d) { return d instanceof InformixDialect; }

Try / catch

try {
    query.setLockMode(LockModeType.PESSIMISTIC_WRITE).getResultList();
} catch (IllegalQueryOperationException e) {
    if ( String.valueOf(e.getMessage()).contains("Locking with joins") ) {
        // re-issue as: lock root by id in a join-free query, then fetch relations
    }
    throw e;
}

Prevention

When it happens

Trigger: Applying pessimistic locking (LockOptions/LockModeType.PESSIMISTIC_READ or WRITE, or setLockMode on a Query) to an HQL query with a join on Informix, in a context where follow-on locking is disallowed (e.g., lock timeout hints that force single-statement locking). With FollowOn.IGNORE the lock is silently dropped (LockStrategy.NONE); with the default PREFER, follow-on locking is used instead.

Common situations: Detached-object reload patterns ('select a from A a join a.items where ... for update') ported to Informix; batch jobs using pessimistic locks generically over entity graphs; enabling pessimistic locking globally via lock timeout properties.

Related errors


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