Hard‑Learned MyBatis‑Plus Pitfalls and How to Avoid Them in Production

The article reviews the most common MyBatis‑Plus traps encountered in large‑scale Java services—duplicate Snowflake IDs, batch‑insert reordering, enum storage mismatches, camel‑case mapping errors, auto‑fill loss, and JSON field disappearance—explaining each root cause, illustrating it with real code snippets, and providing concrete solutions to keep production systems stable.

Shepherd Advanced Notes
Shepherd Advanced Notes
Shepherd Advanced Notes
Hard‑Learned MyBatis‑Plus Pitfalls and How to Avoid Them in Production

MyBatis‑Plus was created to give Java developers a production‑ready ORM that simplifies CRUD, adds features such as logical delete, auto‑fill, optimistic lock, multi‑tenant support, and built‑in pagination. Although the framework’s promises are attractive, the author’s years of production experience reveal a series of hidden pitfalls that only surface under high‑concurrency, containerized, or distributed environments.

1. Snowflake ID duplication

Problem

Duplicate primary‑key errors appear at 3 a.m. when two container instances generate the same Snowflake ID, despite the expectation that IDs are globally unique.

Root causes

Worker ID not configured, so every instance falls back to the default value.

Docker containers share the same MAC‑address prefix; extracting the last two bytes and taking mod 32 yields identical worker IDs after 32 instances.

Kubernetes pods receive virtual MAC addresses from CNI plugins, often identical across pods, causing the same worker ID.

Clock rollback makes the timestamp component repeat, completing the collision when other fields also match.

Solutions

Use an external distributed ID service (e.g., Zookeeper‑based) to guarantee unique worker IDs.

Switch to database auto‑increment IDs when a global unique key is required.

2. Batch insert disorder

Problem phenomenon

When inserting parent‑child orders in bulk, foreign‑key constraints fail because child rows are persisted before their parents. Remote‑ordered lists stored in the database also lose their original order.

MyBatis‑Plus batch modes

Default BATCH mode : receives a list, creates a ExecutorType.BATCH session, and adds each INSERT statement to the batch one by one. It supports primary‑key back‑fill and works in single‑threaded order, but multithreaded execution can scramble order and incurs many round‑trips.

Optimized batch mode : concatenates all values into a single multi‑value INSERT. It is fast (single round‑trip) and order‑stable, but does not support primary‑key back‑fill and requires manual primary‑key handling.

Underlying reasons for disorder

MySQL JDBC driver’s rewriteBatchedStatements merges statements, changing execution order.

The optimizer groups statements by table, further reordering inserts.

Solutions

Split data into smaller batches and call sqlSession.flushStatements() after each batch to force ordering.

Write custom @Insert SQL that inserts the whole list in the desired sequence.

3. Enum field storage risk

Problem

Enum fields are stored as their name() (e.g., "PENDING") instead of the intended numeric code, causing mismatches with existing data.

Solution

Annotate the numeric field with @EnumValue so MyBatis‑Plus persists the code value.

4. Camel‑case conversion misalignment

Problem

Fields such as buyerUID or XMLData are converted to buyer_u_i_d and x_m_l_data, breaking column mapping.

Solutions

Use @TableField("buyer_uid") to explicitly map the column.

Adopt a naming convention that avoids consecutive uppercase letters (e.g., buyerUid, xmlData).

Provide a custom naming strategy via GlobalConfig.DbConfig to control column formatting.

5. Auto‑fill loss during batch operations

Problem

Fields annotated with @TableField(fill = FieldFill.INSERT) or INSERT_UPDATE remain null when saveBatch is used.

Reason

Batch execution bypasses the MetaObjectHandler that normally populates these fields, because the framework directly executes raw JDBC batch statements.

Solutions

Manually invoke the handler before the batch:

entityList.forEach(e -> {
    MetaObject mo = SystemMetaObject.forObject(e);
    metaObjectHandler.insertFill(mo);
});
service.saveBatch(entityList);

Implement a custom batch‑save method that reflects over the entity, sets createTime, updateTime, and createBy, then calls the mapper’s insertBatch.

6. JSON field loss or format error

Problem

Complex fields such as Map<String,Object> or List<String> are stored as toString() output (e.g., {key=value}), which is not valid JSON, and are read back as null.

Solutions

Prefer primitive column types; move complex structures to separate tables.

If a JSON column is unavoidable, serialize the object to a JSON string before persisting and deserialize after retrieval.

Conclusion

The cases demonstrate that MyBatis‑Plus defaults assume a simple, single‑node environment. In production—especially with containers, Kubernetes, or cloud‑native deployments—those defaults become hidden hazards. Understanding the framework’s internal mechanisms, configuring worker IDs, choosing the appropriate batch mode, explicitly mapping columns, and handling complex types are essential steps to keep a Java service reliable.

About the author: Zhu Hongxu, Java engineer at XianKeHui.
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.

JavaORMMyBatis-PlusBatch InsertID GenerationProductionAuto FillEnum Mapping
Shepherd Advanced Notes
Written by

Shepherd Advanced Notes

Dedicated to sharing advanced Java technical insights, daily work snippets, and the power of persistent effort.

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.