15 MyBatis Pitfalls and How to Avoid Them

This guide lists the 15 most common MyBatis pitfalls—from SQL injection and mismatched field mappings to N+1 queries and improper pagination—paired with concrete best‑practice solutions, code snippets, and configuration tips to make your persistence layer safer, more efficient, and easier to maintain.

Ray's Galactic Tech
Ray's Galactic Tech
Ray's Galactic Tech
15 MyBatis Pitfalls and How to Avoid Them

Overview

MyBatis is a powerful Java persistence framework, but overlooking details can easily lead to bugs, security issues, and performance problems. The article compiles the 15 most frequent pitfalls and provides concrete best‑practice remedies.

Pitfall Reference

1. Direct SQL concatenation – vulnerable to injection. Correct: use #{} placeholders.

2. Mismatched DB column and Java property – mapping fails. Correct: define an explicit <resultMap>.

3. Conditional queries written with many if‑else blocks . Correct: use <where> together with <if>.

4. In‑memory pagination . Correct: let the database paginate (e.g., PageHelper).

5. Missing transaction annotation . Correct: annotate with @Transactional.

6. N+1 query problem . Correct: enable lazy loading and map associations properly.

7. Improper handling of enums or special types . Correct: implement a custom TypeHandler.

8. Cache causing data inconsistency . Correct: enable cache selectively.

9. Chaotic multi‑table association mapping . Correct: use <association> / <collection>.

10. Batch operations executing single‑row SQL repeatedly . Correct: use <foreach> for bulk inserts.

11. Multiple mutually exclusive conditions written with many &lt;if&gt; . Correct: use <choose> with <when> / <otherwise>.

12. Manual AND/OR handling in dynamic SQL . Correct: wrap conditions with <trim prefixOverrides="AND |OR ">.

13. Repeatedly writing field mappings . Correct: enable mapUnderscoreToCamelCase globally.

14. Repeating column lists in SQL . Correct: define reusable <sql> fragments.

15. Debugging SQL errors . Correct: turn on MyBatis logging (e.g., STDOUT_LOGGING).

Detailed Usage Examples

1. SQL Injection Protection

<select id="getUser" resultType="User">
  SELECT * FROM users WHERE id = #{id}
</select>

2. Field‑to‑Property Mapping

<resultMap id="userResultMap" type="User">
  <id property="userId" column="user_id"/>
  <result property="userName" column="user_name"/>
</resultMap>

3. Dynamic Conditional Query

<select id="findUsers" resultType="User">
  SELECT * FROM users
  <where>
    <if test="name != null">AND name = #{name}</if>
    <if test="age != null">AND age = #{age}</if>
  </where>
</select>

4. Database‑Level Pagination

PageHelper.startPage(1, 10);
List<User> users = userMapper.selectAll();
PageInfo<User> pageInfo = new PageInfo<>(users);

5. Transaction Management

@Transactional
public void updateUser(User user) {
    userMapper.update(user);
    logMapper.insertLog(user.getId(), "update");
}

6. Avoiding N+1 Queries

<settings>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="aggressiveLazyLoading" value="false"/>
</settings>

7. Custom TypeHandler

public class MyEnumTypeHandler implements TypeHandler<MyEnum> {
    // implement methods
}

8. Reasonable Cache Usage

<select id="getConfig" resultType="Config" useCache="true">
  SELECT * FROM config WHERE id = #{id}
</select>

9. Multi‑Table Association Mapping

<resultMap id="orderResultMap" type="Order">
  <id property="id" column="order_id"/>
  <association property="user" javaType="User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
  </association>
</resultMap>

10. Batch Operations

<insert id="insertUsers">
  INSERT INTO users (name, age)
  VALUES
  <foreach collection="list" item="user" separator=",">
    (#{user.name}, #{user.age})
  </foreach>
</insert>

11. Conditional Branching

<choose>
  <when test="id != null">id = #{id}</when>
  <when test="name != null">name = #{name}</when>
  <otherwise>age > 18</otherwise>
</choose>

12. SQL Trimming Optimization

<trim prefixOverrides="AND |OR ">
  <if test="name != null">AND name = #{name}</if>
  <if test="age != null">OR age = #{age}</if>
</trim>

13. Camel‑Case Mapping

<settings>
  <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

14. SQL Fragment Reuse

<sql id="userColumns">id, name, age, email</sql>
<select id="getUser" resultType="User">
  SELECT <include refid="userColumns"/> FROM users WHERE id = #{id}
</select>

15. Logging for Debugging

<settings>
  <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

Conclusion

Avoid using ${} expressions whenever possible.

Leverage global settings and reusable SQL fragments to reduce duplication.

Be intentional about pagination, batch processing, and cache configuration for optimal performance.

Mastering these practices makes your MyBatis code more secure , more efficient , and easier to maintain .

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.

JavaSQLMyBatisORM
Ray's Galactic Tech
Written by

Ray's Galactic Tech

Practice together, never alone. We cover programming languages, development tools, learning methods, and pitfall notes. We simplify complex topics, guiding you from beginner to advanced. Weekly practical content—let's grow 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.