Why MyBatis Batch Insert Fails After Upgrade and How to Fix It
This article analyzes a MyBatis batch‑insert failure caused by the useGeneratedKeys setting after upgrading from 3.4.6 to 3.5.6, explains the root‑cause in the Jdbc3KeyGenerator, and provides both temporary and permanent solutions with code examples.
1. Introduction
When using MyBatis for batch inserts, the useGeneratedKeys attribute can be set to true to enable automatic back‑filling of database‑generated primary keys, simplifying key retrieval logic and improving development efficiency.
Benefits of useGeneratedKeys=true
Automatic key retrieval : MyBatis calls JDBC.getGeneratedKeys() to obtain auto‑increment IDs without extra SQL.
Simplified code : No need for a separate SELECT LAST_INSERT_ID(); the key is directly populated into the object via keyProperty.
Configurable scope : Can be set globally in settings or locally in a specific mapper XML/annotation.
Database compatibility : Works with databases that support auto‑increment (e.g., MySQL, SQL Server). For databases without this feature (e.g., Oracle), a <selectKey> tag is required.
Reduced key‑retrieval errors : JDBC guarantees atomicity, avoiding concurrency‑related mistakes.
2. Background
The Yili Life system, a core marketing platform, serves millions of daily users. After upgrading MyBatis from 3.4.6 to 3.5.6, the system began throwing ExecutorException with the message “Too many keys are generated…”, causing batch‑insert failures.
3. Impact
Retry mechanism failure : Batch‑insert errors prevent scheduled retry jobs from succeeding.
Data loss risk : Failed records require manual intervention, increasing operational cost.
System stability degradation : Frequent exceptions affect overall database performance and may lead to temporary service outages.
4. Investigation
Environment
SpringBoot 2.3.12.RELEASE, SpringCloud Hoxton.SR12
MyBatis 3.5.6 (upgraded version)
JDK 1.8
Stack trace analysis
Caused by: org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.executor.ExecutorException: Too many keys are generated. There are only 1 target objects. You either specified a wrong 'keyProperty' or encountered a driver bug like #1523.
at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:96)
... (additional stack frames omitted)The exception originates from Jdbc3KeyGenerator.assignKeys() inside MyBatis.
Relevant code snippets
Service / Repository layer
List<XXXDTO> entities; // data acquisition omitted
Map<String, Object> param = Maps.newHashMap();
param.put("list", entities);
XXXMapper.batchInsert(param);Mapper interface
int batchInsert(Map<String, Object> param);XML mapper (original, problematic)
<insert id="batchInsert" keyProperty="id" useGeneratedKeys="true" parameterType="com.xxx.XXXDTO">
insert into table_name
<trim prefix="(" suffix=")" suffixOverrides=",">
column,
</trim>
<foreach collection="list" item="item" index="index" separator="," open="values">
<trim prefix="(" suffix=")" suffixOverrides=",">
#{item.columnName,jdbcType=VARCHAR},
</trim>
</foreach>
</insert>The error occurs only when useGeneratedKeys=true is present and the parameter is a Map containing a list key.
5. Trigger Conditions
Removing useGeneratedKeys=true eliminates the exception.
Using a List parameter directly (instead of a Map) also avoids the error.
6. Fix Example (list‑parameter approach)
Mapper interface
int batchInsert(@Param("list") Collection<XXXDTO> list);XML mapper
<insert id="batchInsert" keyProperty="id" useGeneratedKeys="true" parameterType="java.util.Collection">
insert into table_name
<trim prefix="(" suffix=")" suffixOverrides=",">
column,
</trim>
<foreach collection="list" item="item" index="index" separator="," open="values">
<trim prefix="(" suffix=")" suffixOverrides=",">
#{item.columnName,jdbcType=VARCHAR},
</trim>
</foreach>
</insert>7. Root‑Cause Analysis
In MyBatis 3.5.6, Jdbc3KeyGenerator.assignKeys() calls collectionize(parameter). When the parameter is a Map, collectionize wraps the whole map as a single element, so the generator expects one target object but receives multiple generated keys, leading to the “Too many keys are generated” exception. The older 3.4.6 version contained special handling for Map (checking keys like list, collection, etc.), which was removed in the newer version.
8. Summary of Findings
The upgrade eliminated Map‑parameter support for useGeneratedKeys, causing the exception in scenarios that rely on that implicit behavior.
9. Solutions
Temporary measures
Replace batch insert with a loop of single inserts (acceptable for low volume or non‑time‑critical data).
Remove useGeneratedKeys=true when the generated key is not needed.
Fundamental fix
Adopt the list‑parameter style shown in section 6 and avoid passing a Map for batch inserts. This aligns with the current MyBatis contract and prevents the bug.
Note: The issue is reported on MyBatis GitHub (issue #3536) and is unlikely to be fixed in future releases, so upgrading further will not resolve it.
10. Recommendations
Perform thorough version‑upgrade research, focusing on breaking changes and bug fixes.
Write unit tests for batch‑insert operations to catch similar regressions early.
Prefer explicit, strongly‑typed parameters (e.g., List<User>) and use @Param annotations to define intent clearly.
Document framework‑level contracts and avoid relying on undocumented internal behaviors.
11. References
Official source code: https://github.com/mybatis
Bug discussion: https://github.com/mybatis/mybatis-3/issues/2375
Issue report: https://github.com/mybatis/mybatis-3/issues/3536
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
