hibernate/hibernate-orm · error · GenericJDBCException

Could not extract row count from CallableStatement

Error message

Could not extract row count from CallableStatement

What it means

Expectation.OutParameter verifies write outcomes by reading the affected-row count from a stored-procedure OUT parameter via CallableStatement.getInt(parameterIndex()) (prepare() registers it as Types.NUMERIC). If that getInt call throws SQLException - parameter not registered, wrong index, or non-numeric value - Hibernate logs the SQLException and wraps it in GenericJDBCException with this message instead of verifying the row count.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/jdbc/Expectation.java:227

	/**
	 * Essentially identical to {@link RowCount} except that the row count
	 * is obtained via an output parameter of a {@linkplain CallableStatement
	 * stored procedure}.
	 * <p>
	 * Statement batching is disabled when {@code OutParameter} is used.
	 *
	 * @since 6.5
	 */
	class OutParameter implements Expectation {
		@Override
		public final void verifyOutcome(int rowCount, PreparedStatement statement, int batchPosition, String sql) {
			final int result;
			try {
				result = toCallableStatement( statement ).getInt( parameterIndex() );
			}
			catch ( SQLException sqle ) {
				sqlExceptionHelper.logExceptions( sqle, "Could not extract row count from CallableStatement" );
				throw new GenericJDBCException( "Could not extract row count from CallableStatement", sqle );
			}
			if ( batchPosition < 0 ) {
				checkNonBatched( expectedRowCount(), result, sql );
			}
			else {
				checkBatched( expectedRowCount(), result, batchPosition, sql );
			}
		}

		@Override
		public void validate(boolean callable) throws MappingException {
			if ( !callable ) {
				throw new MappingException( "Expectation.OutParameter operates exclusively on CallableStatements" );
			}
		}

		@Override
		public int getNumberOfParametersUsed() {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Make the row-count available as a NUMERIC out parameter at the index the expectation reads (typically the first parameter) and keep callable = true on the annotation
  2. Align the procedure signature with the custom SQL: {? = call proc(?, ?)} if the count is a return value, or {call proc(?, ?, ?)} with the count as an explicit out parameter
  3. Inspect the wrapped SQLException (cause) to see whether registration, index, or type conversion failed

Example fix

// before
@SQLInsert(sql = "{ call insert_person(?, ?, ?) }", callable = true) // no out parameter for row count
@Expectation(type = ExpectationType.OUT_PARAM)
class Person { }

// after
@SQLInsert(sql = "{ ? = call insert_person(?, ?, ?) }", callable = true) // returns affected row count
@Expectation(type = ExpectationType.OUT_PARAM)
class Person { }
Defensive patterns

Strategy: try-catch

Try / catch

try {
    session.flush();
} catch (org.hibernate.exception.GenericJDBCException e) {
    if (e.getMessage() != null && e.getMessage().contains("CallableStatement")) {
        // out parameter not registered / wrong index / non-numeric:
        // check the stored procedure signature and parameterIndex of the expectation
    }
    throw e;
}

Prevention

When it happens

Trigger: Custom SQL with @Expectation(type = ExpectationType.OUT_PARAM) (or a custom subclass of Expectation.OutParameter) where the callable statement has no NUMERIC out parameter at the expected index, e.g. the procedure does not return the row count, or the out parameter sits at a different position than parameterIndex() expects.

Common situations: Stored-procedure insert/update/delete where the count parameter was dropped or reordered during a proc refactor; mixing function-style {?=call...} with procedure-style {call...} signatures; drivers that require out parameters to be registered before input parameters.

Related errors


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