Understanding MyBatis Dynamic SQL: Parsing, Nodes, and Execution

This article explains MyBatis dynamic SQL, covering its purpose, the distinction between static and dynamic SQL, the core SqlNode and SqlSource classes, how MyBatis parses and assembles SQL scripts recursively, and provides concrete code examples to illustrate each step.

Top Architect
Top Architect
Top Architect
Understanding MyBatis Dynamic SQL: Parsing, Nodes, and Execution

MyBatis dynamic SQL is a powerful feature that helps developers build flexible SQL statements based on runtime conditions, avoiding manual string concatenation and common pitfalls such as missing spaces or trailing commas.

The article introduces dynamic SQL, compares it with static SQL, and explains that MyBatis uses two core classes— SqlNode and SqlSource —to parse and assemble statements.

SqlNode encapsulation

SqlNode parses XML mapper files and represents each part of a dynamic statement. For nested dynamic SQL, MyBatis recursively processes nodes. The article provides a detailed example of a select statement with if conditions and shows how SqlNode objects are created.

SQL script classification

MyBatis distinguishes between static SQL (no conditional logic) and dynamic SQL (contains if, foreach, where, trim, etc.). The following code demonstrates a static SQL snippet and highlights how MyBatis treats it as static:

// Select is a query attribute
<select id="selectBypageTwo" resultType="com.wwl.mybatis.dao.User">
    // this query selects * from user where id > #{user.id}
    select * from user where id > #{user.id}
    // if condition makes the SQL dynamic
    <if test="user.name != null and user.name!=''">
        AND name = #{user.name}
    </if>
</select>

The article then shows the class hierarchy of SqlNode, including MixedSqlNode (which holds a list of child nodes) and IfSqlNode (which evaluates OGNL expressions to decide whether to apply its child nodes).

public class MixedSqlNode implements SqlNode {
    private final List<SqlNode> contents;
    public MixedSqlNode(List<SqlNode> contents) { this.contents = contents; }
    @Override
    public boolean apply(DynamicContext context) {
        for (SqlNode sqlNode : contents) { sqlNode.apply(context); }
        return true;
    }
}
public class IfSqlNode implements SqlNode {
    private final ExpressionEvaluator evaluator;
    private final String test;
    private final SqlNode contents;
    public IfSqlNode(SqlNode contents, String test) {
        this.test = test; this.contents = contents; this.evaluator = new ExpressionEvaluator();
    }
    @Override
    public boolean apply(DynamicContext context) {
        if (evaluator.evaluateBoolean(test, context.getBindings())) {
            contents.apply(context);
            return true;
        }
        return false;
    }
}

Dynamic SQL parsing process

When MyBatis needs a BoundSql, it creates a DynamicContext, lets the root SqlNode (often a MixedSqlNode) apply itself, and then uses SqlSourceBuilder to replace #{} placeholders with ? parameters, producing a static SQL ready for JDBC.

public BoundSql getBoundSql(Object parameterObject) {
    DynamicContext context = new DynamicContext(configuration, parameterObject);
    rootSqlNode.apply(context);
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
    SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
    BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
        boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
}

The article concludes with a two‑step summary: (1) Dynamic SQL is first transformed into a tree of SqlNode objects during mapper parsing; (2) At execution time, the tree is traversed to generate a concrete SQL string, replace placeholders, and bind parameters.

Despite promotional messages about gifts and community groups, the core content provides a thorough technical walkthrough of MyBatis dynamic SQL, making it valuable for backend developers.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentMyBatisORMCode ExampleDynamic SQLSqlNode
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.