mybatis/mybatis-3 · error · BuilderException
Expression based parameters are not supported yet
Error message
Expression based parameters are not supported yet
What it means
Thrown by ParameterMappingTokenHandler when a #{} placeholder contains an 'expression=...' option. MyBatis once planned OGNL-based parameter expressions inside placeholders but never shipped support; the option is recognized by the parser (to give a precise message) yet explicitly unsupported and rejected.
Source
Thrown at src/main/java/org/apache/ibatis/builder/ParameterMappingTokenHandler.java:119
typeHandler = resolveTypeHandler(genericType, jdbcType, typeHandlerAlias);
}
builder.typeHandler(typeHandler);
ParameterMode mode = null;
for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
if ("mode".equals(name)) {
mode = resolveParameterMode(value);
builder.mode(mode);
} else if ("numericScale".equals(name)) {
builder.numericScale(Integer.valueOf(value));
} else if ("resultMap".equals(name)) {
builder.resultMapId(value);
} else if ("jdbcTypeName".equals(name)) {
builder.jdbcTypeName(value);
} else if ("expression".equals(name)) {
throw new BuilderException("Expression based parameters are not supported yet");
} else {
throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content
+ "}. Valid properties are " + PARAMETER_PROPERTIES);
}
}
if (!ParameterMode.OUT.equals(mode) && paramExists) {
if (metaParameters.hasGetter(propertyTokenizer.getName())) {
builder.value(metaParameters.getValue(property));
} else if (parameterObject == null) {
builder.value(null);
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
builder.value(parameterObject);
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
builder.value(metaObject.getValue(property));
}
}
return builder.build();View on GitHub (pinned to 008069adb1)
Solutions
- Remove the expression option and compute the value in Java before passing the parameter
- Use <if>/<choose>/<foreach> dynamic SQL (or @SelectProvider / Provider SQL builder) for conditional logic
- For static trusted text, ${} substitution can be used with full SQL-injection caution
Example fix
<!-- before -->
#{user,expression=name.toUpperCase()}
<!-- after -->
query.setName(user.getName().toUpperCase());
... WHERE name = #{name} Defensive patterns
Strategy: validation
Validate before calling
// Fail fast if generated SQL contains the unsupported expression option
if (sql.contains("expression=")) {
throw new UnsupportedOperationException("#{...,expression=...} is not supported by MyBatis");
} Prevention
- Do not attempt per-row expressions in placeholders — compute values in Java
- Use <if>/<foreach> or @SelectProvider for conditional SQL
When it happens
Trigger: Writing #{user,expression=id > 100} or any placeholder option list containing expression=... in an XML mapper or annotation SQL.
Common situations: Developers attempt dynamic per-row expressions in a batch or try to inline computed values; occasionally copied from very old MyBatis 2.x / iBATIS examples where similar syntax existed.
Related errors
- An invalid property '{name}' was found in mapping #{{content
- Unknown execution method for: {name}
- Mapper method '{name}' attempted to return null from a metho
- Mapper method '{name}' has an unsupported return type: {retu
- method {name} needs either a @ResultMap annotation, a @Resul
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/9ede439249317948.
Report an issue: GitHub.