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
- Inspect the method named in the message and remove or correct one of the duplicate annotations so each databaseId occurs at most once.
- Use distinct databaseId values per vendor (e.g. @Select(databaseId="mysql") and @Select(databaseId="oracle")).
- 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
- Use at most one annotation per databaseId per method.
- Configure a DatabaseIdProvider and test both database profiles in CI.
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
- Could not find a statement annotation that correspond a curr
- Should be specified either value() or name() attribute in th
- Cannot use both value() and name() attribute in the @CacheNa
- %s already contains value for %s
- %s does not contain value for %s
AI-assisted analysis of baomidou/mybatis-plus@bf67d90747 (2026-08-14).
Data as JSON: /api/errors/b726a5bb429b559e.
Report an issue: GitHub.