mybatis/mybatis-3 · error · BuilderException
Dynamic content is not allowed when using RAW language
Error message
Dynamic content is not allowed when using RAW language
What it means
RawLanguageDriver forces every statement to be a static, pre-compilable RawSqlSource. After building a SqlSource it checks the concrete class; anything other than RawSqlSource (i.e. DynamicSqlSource) means the script contained dynamic constructs, so a BuilderException is thrown at startup. Dynamic tags (${} substitution, <if>, <where>, <foreach>, etc.) are incompatible with the RAW driver by design.
Source
Thrown at src/main/java/org/apache/ibatis/scripting/defaults/RawLanguageDriver.java:52
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
SqlSource source = super.createSqlSource(configuration, script, parameterType);
checkIsNotDynamic(source);
return source;
}
@Override
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType,
ParamNameResolver paramNameResolver) {
SqlSource source = super.createSqlSource(configuration, script, parameterType, paramNameResolver);
checkIsNotDynamic(source);
return source;
}
private void checkIsNotDynamic(SqlSource source) {
if (!RawSqlSource.class.equals(source.getClass())) {
throw new BuilderException("Dynamic content is not allowed when using RAW language");
}
}
}
View on GitHub (pinned to 008069adb1)
Solutions
- Replace dynamic constructs with static SQL: use #{} parameters, split conditional variants into separate statements
- Switch those statements back to the default XMLLanguageDriver (remove lang="raw" or per-statement driver override)
- If you only need runtime-value binding (not SQL text changes), #{} placeholders are fine under RAW — convert ${} to #{} where possible
Example fix
<!-- before (RawLanguageDriver) -->
<select id="find" lang="raw" ...>SELECT * FROM t WHERE status = #{status}<if test="name != null"> AND name = #{name}</if></select>
<!-- after -->
<select id="find" ...>SELECT * FROM t WHERE status = #{status} AND name = #{name}</select> Defensive patterns
Strategy: validation
Validate before calling
// at bootstrap, validate each statement against the RAW driver
try {
rawDriver.createSqlSource(configuration, script, parameterType, paramNameResolver);
} catch (BuilderException e) {
// statement contains dynamic content; assign it the default XML driver instead
} Prevention
- Audit mappers for ${}, <if>, <foreach>, <where>, <set>, <choose>, <bind> before enabling RawLanguageDriver
- Enable RAW per statement (lang="raw") incrementally rather than globally
- Keep static-SQL statements in separate mapper files when using RAW
When it happens
Trigger: Setting defaultLanguageDriverClass (or a statement's lang attribute) to RawLanguageDriver while the mapper still contains ${...}, <if>, <choose>, <foreach>, <set>, <where>, <bind>, or <include> fragments that expand to dynamic SQL.
Common situations: Adopting RawLanguageDriver for startup-time performance/static SQL verification on an existing mapper suite; mixing drivers and forgetting to clean dynamic constructs out of some statements; copy-pasting dynamic SQL into a RAW-driver statement.
Related errors
- null is not a valid Language Driver
- Failed to load language driver for " + cls.getName()
- Invalid bound statement (not found): {mapperInterface}.{meth
- Type {type} is not known to the MapperRegistry.
- Error resolving JdbcType. Cause: {cause}
AI-assisted analysis of mybatis/mybatis-3@008069adb1 (2026-08-14).
Data as JSON: /api/errors/f4750464869ca05a.
Report an issue: GitHub.