为了拆分库表,为了省事在xml文件中直接注释:

1
2
3
4
5
6
7
8
9
10
    <select id="queryValidGroupBuyActivityId" resultMap="GroupBuyActivityMap">
SELECT
activity_id, activity_name,
-- source, channel, goods_id,
discount_id, group_type, take_limit_count, target, valid_time,
status, start_time, end_time, tag_id, tag_scope
FROM group_buy_activity
-- WHERE source = #{source} AND channel = #{channel}
WHERE activity_id = #{activityId} AND status = 1
</select>

这里直接自动注释了 Ctrl + /

但是应该这样

1
2
3
4
5
6
7
8
9
10
<select id="queryValidGroupBuyActivityId" resultMap="GroupBuyActivityMap">
SELECT
activity_id, activity_name,
<!-- source, channel, goods_id, -->
discount_id, group_type, take_limit_count, target, valid_time,
status, start_time, end_time, tag_id, tag_scope
FROM group_buy_activity
<!-- WHERE source = #{source} AND channel = #{channel} -->
WHERE activity_id = #{activityId} AND status = 1
</select>

导致一直报错


这个问题你问得非常到位,而且你刚刚的判断是对的:

在 MyBatis 的 Mapper.xml 里,用 -- 注释“并不安全”
你现在这种现象,正是 MyBatis + SQL 注释 + 参数解析的经典坑

我给你一个结论先行 + 原理解释 + 正确做法,你一看就通透。


一、先给结论(重要)

👉 在 MyBatis 的 <select> 里,不要用 -- 注释包含参数的 SQL 行
👉 推荐只用 XML 注释 <!-- -->,不要混用 SQL 注释

你刚才:

1
-- WHERE source = #{source} AND channel = #{channel}

👉 这行虽然“看起来”被注释了,但 MyBatis 在某些情况下仍然会解析到 #{channel}

所以你会看到这种诡异错误:

1
Parameter index out of range (2 > number of parameters, which is 1)

二、为什么“去掉注释就好了”?(核心原理)

1️⃣ MyBatis 做了两件事(不是你以为的一步)

MyBatis 处理 Mapper XML 时,大致是:

  1. 先解析 XML
  2. 再解析 SQL 占位符(#{})生成 ParameterMapping
  3. 最后才交给 JDBC 执行

⚠️ 重点
👉 MyBatis 的参数解析 ≠ 数据库的 SQL 执行


2️⃣ -- 是“数据库注释”,不是“MyBatis 注释”

你写的是:

1
-- WHERE source = #{source} AND channel = #{channel}

MySQL 来说:

这是注释 ✔

但对 MyBatis SQL 解析器 来说:

❌ 它可能已经扫描到了 #{channel} 并生成了 ParameterMapping

结果就是:

  • SQL 真正执行时:只有 1 个 ?
  • MyBatis 绑定参数时:想绑定 2 个参数
  • JDBC:直接炸 💥

3️⃣ 这就是你看到的异常链

1
2
3
4
5
6
7
MyBatis 认为有 channel 参数

PreparedStatement 只有 1 个 ?

setParameter(2, channel)

Parameter index out of range (2 > number of parameters, which is 1)

👉 完全解释得通


三、为什么你“刚刚去掉注释就好了”?

因为你现在的 SQL 变成了:

1
WHERE activity_id = #{activityId} AND status = 1
  • MyBatis 只解析到 #{activityId}
  • PreparedStatement 也只有 1 个 ?
  • 参数数量一致 ✅
  • 一切恢复正常 ✅

四、MyBatis 中“正确 / 错误”的注释方式对比

❌ 不推荐(你刚才踩的坑)

1
2
-- WHERE source = #{source} AND channel = #{channel}
/* WHERE source = #{source} */

⚠️ 凡是 SQL 注释中带 #{},都有风险


✅ 推荐方式 1:XML 注释(最安全)

1
<!-- WHERE source = #{source} AND channel = #{channel} -->

👉 MyBatis 完全不会解析


✅ 推荐方式 2:干脆删掉不用的 SQL(最省心)

1
WHERE activity_id = #{activityId} AND status = 1

✅ 推荐方式 3(进阶):用 <if> 控制,而不是注释

1
2
3
4
5
6
7
WHERE status = 1
<if test="source != null">
AND source = #{source}
</if>
<if test="channel != null">
AND channel = #{channel}
</if>

👉 这才是 MyBatis 的正宗玩法


五、你这个问题和 multiThread() 的关系(帮你“解心结”)

你一开始以为:

“我卡在 MarketNode.multiThread()

现在你应该已经明白了:

multiThread 本身完全没问题
是线程里的 DAO → MyBatis → SQL 注释导致异常

而且因为是:

  • 子线程抛异常
  • 主线程在 Future.get() 才感知

👉 所以“看起来”像是卡在多线程

其实是 数据库层面的参数解析炸了


六、一句经验总结(以后你看到就能条件反射)

MyBatis Mapper.xml 中:

  • ❌ 不要在 SQL 注释里写 #{}
  • ✅ 只用 <!-- --> 注释
  • ✅ 条件逻辑用 <if>,不用“注释开关”