Master MyBatis: Core Concepts, Best Practices, and Advanced Techniques

This comprehensive guide explains what MyBatis is, its advantages and drawbacks, suitable use cases, differences from Hibernate, parameter handling, dynamic SQL, pagination, caching, lazy loading, and how to use mapper interfaces, XML mappings, and plugins with clear code examples.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Master MyBatis: Core Concepts, Best Practices, and Advanced Techniques

What Is MyBatis?

MyBatis is a semi‑ORM framework that wraps JDBC, allowing developers to focus on writing raw SQL while handling driver loading, connection creation, and statement management internally. It supports XML or annotation‑based configuration to map POJOs to database records.

Advantages of MyBatis

SQL‑centric programming offers high flexibility and does not impose constraints on existing database designs.

Reduces boilerplate JDBC code by more than 50%.

Works with any JDBC‑compatible database.

Integrates smoothly with Spring.

Provides mapping tags for object‑relational field mapping.

Drawbacks of MyBatis

Writing SQL can be labor‑intensive, especially for tables with many columns or complex joins, requiring solid SQL skills.

SQL is tied to a specific database, making portability between different databases difficult.

Suitable Scenarios

Projects that need fine‑grained control over SQL performance.

Applications with frequent requirement changes or high performance demands, such as internet‑scale services.

Differences Between MyBatis and Hibernate

MyBatis requires developers to write SQL manually, offering precise performance control but less database‑agnostic support.

Hibernate provides full ORM capabilities, generating SQL automatically and offering better database independence.

#{} vs ${} in MyBatis

#{}

uses prepared‑statement placeholders, preventing SQL injection. ${} performs direct string substitution, which can lead to injection if not used carefully.

Handling Mismatched Field Names

Two approaches are common: define column aliases in the SQL to match POJO property names, or use a <resultMap> to map columns to properties explicitly.

Fuzzy Query (LIKE) Examples

<select id="selectLike">
    SELECT * FROM foo WHERE bar LIKE #{value}
</select>

Pass a wildcard string (e.g., "%smi%") as the parameter. Direct concatenation of wildcards is discouraged due to injection risk.

Mapper Interface Mechanics

The fully qualified name of a mapper interface serves as the XML namespace; each method name matches a statement id. MyBatis creates a JDK dynamic proxy for the interface, intercepts method calls, looks up the corresponding MapperStatement, executes the SQL, and returns the result. Overloading methods is not supported because the lookup key is namespace+methodName.

Pagination

MyBatis can paginate in memory using RowBounds or perform physical pagination by writing SQL with limit/offset clauses. Pagination plugins intercept the SQL, rewrite it according to the database dialect, and add appropriate pagination parameters.

Result Mapping Forms

Explicit <resultMap> tags mapping columns to properties.

Using column aliases in SQL to match POJO property names.

Batch Insert

<insert id="insertName">
    INSERT INTO names (name) VALUES (#{value})
</insert>

Execute batch inserts by opening a session with ExecutorType.BATCH and iterating over the data list, committing after the loop.

Retrieving Auto‑Generated Keys

<insert id="insertName" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO names (name) VALUES (#{name})
</insert>

After execution, the generated key is populated into the parameter object's id field.

Passing Multiple Parameters

Use indexed placeholders #{0}, #{1} for positional parameters.

Annotate method parameters with @Param("name") to reference them by name.

Wrap multiple values in a Map and reference entries via #{key}.

Dynamic SQL

MyBatis supports nine dynamic tags ( trim, where, set, foreach, if, choose, when, otherwise, bind) that conditionally build SQL based on expression values.

Additional XML Tags

Beyond select, insert, update, and delete, MyBatis XML can contain <resultMap>, <parameterMap>, <sql>, <include>, and <selectKey> for advanced mapping and key generation.

Duplicate IDs Across XML Files

If an XML file defines a namespace, IDs can repeat across files because the full key is namespace+id. Without a namespace, IDs must be unique globally.

Semi‑Automatic ORM Explanation

Unlike fully automatic ORM tools like Hibernate, MyBatis requires developers to write SQL manually for queries and associations, offering more control but less automation.

One‑to‑One and One‑to‑Many Associations

<mapper namespace="com.example.UserMapper">
    <!-- One‑to‑one association -->
    <select id="getClass" resultMap="ClassResultMap">
        SELECT * FROM class c, teacher t WHERE c.teacher_id = t.id AND c.id = #{id}
    </select>
    <resultMap type="Class" id="ClassResultMap">
        <id property="id" column="c_id"/>
        <association property="teacher" javaType="Teacher">
            <id property="id" column="t_id"/>
            <result property="name" column="t_name"/>
        </association>
    </resultMap>
    <!-- One‑to‑many collection -->
    <select id="getClassWithStudents" resultMap="ClassWithStudents">
        SELECT * FROM class c, teacher t, student s WHERE c.teacher_id = t.id AND c.id = s.class_id AND c.id = #{id}
    </select>
    <resultMap type="Class" id="ClassWithStudents">
        <id property="id" column="c_id"/>
        <association property="teacher" javaType="Teacher"> ... </association>
        <collection property="students" ofType="Student">
            <id property="id" column="s_id"/>
            <result property="name" column="s_name"/>
        </collection>
    </resultMap>
</mapper>

Lazy Loading Support

MyBatis can lazily load association (one‑to‑one) and collection (one‑to‑many) objects by setting lazyLoadingEnabled=true. It uses CGLIB proxies to fetch related data only when accessed.

Caching

First‑level cache: Session‑scoped PerpetualCache (enabled by default).

Second‑level cache: Namespace‑scoped, configurable (e.g., Ehcache). Requires the cache class to be serializable.

Cache is cleared automatically after any insert, update, or delete operation within the same scope.

Interface Binding

Any Java interface can be bound to SQL statements. Binding can be done via annotations ( @Select, @Update, etc.) for simple statements or via XML files where the namespace matches the interface’s fully qualified name.

Mapper Usage Requirements

Method name must match the id of the corresponding SQL statement.

Method parameter types must correspond to the statement’s parameterType.

Method return type must match the statement’s resultType or resultMap.

The XML file’s namespace must be the fully qualified name of the mapper interface.

Plugin Mechanism

MyBatis allows plugins for the four core interfaces: ParameterHandler, ResultSetHandler, StatementHandler, and Executor. A plugin implements the Interceptor interface, overrides intercept(), and is registered via annotations and configuration.

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.

BackendJavaSQLPersistenceMyBatisORM
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

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.