hibernate/hibernate-orm · error · UnsupportedOperationException

should only be used to bind null

Error message

 should only be used to bind null

What it means

NullJdbcType is Hibernate's placeholder JdbcType for the SQL NULL type; its binder implements only doBindNull(). If it ends up as the resolved JdbcType for a parameter and a non-null value is bound to a PreparedStatement, doBind throws UnsupportedOperationException('... should only be used to bind null'). The underlying problem is that Hibernate could not determine a real JdbcType for the binding - typically an untyped query parameter.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/type/descriptor/jdbc/NullJdbcType.java:71

	}

	@Override
	public <X> ValueBinder<X> getBinder(JavaType<X> javaType) {
		return new BasicBinder<>( javaType, this ) {

			@Override
			protected void doBindNull(PreparedStatement st, int index, WrapperOptions options) throws SQLException {
				st.setNull( index, Types.NULL );
			}

			@Override
			protected void doBindNull(CallableStatement st, String name, WrapperOptions options) throws SQLException {
				st.setNull( name, Types.NULL );
			}

			@Override
			protected void doBind(PreparedStatement st, X value, int index, WrapperOptions options) {
				throw new UnsupportedOperationException( getClass().getName() + " should only be used to bind null" );
			}

			@Override
			protected void doBind(CallableStatement st, X value, String name, WrapperOptions options) {
				throw new UnsupportedOperationException( getClass().getName() + " should only be used to bind null" );
			}
		};
	}
}

View on GitHub (pinned to fad1729dce)

Solutions

  1. Bind the parameter with an explicit type: setParameter(name, value, Integer.class) / setParameter(name, value, StringType.INSTANCE) or the typed overloads.
  2. For native queries prefer positional '?' placeholders combined with typed binding.
  3. If it happens during flush of a mapped attribute, review that attribute's @JdbcType/@Type/@JdbcTypeCode resolution so a real JdbcType is chosen.

Example fix

// before
Query q = em.createNativeQuery("select * from t where status = :p");
q.setParameter("p", "ACTIVE"); // type inference falls back to NullJdbcType

// after: bind with an explicit type
q.setParameter("p", "ACTIVE", String.class);
Defensive patterns

Strategy: validation

Validate before calling

// Central typed binding helper prevents untyped parameters
static <T> void bind(jakarta.persistence.Query q, String name, T value, Class<T> type) {
    if (value != null) {
        q.setParameter(name, value, type); // typed overload never resolves to NullJdbcType
    } else {
        q.setParameter(name, null, type);
    }
}

Try / catch

try {
    query.setParameter("p", value).getResultList();
} catch (UnsupportedOperationException e) {
    if (e.getMessage() != null && e.getMessage().contains("should only be used to bind null")) {
        // parameter had no resolvable JdbcType: rebind with setParameter(name, value, ExpectedType.class)
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Binding a non-null value to a parameter whose JdbcType resolved to NullJdbcType: native queries with setParameter(name, value) and no explicit type, StoredProcedureQuery parameters registered without a concrete type, or criteria/HQL parameters whose type inference failed.

Common situations: Native queries with named parameters bound without a type hint; stored-procedure parameter registration missing the Java type; mappings where the attribute's type resolution fell back to the NULL type after refactoring.

Related errors


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