mybatis/mybatis-3 · error · BindingException

Type {type} is already known to the MapperRegistry.

Error message

Type {type} is already known to the MapperRegistry.

What it means

MapperRegistry.addMapper() refuses double registration of the same interface. The registry is keyed by Class, and re-parsing a mapper (XML plus annotation builder, or two config files both including it) triggers this BindingException instead of silently replacing the factory.

Source

Thrown at src/main/java/org/apache/ibatis/binding/MapperRegistry.java:63

    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

  public <T> boolean hasMapper(Class<T> type) {
    return knownMappers.containsKey(type);
  }

  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<>(type));
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }

View on GitHub (pinned to 008069adb1)

Solutions

  1. Register each mapper interface through exactly one mechanism (pick XML include, package scan, or programmatic addMapper)
  2. Guard programmatic registration with hasMapper(type) before addMapper
  3. In Spring, let one SqlSessionFactory + one MapperScan own mapper registration; do not also list them in mybatis-config.xml

Example fix

// before
configuration.addMapper(FooMapper.class); // may already be registered
// after
if (!configuration.getMapperRegistry().hasMapper(FooMapper.class)) {
  configuration.addMapper(FooMapper.class);
}
Defensive patterns

Strategy: validation

Validate before calling

MapperRegistry registry = configuration.getMapperRegistry();
if (!registry.hasMapper(FooMapper.class)) {
  registry.addMapper(FooMapper.class);
}

Prevention

When it happens

Trigger: Calling configuration.addMapper(X.class) after the same interface was already loaded via XML <mapper resource=.../>; mybatis-config listing the mapper both as <package> and as <mapper class>; mybatis-spring re-scanning an already-registered mapper; multiple SqlSessionFactory configurations sharing state incorrectly.

Common situations: Mixing registration styles (annotation + XML + package scan) for the same interface; programmatic bootstrap code that registers mappers on every request instead of once; integration tests building a factory per test against shared static state.

Related errors


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