mybatis/mybatis-3 · error · BindingException
Mapper method '{name}' attempted to return null from a metho
Error message
Mapper method '{name}' attempted to return null from a method with a primitive return type ({returnType}). What it means
A mapper method declared with a primitive return type (int, boolean, long, ...) executed a query that returned null. Java cannot unbox null into a primitive, so MyBatis fails fast with a BindingException instead of throwing a bare NullPointerException at the call site.
Source
Thrown at src/main/java/org/apache/ibatis/binding/MapperMethod.java:100
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
throw new BindingException("Mapper method '" + command.getName()
+ "' attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
}
return result;
}
private Object rowCountResult(int rowCount) {
final Object result;
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());View on GitHub (pinned to 008069adb1)
Solutions
- Change the mapper method return type from primitive to its wrapper (int -> Integer, boolean -> Boolean) and handle null at the call site
- If the query should always return a row, fix the WHERE clause or the data so a row is always matched
- Add COALESCE/IFNULL in SQL so the database returns a non-null scalar instead of no row
Example fix
// before
int findAge(@Param("id") Long id);
// after
Integer findAge(@Param("id") Long id); // caller handles null Defensive patterns
Strategy: type-guard
Type guard
static boolean safePrimitiveReturn(Class<?> rt) {
return !rt.isPrimitive(); // prefer wrappers in mapper signatures
} Try / catch
catch (BindingException e) { if (e.getMessage().contains("primitive return type")) return defaultValue; throw e; } Prevention
- Use wrapper types (Integer/Boolean/Long) in mapper signatures instead of primitives
- Write one integration test per mapper method covering the zero-rows case
When it happens
Trigger: Mapper method like 'int findAge(Long id)' where selectOne returns no row; INSERT/UPDATE/DELETE methods declared with primitive types not covered by rowCountResult (which does handle int/long/boolean); a SELECT method with primitive return whose query yields zero rows.
Common situations: Querying by an id that does not exist; filters that match nothing; changing a wrapper return type (Integer) to a primitive (int) during refactoring.
Related errors
- Unknown execution method for: {name}
- Mapper method '{name}' has an unsupported return type: {retu
- method {name} needs either a @ResultMap annotation, a @Resul
- Parameter '{key}' not found. Available parameters are {keySe
- Invalid bound statement (not found): {mapperInterface}.{meth
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/2cfd54aff610caad.
Report an issue: GitHub.