mybatis/mybatis-3 · error · BindingException

Mapper method '{name}' has an unsupported return type: {retu

Error message

Mapper method '{name}' has an unsupported return type: {returnType}

What it means

rowCountResult() converts the row-count int returned by INSERT/UPDATE/DELETE into the mapper method's return type. Only void, Integer/int, Long/long, and Boolean/boolean are supported (boolean means 'rowCount > 0'). Any other declared return type on a DML mapper method triggers this BindingException.

Source

Thrown at src/main/java/org/apache/ibatis/binding/MapperMethod.java:117

    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());
    }
    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 = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
    } else {

View on GitHub (pinned to 008069adb1)

Solutions

  1. Change the DML mapper method return type to int/Integer, long/Long, boolean/Boolean, or void
  2. If you need the generated key, accept it via an @Options(useGeneratedKeys=true, keyProperty=...) parameter object instead of a return value
  3. Move the data-retrieval concern to a separate <select> method

Example fix

// before
String updateUser(User u);
// after
int updateUser(User u); // or boolean updateUser(User u);
Defensive patterns

Strategy: validation

Validate before calling

for (Method m : UserMapper.class.getMethods()) {
  Class<?> rt = m.getReturnType();
  boolean dmlOk = rt == void.class || rt == int.class || rt == Integer.class
      || rt == long.class || rt == Long.class || rt == boolean.class || rt == Boolean.class;
  // assert dmlOk for methods bound to insert/update/delete statements
}

Prevention

When it happens

Trigger: Declaring 'String updateUser(User u)' or 'List<Integer> insert(...)' on a method bound to an <insert>/<update>/<delete> statement; returning a custom POJO from a DML statement without useGeneratedKeys/resultMap mechanisms that go through a different path.

Common situations: Copying a SELECT method signature onto a DML statement; wanting to return the generated key typed as something other than the supported set.

Related errors


AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14). Data as JSON: /api/errors/e7ddc922c30531e8. Report an issue: GitHub.