hibernate/hibernate-orm · error · IllegalArgumentException

Named query exists, but did not specify a resultClass

Error message

Named query exists, but did not specify a resultClass

What it means

Thrown when a named native query is executed via createNamedQuery(name, resultType) with a concrete result type, but the query was defined without a resultClass or resultSetMapping, so its ResultSetMapping has zero result builders. Hibernate refuses to guess how to map the JDBC result set onto the requested type. Types Hibernate always accepts (Object, Object[], Map, List, Tuple) bypass this check entirely.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java:2084

	/// a "tuple transformation" for the resultType.
	private void handleExplicitResultSetMapping() {
		if ( resultType != null ) {
			if ( isResultTypeAlwaysAllowed( resultType ) ) {
				setTupleTransformerForResultType( resultType );
			}
			else {
				checkResultType( resultType, resultSetMapping );
			}
		}
	}

	private void checkResultType(Class<?> resultType, ResultSetMapping resultSetMapping) {
		// resultType can be null if any of the deprecated methods were used to create the query
		if ( resultType != null && !isResultTypeAlwaysAllowed( resultType )) {
			switch ( resultSetMapping.getNumberOfResultBuilders() ) {
				case 0:
					if ( !resultSetMapping.isDynamic() ) {
						throw new IllegalArgumentException( "Named query exists, but did not specify a resultClass" );
					}
					break;
				case 1:
					final var actualResultJavaType = resultSetMapping.getResultBuilders().get( 0 ).getJavaType();
					if ( actualResultJavaType != null
							&& !boxedType( resultType ).isAssignableFrom( boxedType( actualResultJavaType ) ) ) {
						throw buildIncompatibleException( resultType, actualResultJavaType );
					}
					break;
				default:
					// The return type has to be a class with an appropriate constructor,
					// i.e. one whose parameter types match the types of the result builders.
					// If no such constructor is found, throw an IAE
					if ( !validConstructorFoundForResultType( resultType, resultSetMapping ) ) {
						throw new IllegalArgumentException(
								"The return type for a multivalued result set mapping should be Object[], Map, List, or Tuple"
								+ " or it must have an appropriate constructor"
						);

View on GitHub (pinned to fad1729dce)

Solutions

  1. Add resultClass = Person.class (or resultSetMapping = "...") to the @NamedNativeQuery definition so the mapping has result builders.
  2. If you cannot change the definition, call createNamedQuery("q") without a type and map rows yourself (Object[]/Tuple), or use Tuple.class which is always allowed.
  3. If the mapping was meant to be dynamic (built via addScalar at runtime), build it with a dynamic ResultSetMapping instead of a static named one.
  4. For orm.xml, add <result-class> or nest a <result-set-mapping> reference in the named-native-query entry.

Example fix

// before
@NamedNativeQuery(name = "allPersons", query = "select * from person")
List<Person> persons = em.createNamedQuery("allPersons", Person.class).getResultList(); // IllegalArgumentException

// after
@NamedNativeQuery(name = "allPersons", query = "select * from person", resultClass = Person.class)
List<Person> persons = em.createNamedQuery("allPersons", Person.class).getResultList();
Defensive patterns

Strategy: try-catch

Try / catch

try {
    List<Person> r = em.createNamedQuery("allPersons", Person.class).getResultList();
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().contains("did not specify a resultClass")) {
        // definition lacks resultClass: fall back to untyped access and map manually
        List<?> raw = em.createNamedQuery("allPersons").getResultList();
    } else { throw e; }
}

Prevention

When it happens

Trigger: Calling em.createNamedQuery("q", Person.class) or session.createNamedQuery("q", Person.class) where @NamedNativeQuery(name="q", query="...") declares neither resultClass nor resultSetMapping; likewise a named query registered in code via addNamedQuery without a result class, or an orm.xml <named-native-query> with no <result-class>. Fires from handleExplicitResultSetMapping -> checkResultType once the mapping has 0 result builders and is not dynamic.

Common situations: Migrating from Hibernate 5 where untyped named queries were tolerated; defining @NamedNativeQuery in annotations but forgetting resultClass; renaming DTOs so the declared resultClass is dropped; querying a named native query with a DTO type while the mapping was defined purely as scalars via addScalar (dynamic mapping would not throw, static one does).

Related errors


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