{"record":{"id":"8ec7713136928483","repo":"mybatis/mybatis-3","slug":"method-name-needs-either-a-resultmap-annotation","errorCode":null,"errorMessage":"method {name} needs either a @ResultMap annotation, a @ResultType annotation, or a resultType attribute in XML so a ResultHandler can be used as a parameter.","messagePattern":"method (.+?) needs either a @ResultMap annotation, a @ResultType annotation, or a resultType attribute in XML so a ResultHandler can be used as a parameter\\.","errorType":"exception","errorClass":"BindingException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/binding/MapperMethod.java","lineNumber":127,"sourceCode":"      result = null;\n    } else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {\n      result = rowCount;\n    } else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {\n      result = (long) rowCount;\n    } else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {\n      result = rowCount > 0;\n    } else {\n      throw new BindingException(\n          \"Mapper method '\" + command.getName() + \"' has an unsupported return type: \" + method.getReturnType());\n    }\n    return result;\n  }\n\n  private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {\n    MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());\n    if (!StatementType.CALLABLE.equals(ms.getStatementType())\n        && void.class.equals(ms.getResultMaps().get(0).getType())) {\n      throw new BindingException(\n          \"method \" + command.getName() + \" needs either a @ResultMap annotation, a @ResultType annotation,\"\n              + \" or a resultType attribute in XML so a ResultHandler can be used as a parameter.\");\n    }\n    Object param = method.convertArgsToSqlCommandParam(args);\n    if (method.hasRowBounds()) {\n      RowBounds rowBounds = method.extractRowBounds(args);\n      sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));\n    } else {\n      sqlSession.select(command.getName(), param, method.extractResultHandler(args));\n    }\n  }\n\n  private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {\n    List<E> result;\n    Object param = method.convertArgsToSqlCommandParam(args);\n    if (method.hasRowBounds()) {\n      RowBounds rowBounds = method.extractRowBounds(args);\n      result = sqlSession.selectList(command.getName(), param, rowBounds);","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/binding/MapperMethod.java#L109-L145","documentation":"executeWithResultHandler() requires that MyBatis knows the row type before streaming rows into a ResultHandler. For non-CALLABLE statements, if the first result map's type is void.class (the default placeholder), MyBatis cannot create result objects, so it demands an explicit result type declaration.","triggerScenarios":"Declaring 'void processAll(ResultHandler<X> handler)' in a mapper interface where the XML <select> has no resultType/resultMap attribute and no @ResultMap/@ResultType annotation is present; stored procedures (CALLABLE) are exempt because OUT params carry types.","commonSituations":"Streaming large result sets with ResultHandler and forgetting resultType; annotation-based mappers using @Select without @ResultType.","solutions":["Add resultType=\"com.example.X\" (or a resultMap attribute) to the <select> element","Or annotate the mapper method with @ResultMap(\"...id...\") or @ResultType(X.class)"],"exampleFix":"<!-- before -->\n<select id=\"processAll\">SELECT * FROM users</select>\n<!-- after -->\n<select id=\"processAll\" resultType=\"com.example.User\">SELECT * FROM users</select>","handlingStrategy":"validation","validationCode":"MappedStatement ms = configuration.getMappedStatement(\"com.example.UserMapper.processAll\");\nClass<?> rowType = ms.getResultMaps().get(0).getType();\nif (rowType == void.class) {\n  throw new IllegalStateException(\"Add resultType/resultMap before using ResultHandler\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pair ResultHandler mapper methods with an explicit resultType or @ResultType","Add a build-time check that every <select> declares resultType or resultMap"],"tags":["mybatis","binding","result-handler","result-type"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}