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
- Add resultClass = Person.class (or resultSetMapping = "...") to the @NamedNativeQuery definition so the mapping has result builders.
- 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.
- 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.
- 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
- Always declare resultClass or resultSetMapping on every @NamedNativeQuery.
- Add a startup smoke test that iterates all named queries from the metamodel/annotations and executes createNamedQuery with each declared type.
- Prefer resultClass = Tuple.class or Object[].class for ad-hoc projections.
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
- The return type for a multivalued result set mapping should
- No result set mapping with given name '{}'
- Owner alias [{ownerAlias}] is unknown for alias [{alias}]
- null is not a valid query name
- Named query definition is null
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/cfdb549851f93a6e.
Report an issue: GitHub.