baomidou/mybatis-plus · error · BuilderException

Detected conflicting annotations '%s' and '%s' on '%s'.

Error message

Detected conflicting annotations '%s' and '%s' on '%s'.

What it means

Thrown while collecting statement annotations on a mapper method when two annotations of the same category (e.g. two @Select annotations via repeatable containers, or @Select variants for different databaseId values) map to the same databaseId key. The builder groups annotations by their databaseId into a map; a duplicate key invokes the merge function, which raises this BuilderException naming both conflicting annotations and the method.

Source

Thrown at mybatis-plus-core/src/main/java/com/baomidou/mybatisplus/core/MybatisMapperAnnotationBuilder.java:605

    private SqlSource buildSqlSourceFromStrings(String[] strings, Class<?> parameterTypeClass,
                                                LanguageDriver languageDriver) {
        return languageDriver.createSqlSource(configuration, String.join(" ", strings).trim(), parameterTypeClass);
    }

    @SafeVarargs
    private final Optional<AnnotationWrapper> getAnnotationWrapper(Method method, boolean errorIfNoMatch,
                                                                                           Class<? extends Annotation>... targetTypes) {
        return getAnnotationWrapper(method, errorIfNoMatch, Arrays.asList(targetTypes));
    }

    private Optional<AnnotationWrapper> getAnnotationWrapper(Method method, boolean errorIfNoMatch,
                                                                                     Collection<Class<? extends Annotation>> targetTypes) {
        String databaseId = configuration.getDatabaseId();
        Map<String, AnnotationWrapper> statementAnnotations = targetTypes.stream()
            .flatMap(x -> Arrays.stream(method.getAnnotationsByType(x))).map(AnnotationWrapper::new)
            .collect(Collectors.toMap(AnnotationWrapper::getDatabaseId, x -> x, (existing, duplicate) -> {
                throw new BuilderException(
                    String.format("Detected conflicting annotations '%s' and '%s' on '%s'.", existing.getAnnotation(),
                        duplicate.getAnnotation(), method.getDeclaringClass().getName() + "." + method.getName()));
            }));
        AnnotationWrapper annotationWrapper = null;
        if (databaseId != null) {
            annotationWrapper = statementAnnotations.get(databaseId);
        }
        if (annotationWrapper == null) {
            annotationWrapper = statementAnnotations.get("");
        }
        if (errorIfNoMatch && annotationWrapper == null && !statementAnnotations.isEmpty()) {
            // Annotations exist, but there is no matching one for the specified databaseId
            throw new BuilderException(String.format(
                "Could not find a statement annotation that correspond a current database or default statement on method '%s.%s'. Current database id is [%s].",
                method.getDeclaringClass().getName(), method.getName(), databaseId));
        }
        return Optional.ofNullable(annotationWrapper);
    }

View on GitHub (pinned to bf67d90747)

Solutions

  1. Inspect the method named in the message and remove or correct one of the duplicate annotations so each databaseId occurs at most once.
  2. Use distinct databaseId values per vendor (e.g. @Select(databaseId="mysql") and @Select(databaseId="oracle")).
  3. Register a DatabaseIdProvider in configuration so databaseId-based routing actually works as intended.

Example fix

// before
@Select(databaseId = "mysql", value = "SELECT ...")
@Select(databaseId = "mysql", value = "SELECT /*v2*/ ...")
List<User> list();

// after
@Select(databaseId = "mysql", value = "SELECT ...")
@Select(databaseId = "oracle", value = "SELECT ... FROM dual")
List<User> list();
Defensive patterns

Strategy: validation

Validate before calling

// detect two same-databaseId statement annotations on a method
for (java.lang.reflect.Method m : mapper.getMethods()) {
    Map<String, Integer> ids = new java.util.HashMap<>();
    for (java.lang.annotation.Annotation a : m.getAnnotations()) {
        try {
            java.lang.reflect.Method dbId = a.annotationType().getMethod("databaseId");
            String k = (String) dbId.invoke(a);
            if (!ids.merge(k == null ? "" : k, 1, Integer::sum).equals(1)) {
                throw new IllegalStateException("Duplicate databaseId '" + k + "' on " + m);
            }
        } catch (NoSuchMethodException ignored) { }
    }
}

Try / catch

Catch BuilderException whose message contains 'Detected conflicting annotations'; the method and both annotations are named — deduplicate and rebuild.

Prevention

When it happens

Trigger: Declaring two @Select(databaseId = "mysql", ...) annotations with the same databaseId on one method; stacking a plain @Select plus a databaseId-qualified @Select where both resolve to the empty-string default databaseId key.

Common situations: Adding vendor-specific statement variants while copying an existing annotation and forgetting to change databaseId; mixing repeatable annotations with the container annotation @SelectList directly; annotation duplicated by merge conflicts.

Related errors


AI-assisted analysis of baomidou/mybatis-plus@bf67d90747 (2026-08-14). Data as JSON: /api/errors/b726a5bb429b559e. Report an issue: GitHub.