{"record":{"id":"baa4461bcdb0ab90","repo":"baomidou/mybatis-plus","slug":"method-s-needs-either-a-resultmap-annotation-a","errorCode":null,"errorMessage":"method %s 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":"mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperMethod.java","lineNumber":166,"sourceCode":"        if (method.returnsVoid()) {\n            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(\"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(\"method \" + command.getName()\n                + \" 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 = this.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 = this.convertArgsToSqlCommandParam(args);\n        if (method.hasRowBounds()) {\n            RowBounds rowBounds = method.extractRowBounds(args);\n            result = sqlSession.selectList(command.getName(), param, rowBounds);","sourceCodeStart":148,"sourceCodeEnd":184,"githubUrl":"https://github.com/baomidou/mybatis-plus/blob/bf67d907478c724120bf76292da54abf9e73c2b3/mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperMethod.java#L148-L184","documentation":"executeWithResultHandler runs when a mapper method takes a ResultHandler parameter. MyBatis streams rows into the handler instead of materializing results, so it must know the row type; if the statement's result map resolves to void.class (no resultType/resultMap) and it is not a CALLABLE statement, execution is refused.","triggerScenarios":"Mapper method like 'void findAll(ResultHandler<User> handler)' whose statement (annotation or XML) declares no resultType/@ResultMap — e.g. a bare @Select without resultType, or a <select> element missing the attribute.","commonSituations":"Streaming large result sets with ResultHandler and forgetting resultType; annotation @Select(\"SELECT * FROM user\") with no @ResultType; migrating from returning-list methods and dropping the resultType attribute.","solutions":["Add resultType (or resultMap) to the statement: @Select(value = \"...\", resultType = User.class) or <select id=\"findAll\" resultType=\"User\">","For annotation mappers use @ResultType(User.class) on the method or the statement's resultType attribute","Alternatively drop the ResultHandler parameter and return a typed List if streaming is not required"],"exampleFix":"// before\n@Select(\"SELECT * FROM user\")\nvoid scanUsers(ResultHandler<User> handler);\n// after\n@Select(value = \"SELECT * FROM user\", resultType = User.class)\nvoid scanUsers(ResultHandler<User> handler);","handlingStrategy":"validation","validationCode":"// Before exposing a ResultHandler method, verify the statement declares a row type\nMappedStatement ms = configuration.getMappedStatement(\"com.example.UserMapper.scanUsers\");\nClass<?> rowType = ms.getResultMaps().get(0).getType();\nif (void.class.equals(rowType)) throw new IllegalStateException(\"scanUsers needs resultType/@ResultType\");","typeGuard":null,"tryCatchPattern":"catch BindingException on first streaming call and fail with guidance to add resultType; do not swallow — every call would fail identically.","preventionTips":["Always set resultType (or @ResultType) on statements consumed via ResultHandler","Write one streaming integration test per ResultHandler mapper method","Remember ResultHandler methods must return void"],"tags":["mybatis","result-handler","streaming","result-type"],"backgroundTag":null,"analyzedSha":"bf67d907478c724120bf76292da54abf9e73c2b3","analyzedAt":"2026-08-14T15:17:09.543Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}