baomidou/mybatis-plus · error · BindingException
method %s needs either a @ResultMap annotation, a @ResultTyp
Error message
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.
What it means
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.
Source
Thrown at mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/override/MybatisMapperMethod.java:166
if (method.returnsVoid()) {
result = null;
} else if (Integer.class.equals(method.getReturnType()) || Integer.TYPE.equals(method.getReturnType())) {
result = rowCount;
} else if (Long.class.equals(method.getReturnType()) || Long.TYPE.equals(method.getReturnType())) {
result = (long) rowCount;
} else if (Boolean.class.equals(method.getReturnType()) || Boolean.TYPE.equals(method.getReturnType())) {
result = rowCount > 0;
} else {
throw new BindingException("Mapper method '" + command.getName() + "' has an unsupported return type: " + method.getReturnType());
}
return result;
}
private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
if (!StatementType.CALLABLE.equals(ms.getStatementType())
&& void.class.equals(ms.getResultMaps().get(0).getType())) {
throw new BindingException("method " + command.getName()
+ " needs either a @ResultMap annotation, a @ResultType annotation,"
+ " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
}
Object param = this.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
} else {
sqlSession.select(command.getName(), param, method.extractResultHandler(args));
}
}
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = this.convertArgsToSqlCommandParam(args);
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);View on GitHub (pinned to bf67d90747)
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
Example fix
// before
@Select("SELECT * FROM user")
void scanUsers(ResultHandler<User> handler);
// after
@Select(value = "SELECT * FROM user", resultType = User.class)
void scanUsers(ResultHandler<User> handler); Defensive patterns
Strategy: validation
Validate before calling
// Before exposing a ResultHandler method, verify the statement declares a row type
MappedStatement ms = configuration.getMappedStatement("com.example.UserMapper.scanUsers");
Class<?> rowType = ms.getResultMaps().get(0).getType();
if (void.class.equals(rowType)) throw new IllegalStateException("scanUsers needs resultType/@ResultType"); Try / catch
catch BindingException on first streaming call and fail with guidance to add resultType; do not swallow — every call would fail identically.
Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- %s already contains value for %s
- %s does not contain value for %s
- %s is ambiguous in %s (try using the full name including the
- Should be specified either value() or name() attribute in th
- Cannot use both value() and name() attribute in the @CacheNa
AI-assisted analysis of baomidou/mybatis-plus@bf67d90747 (2026-08-14).
Data as JSON: /api/errors/baa4461bcdb0ab90.
Report an issue: GitHub.