hibernate/hibernate-orm · error · IllegalStateException
Locking not supported for ProcedureCall
Error message
Locking not supported for ProcedureCall
What it means
StoredProcedureQuery inherits setLockMode(LockModeType) from the JPA Query contract, and the JPA specification requires IllegalStateException when locking is requested on a stored-procedure query. ProcedureCallImpl implements exactly that (see the source comment on getLockMode): pessimistic locking is a Hibernate/JPA translation feature with no meaning for a database call.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/procedure/internal/ProcedureCallImpl.java:322
@Nonnull
public ProcedureCallImplementor<R> setMaxResults(int maxResult) {
checkNotClosed();
super.setMaxResults( maxResult );
return this;
}
@Override
@Nonnull
public ProcedureCallImplementor<R> setFirstResult(int startPosition) {
checkNotClosed();
super.setFirstResult( startPosition );
return this;
}
@Override
@Nonnull
public ProcedureCallImplementor<R> setLockMode(@Nonnull LockModeType lockMode) {
throw new IllegalStateException( "Locking not supported for ProcedureCall" );
}
@Override
@Nonnull
public ProcedureCallImplementor<R> addQueryHint(@Nonnull String hint) {
checkNotClosed();
super.addQueryHint( hint );
return this;
}
@Override @Deprecated
@SuppressWarnings("removal")
@Nonnull
public Query<R> setEntityGraph(@Nonnull EntityGraph<? super R> graph, @Nonnull GraphSemantic semantic) {
throw new UnsupportedOperationException( "Entity graph not supported for ProcedureCall" );
}
@Override @DeprecatedView on GitHub (pinned to fad1729dce)
Solutions
- Guard with instanceof: skip setLockMode when the query is a ProcedureCall/StoredProcedureQuery.
- If row locking is genuinely needed, do SELECT ... FOR UPDATE inside the procedure/function itself.
- Split the generic API so locking applies only to selection queries.
Example fix
// before
jakarta.persistence.Query q = em.createStoredProcedureQuery("reserve_stock");
q.setLockMode(LockModeType.PESSIMISTIC_WRITE); // throws IllegalStateException
// after
if (q instanceof StoredProcedureQuery) {
// locking handled inside the procedure (SELECT ... FOR UPDATE)
} else {
q.setLockMode(LockModeType.PESSIMISTIC_WRITE);
} Defensive patterns
Strategy: type-guard
Type guard
static boolean supportsLocking(jakarta.persistence.Query q) {
return !( q instanceof org.hibernate.procedure.ProcedureCall )
&& !( q instanceof StoredProcedureQuery );
} Try / catch
try {
q.setLockMode( lockMode );
} catch (IllegalStateException e) {
// spec-required failure for procedure calls; locking belongs inside the procedure
} Prevention
- Guard setLockMode with instanceof StoredProcedureQuery in generic DAOs.
- Implement locking inside the procedure (SELECT ... FOR UPDATE) where needed.
- Do not annotate repository methods that call procedures with @Lock.
When it happens
Trigger: Calling setLockMode(LockModeType.PESSIMISTIC_WRITE) (or OPTIMISTIC) on a StoredProcedureQuery/ProcedureCall — usually from generic DAO code that applies a lock mode to every query in a write transaction.
Common situations: Reusable repository layers with a findByQuery(Query q, LockModeType lock) method; @Lock annotations on repository methods that end up invoking procedures; copy-pasted query-building helpers.
Related errors
- Illegal attempt to get lock mode for a procedure call
- Duplicate named stored procedure '{}'
- NamedStoredProcedureQuery [%s] specified both resultClasses
- Entity '${persister.getEntityName()}' has no version and may
- No named stored procedure call with given name '{}'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/9790bb6acb92ea4f.
Report an issue: GitHub.