{"record":{"id":"cfdb549851f93a6e","repo":"hibernate/hibernate-orm","slug":"named-query-exists-but-did-not-specify-a-resultcl","errorCode":null,"errorMessage":"Named query exists, but did not specify a resultClass","messagePattern":"Named query exists, but did not specify a resultClass","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java","lineNumber":2084,"sourceCode":"\t/// a \"tuple transformation\" for the resultType.\n\tprivate void handleExplicitResultSetMapping() {\n\t\tif ( resultType != null ) {\n\t\t\tif ( isResultTypeAlwaysAllowed( resultType ) ) {\n\t\t\t\tsetTupleTransformerForResultType( resultType );\n\t\t\t}\n\t\t\telse {\n\t\t\t\tcheckResultType( resultType, resultSetMapping );\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate void checkResultType(Class<?> resultType, ResultSetMapping resultSetMapping) {\n\t\t// resultType can be null if any of the deprecated methods were used to create the query\n\t\tif ( resultType != null && !isResultTypeAlwaysAllowed( resultType )) {\n\t\t\tswitch ( resultSetMapping.getNumberOfResultBuilders() ) {\n\t\t\t\tcase 0:\n\t\t\t\t\tif ( !resultSetMapping.isDynamic() ) {\n\t\t\t\t\t\tthrow new IllegalArgumentException( \"Named query exists, but did not specify a resultClass\" );\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tfinal var actualResultJavaType = resultSetMapping.getResultBuilders().get( 0 ).getJavaType();\n\t\t\t\t\tif ( actualResultJavaType != null\n\t\t\t\t\t\t\t&& !boxedType( resultType ).isAssignableFrom( boxedType( actualResultJavaType ) ) ) {\n\t\t\t\t\t\tthrow buildIncompatibleException( resultType, actualResultJavaType );\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\t// The return type has to be a class with an appropriate constructor,\n\t\t\t\t\t// i.e. one whose parameter types match the types of the result builders.\n\t\t\t\t\t// If no such constructor is found, throw an IAE\n\t\t\t\t\tif ( !validConstructorFoundForResultType( resultType, resultSetMapping ) ) {\n\t\t\t\t\t\tthrow new IllegalArgumentException(\n\t\t\t\t\t\t\t\t\"The return type for a multivalued result set mapping should be Object[], Map, List, or Tuple\"\n\t\t\t\t\t\t\t\t+ \" or it must have an appropriate constructor\"\n\t\t\t\t\t\t);","sourceCodeStart":2066,"sourceCodeEnd":2102,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/query/sql/internal/NativeQueryImpl.java#L2066-L2102","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\n@NamedNativeQuery(name = \"allPersons\", query = \"select * from person\")\nList<Person> persons = em.createNamedQuery(\"allPersons\", Person.class).getResultList(); // IllegalArgumentException\n\n// after\n@NamedNativeQuery(name = \"allPersons\", query = \"select * from person\", resultClass = Person.class)\nList<Person> persons = em.createNamedQuery(\"allPersons\", Person.class).getResultList();","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    List<Person> r = em.createNamedQuery(\"allPersons\", Person.class).getResultList();\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().contains(\"did not specify a resultClass\")) {\n        // definition lacks resultClass: fall back to untyped access and map manually\n        List<?> raw = em.createNamedQuery(\"allPersons\").getResultList();\n    } else { throw e; }\n}","preventionTips":["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."],"tags":["hibernate","native-query","named-query","result-mapping","illegalargumentexception"],"backgroundTag":"named-query-missing-result-class","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}