Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Mastering Dynamic SQL in MyBatis: A Comprehensive Guide

Tech May 17 2
  • <if></if>
  • <where></where> (along with trim and set)
  • <choose></choose> (with when and otherwise)
  • <foreach></foreach>

Important Note: When working with XML mappers, special characters such as greater than (>), less than (<), greater than or equal to (>=), and less than or equal to (<=) should be replaced with their corresponding XML entities. This prevents parsing errors since XML files already contain numerous angle brackets.

Original Symbol < <= > >= & ' "
XML Entity &lt; &lt;= &gt; &gt;= &amp; &apos; &quot;

Conditional Statements with <if>

The <if> tag is commonly used to construct conditional WHERE clauses. When multiple conditions need to be combined, developers can use multiple <if> tags. A traditional approach involves adding "WHERE 1=1" to ensure the query remains valid even when all conditions evaluate to false. Each subsequent condition can then be prefixed with "AND".

<!-- Dynamic SQL with if tags -->
<select id="findUsersByConditions" resultType="User">
    SELECT id, username, email, age
    FROM users
    WHERE 1=1
    <if test="username != null and username != ''">
        AND username LIKE '%' #{username} '%'
    </if>
    <if test="age != null and age > 0">
        AND age > #{age}
    </if>
</select>

When both conditions are provided, the resulting SQL will be: SELECT id, username, email, age FROM users WHERE 1=1 AND username LIKE '%' ? '%' AND age > ?

With only the username condition: SELECT id, username, email, age FROM users WHERE 1=1 AND username LIKE '%' ? '%'

With no conditions: SELECT id, username, email, age FROM users WHERE 1=1

While this approach works, the "WHERE 1=1" clause can negatively impact query performance on large datasets.

Advanced Conditional Tags: <where>, <trim>, and <set>

The <where> tag provides a more elegant solution by automatically adding the WHERE keyword only when atleast one condition evaluates to true. It also handles the removal of leading AND or OR keywords from the first condition that evaluates to true.

<select id="findUsersByConditionsOptimized" resultType="User">
    SELECT id, username, email, age
    FROM users
    <where>
        <if test="username != null and username != ''">
            AND username LIKE '%' #{username} '%'
        </if>
        <if test="age != null and age > 0">
            AND age > #{age}
        </if>
    </where>
</select>

For more complex scenarios, the <trim> tag offers greater flexibility with its attributes:

  • prefix: Adds a prefix before the content, if applicable
  • suffix: Adds a suffix after the content, if applicable
  • prefixOverrides: Removes specified content from the beginning of the trimmed content
  • suffixOverrides: Removes specified content from the end of the trimmed content

Here's an example of using <trim> to replace the <where> functionality:

<select id="findUsersWithTrim" resultType="User">
    SELECT id, username, email, age
    FROM users
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
        <if test="username != null and username != ''">
            AND username LIKE '%' #{username} '%'
        </if>
        <if test="age != null and age > 0">
            AND age > #{age}
        </if>
    </trim>
</select>

The <set> tag is specifically designed for UPDATE statements and handles the removal of trailing commas:

<update id="updateUser">
    UPDATE users
    <set>
        <if test="username != null">username = #{username},</if>
        <if test="email != null">email = #{email},</if>
        <if test="age != null">age = #{age},</if>
    </set>
    WHERE id = #{id}
</update>

Conditional Logic with <choose>, <when>, and <otherwise>

When you need to implement mutually exclusive conditions (similar to a switch-case statement), the <choose> tag comes in handy. It evaluates each <when> condition in order and executes the first one that evaluates to true. If none of the <when> conditions are met, the <otherwise> block is executed.

<select id="findUsersWithPriority" resultType="User">
    SELECT id, username, email, age
    FROM users
    <where>
        <choose>
            <when test="username != null and username != ''">
                AND username LIKE '%' #{username} '%'
            </when>
            <when test="email != null and email != ''">
                AND email = #{email}
            </when>
            <otherwise>
                AND status = 'ACTIVE'
            </otherwise>
        </choose>
    </where>
</select>

Iterating with <foreach>

The <foreach> tag is essential for iterating over collections and constructing IN clauses or batch operations. Key attributes include:

  • collection: The collection to iterate (array, list, map)
  • item: The variable name for each element in the iteration
  • index: The variable name for the index (for lists/arrays) or key (for maps)
  • open: The string to prepend to the entire iteration result
  • close: The string to append to the entire iteration result
  • separator: The separator between each iteration

Example with an array of IDs:

<select id="findUsersByIdArray" resultType="User">
    SELECT id, username, email, age
    FROM users
    WHERE id IN
    <foreach collection="array" item="userId" open="(" close=")" separator=",">
        #{userId}
    </foreach>
</select>

Example with a list of IDs:

<select id="findUsersByIdList" resultType="User">
    SELECT id, username, email, age
    FROM users
    WHERE id IN
    <foreach collection="list" item="userId" open="(" close=")" separator=",">
        #{userId}
    </foreach>
</select>

Example with a list of objects:

<select id="findUsersByObjectList" resultType="User">
    SELECT id, username, email, age
    FROM users
    WHERE id IN
    <foreach collection="list" item="user" open="(" close=")" separator=",">
        #{user.id}
    </foreach>
</select>

SQL Fragments with <sql> and <include>

The <sql> tag allows you to define reusable SQL fragments that can be included in other statements using the <include> tag. This promotes code reuse and consistency across your mappings.

<sql id="userColumns">
    id, username, email, age
</sql>

<sql id="userTable">
    users
</sql>

<select id="findUsersWithFragments" resultType="User">
    SELECT
    <include refid="userColumns"/>
    FROM
    <include refid="userTable"/>
    <where>
        <if test="status != null">
            status = #{status}
        </if>
    </where>
</select>

MyBatis dynamic SQL tags provide a flexible and powerful way to construct complex queries while maintaining clean and readable code. By leveraging these tags, developers can create sophisticated data access logic without resorting to string concatenation or complex conditional checks in their application code.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.