Analyzing MyBatis ParameterType and ResultType Misconfiguration Causing Thread Blocking Under High Concurrency
Under high concurrency, improper configuration of MyBatis parameterType and resultType can lead to thread BLOCKED states due to repeated TypeHandler cache misses, and this article analyzes the root causes, demonstrates debugging steps, and provides recommendations to avoid such performance bottlenecks.
When using MyBatis, careless configuration of parameterType and resultType may cause severe performance degradation under high concurrency. The article investigates why certain parameter types trigger thread BLOCKED states, focusing on the interaction with MyBatis's TypeHandlerRegistry and internal caching.
Initial observations show that service restarts generate CPU alerts and many threads become BLOCKED. Stack traces reveal that threads are blocked on ConcurrentHashMap.putVal, which is synchronized inside MyBatis when caching a new TypeHandler. The blockage originates from ParameterMapping resolving a TypeHandler for a parameter type that is not present in the cache.
MyBatis startup flow includes reading XML configuration, parsing statements, building SqlSource, and finally creating ParameterMapping. During step 6 ( ParameterMapping#getJdbcHandlerMap), MyBatis attempts to map Java types to JDBC types and caches the result. If the parameterType does not match the cached key, MyBatis re‑parses the type at execution time, causing the synchronized cache insertion and thread blockage.
Two experimental groups were set up:
Experiment A uses parameterType="java.util.Map" with a HashMap at runtime. Debugging shows both parameters are handled by UnknownTypeHandler, which re‑invokes resolveTypeHandler() for each execution, leading to repeated cache writes.
Control B uses a concrete JavaBean as parameterType. Both parameters are resolved to StringTypeHandler and IntegerTypeHandler at startup, avoiding further cache operations.
Similarly, result handling was examined. Using resultMap (Control) yields no unmapped columns, while using resultType (Experiment) produces a list of unmapped column names, forcing MyBatis to invoke TypeHandlerRegistry again and causing additional contention.
Key recommendations:
Ensure the parameterType declared in XML matches the actual Java type passed (avoid java.util.HashMap when possible).
Prefer concrete JavaBeans over generic Maps for parameters to leverage MyBatis's pre‑loaded TypeHandler cache.
Use resultMap for result mapping instead of resultType to prevent extra type resolution.
MyBatis 3.5.8+ has optimized the TypeHandler caching logic, but projects on older versions (e.g., 3.4.4 with Spring 4.x) can still mitigate the issue by following the above practices.
Relevant code excerpts:
"consumer_order_status_jmq1714_1684822992337" #3125 daemon prio=5 os_prio=0 tid=0x00007fd9eca34000 nid=0x1ca4f waiting for monitor entry [0x00007fd1f33b5000]
java.lang.Thread.State: BLOCKED (on object monitor)
at java.util.concurrent.ConcurrentHashMap.putVal(ConcurrentHashMap.java:1027)
- waiting to lock <0x000000056e822bc8> (a java.util.concurrent.ConcurrentHashMap$Node)
at java.util.concurrent.ConcurrentHashMap.put(ConcurrentHashMap.java:1006)
at org.apache.ibatis.type.TypeHandlerRegistry.getJdbcHandlerMap(TypeHandlerRegistry.java:234)
at org.apache.ibatis.type.TypeHandlerRegistry.getTypeHandler(TypeHandlerRegistry.java:200)
... <select id="listxxxByMap" parameterType="java.util.Map" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from xxxxx
where business_id = #{businessId,jdbcType=VARCHAR}
and template_id = #{templateId,jdbcType=INTEGER}
</select> Map<String, Object> params = new HashMap<>();
params.put("businessId", "11111");
params.put("templateId", "11111");
List<TrackingInfo> result = trackingInfoMapper.listxxxByMap(params); <select id="listResultMap" parameterType="com.jdwl.xxx.domain.TrackingInfo" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
from xxxx
where business_id = #{businessId,jdbcType=VARCHAR}
and template_id = #{templateId,jdbcType=INTEGER}
</select> TrackingInfo record = new TrackingInfo();
record.setBusinessId("11111");
record.setTemplateId(11111);
List<TrackingInfo> result = trackingInfoMapper.listResultMap(record);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.
JD Tech
Official JD technology sharing platform. All the cutting‑edge JD tech, innovative insights, and open‑source solutions you’re looking for, all in one place.
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.
