Why MyBatis Treats Integer 0 as an Empty String in <if> Conditions
When using MyBatis dynamic SQL to filter records by a numeric state field, the framework incorrectly evaluates the condition "state != ''" as false for the integer value 0 because its OGNL expression evaluator treats 0 as equivalent to an empty string, leading to unexpected query results.
Problem
A MyBatis mapper uses a dynamic <if> clause to filter rows by a state column:
<if test="req.state != null and req.state != ''">
AND md.state = #{req.state}
</if>The front‑end sends state as an Integer (0 or 1). When req.state is 0, the condition is evaluated as false and the state filter is omitted, causing incorrect query results.
Root Cause
MyBatis evaluates the test attribute with OGNL. In OGNL, numeric values are converted to BigDecimal and considered true only if they are non‑zero. The expression state != '' forces OGNL to compare an Integer with an empty string, which results in the integer being treated as false when its value is 0. Consequently the whole expression state != null and state != '' evaluates to false for 0.
MyBatis Internals
The parsing flow is:
private void initNodeHandlerMap() {
nodeHandlerMap.put("if", new IfHandler());
// other handlers omitted
} IfHandlercreates an IfSqlNode that stores the test string and delegates evaluation to ExpressionEvaluator:
public class IfHandler implements NodeHandler {
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
String test = nodeToHandle.getStringAttribute("test");
IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
targetContents.add(ifSqlNode);
}
}The IfSqlNode applies the expression:
public boolean apply(DynamicContext context) {
if (evaluator.evaluateBoolean(test, context.getBindings())) {
contents.apply(context);
return true;
}
return false;
}Key part – ExpressionEvaluator.evaluateBoolean:
public boolean evaluateBoolean(String expression, Object parameterObject) {
Object value = OgnlCache.getValue(expression, parameterObject);
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof Number) {
return new BigDecimal(String.valueOf(value)).compareTo(BigDecimal.ZERO) != 0;
}
return value != null;
}When the expression is state != null and state != '' and state = 0, OGNL evaluates the second part as false, making the whole test false.
Reproduction
Add OGNL dependency and run a simple test:
<dependency>
<groupId>ognl</groupId>
<artifactId>ognl</artifactId>
<version>2.7.3</version>
</dependency>
public static void main(String[] args) {
Map<String, Object> map = new HashMap<>();
map.put("state", 0);
Object result = OgnlCache.getValue("state != null and state != ''", map);
System.out.println(result); // prints false
}The program prints false, confirming the OGNL behaviour.
Solution
Do not compare numeric parameters with an empty string. Typical fixes are:
Remove the empty‑string check:
<if test="req.state != null">
AND md.state = #{req.state}
</if>Explicitly handle zero:
<if test="req.state != null and req.state != 0">
AND md.state = #{req.state}
</if>Either approach ensures that a legitimate 0 value is not filtered out.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architecture Digest
Focusing on Java backend development, covering application architecture from top-tier internet companies (high availability, high performance, high stability), big data, machine learning, Java architecture, and other popular fields.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
