Mybatis Mapper

At the heart of MyBatis lies a philosophy of clean separation: keeping SQL statements completely decoupled from your core Java application logic. The Mapper XML file serves as the primary vessel for this architecture, defining both your raw queries and your result-mapping rules in a structured format. By housing SQL in these dedicated files, developers can interact with databases seamlessly without littering source code with hardcoded string concatenation.

A standard MyBatis Mapper file generally follows a structured XML layout:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">

    <select id="selectById" parameterType="int" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>

</mapper>
  • namespace: Acts as a unique identifier for the mapper, and it must precisely match the fully qualified name of its corresponding Java interface to bind methods correctly.
  • id: Represents the unique identifier for a specific SQL statement, which must correspond directly to a method name defined within your interface.
  • parameterType: Declares the expected input data type, though it is frequently omitted because MyBatis can automatically infer it from runtime arguments.
  • resultType / resultMap: Directs the framework on how to map the resulting database rows back into Java objects.

Essential CRUD Operations

Mapper files handle standard database interactions using four primary tags for creating, reading, updating, and deleting records:

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user(name, age) VALUES(#{name}, #{age})
</insert>

<update id="updateUser">
    UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}
</update>

<delete id="deleteById">
    DELETE FROM user WHERE id = #{id}
</delete>

When performing inserts, combining useGeneratedKeys with keyProperty is a common technique that automatically populates the newly generated auto-increment primary key back into your Java entity object.

Understanding Parameter Placeholders: #{} vs ${}

One of the most crucial distinctions to master in MyBatis development is the difference between these two placeholder formats:

  • #{}: A precompiled placeholder that relies on underlying PreparedStatement parameters. It supplies values safely using ? bindings, effectively preventing SQL injection attacks, and should always be your default choice.
  • ${}: Performs direct string interpolation. Because it inserts values raw without precompilation, it introduces severe security risks and should strictly be reserved for structural elements that cannot be parameterized, such as dynamic table names or column names.
<!-- Secure and strongly recommended -->
SELECT * FROM user WHERE name = #{name}

<!-- Restricted to dynamic structural identifiers -->
SELECT * FROM ${tableName}

Dynamic SQL Generation

Dynamic SQL is one of the most powerful features of MyBatis mappers, allowing you to build queries conditionally. Key tags include:

  • if: Evaluates conditions to conditionally append SQL fragments.
  • where: Intelligently prepends the WHERE clause and automatically strips away any redundant leading AND or OR keywords.
  • set: Specialized for UPDATE statements to smartly manage trailing commas.
  • foreach: Iterates over collections, commonly used for IN clauses and batch operations.
  • choose / when / otherwise: Provides multi-branch conditional logic similar to a switch statement in Java.

Example implementations:

<select id="selectByCondition" resultType="User">
    SELECT * FROM user
    <where>
        <if test="name != null and name != ''">
            AND name LIKE CONCAT('%', #{name}, '%')
        </if>
        <if test="minAge != null">
            AND age &gt;= #{minAge}
        </if>
    </where>
</select>

<select id="selectByIds" resultType="User">
    SELECT * FROM user WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

Note: When writing XML, comparison operators like < must be properly escaped as &lt;, or alternatively enclosed inside a <![CDATA[ ]]> block.

Advanced Result Mapping with resultMap

Whenever your database column names do not align directly with your entity field names, a resultMap provides explicit mapping control:

<resultMap id="userMap" type="User">
    <id property="id" column="user_id"/>
    <result property="userName" column="user_name"/>
    <result property="createTime" column="create_time"/>
</resultMap>

Additionally, resultMap supports advanced relational elements like association (for one-to-one relationships) and collection (for one-to-many relationships), making it indispensable for handling complex graph queries.

Best Practices

  1. Strict Namespace Alignment: Always ensure that your XML namespace precisely matches your Mapper interface path to prevent binding failures.
  2. Prioritize Safety: Rely exclusively on #{} for parameter substitution to maintain robust protection against SQL injection vulnerabilities.
  3. Reduce Redundancy: Extract repetitive SQL snippets into reusable <sql> fragments and reference them using <include>.
  4. Leverage Logging: Enable MyBatis SQL logging during development to easily inspect the exact compiled statements and parameters executed at runtime.

Mastering MyBatis Mapper files is foundational to unlocking the full potential of the framework. By effectively utilizing dynamic SQL and advanced result mappings, you can maintain a data access layer that is both highly flexible and exceptionally easy to maintain.