news 2026/9/25 7:38:50

MyBatis 调用存储过程返回游标:parameterType 为什么必须是 java.util.Map 及 TaoToken 配置骨架

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MyBatis 调用存储过程返回游标:parameterType 为什么必须是 java.util.Map 及 TaoToken 配置骨架

1. 为什么 parameterType 写错就报 "无效的列类型"

先说结论:MyBatis 调 Oracle 或达梦的存储过程、函数返回游标时,parameterType必须写成java.util.Map,不是因为它"看起来方便",而是因为游标这个 OUT 参数在 JDBC 层面根本没有对应的 Java 基本类型可以承载,只能靠一个可变的容器把结果"回填"回来。

我见过太多人卡在这一步:mapper 里写了parameterType="java.util.Map",但调用时传了个实体对象,或者干脆不传参,结果控制台甩出一句无效的列类型: 1111或者ORA-17004。1111 就是java.sql.Types.OTHER,Oracle 的游标(REF CURSOR)在 JDBC 里就注册成这个类型。MyBatis 处理 CALLABLE 语句时,会遍历你传入的参数对象,把每个mode=OUT的参数按名字塞回去。如果传入的不是 Map,它没有"按名字回填"的能力,游标就丢了。

这篇面向的是正在用 Cline、CC Switch 这类 AI 工具辅助排查 MyBatis 配置的开发者。你可能会让 AI 帮你补全 mapper XML,但 AI 经常把parameterType写成实体类,因为它不知道游标要回填。下面我把可复制的骨架、TaoToken 的统一 Key 配置、以及一次调用后打印 Map 里游标键值的验证动作都写清楚,你照着改就能跑通。

核心检索词先摆出来:MyBatis 调用存储过程返回游标、parameterType 必须是 java.util.Map、游标结果回填 Map、map.get("result")取游标记录。适合谁?适合用 Oracle/达梦、需要调P_xxx或FN_xxx返回结果集、并且被 1111 报错折磨过的后端同学。

2. TaoToken 前置:把 Key 和 API 通道统一起来

在动手改 mapper 之前,先把调用链路的"通道"理顺。很多排查场景里,AI 工具(Cline、CC Switch)需要访问模型来帮你分析报错,而你的业务代码又要访问数据库。这两条链路如果 Key 散落在各处,改一次配置要翻五个文件。我的做法是用 TaoToken 做统一入口,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end ,API 地址是 https://taotoken.net/api 。

你需要先拿到一个 Key。进控制台创建:https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,然后在 API Keys 页面生成:https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。这个 Key 同时给 AI 工具和你的调试脚本用,省得来回切。

为什么要在讲 MyBatis 的文章里提这个?因为游标报错的排查过程,本质是"让 AI 读你的 mapper XML 和异常栈,然后给出修改建议"。如果 AI 工具的模型通道没配好,你连让它帮你分析1111的机会都没有。把通道配好,后面排错效率完全不一样。

如果你只是想让 AI 帮你解释一段 mapper 配置,用模型对话就行:https://taotoken.net/model-chat?utm_source=taotoken_aicg_blog_end&utm_content=model-chat&utm_campaign=rewrite 。如果你在长期做编码、想让 Agent 持续读你的工程文件,那更适合 Coding Plan:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,ClaudeCode 相关的配置参考 https://taotoken.net/claudecode-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode-anthropic&utm_campaign=rewrite 。

3. 可复制配置:mapper XML 骨架与工具配置片段

3.1 函数返回游标的 mapper 骨架

先看函数版本。Oracle 函数返回SYS_REFCURSOR,MyBatis 里用{#{result,mode=OUT,...}=call FN_xxx(...)}这种带等号的写法,等号左边就是接收返回值的占位符。

<resultMap id="cursorMap" type="com.vcare.model.Photo"> <result column="AD_PIC_ID" property="photoId" jdbcType="INTEGER"/> <result column="SRC" property="url" jdbcType="VARCHAR"/> </resultMap> <select id="findAllPhoto" parameterType="java.util.Map" statementType="CALLABLE"> {#{result,mode=OUT,jdbcType=CURSOR,resultMap=cursorMap} = call FN_QUERY_APP_AD_PIC(#{agencyId,mode=IN,jdbcType=INTEGER})} </select>

注意resultMap="cursorMap"这个属性,它告诉 MyBatis 游标里的每一行按什么规则映射成Photo对象。没有它,游标就算回填进 Map,你也拿不到结构化数据。

3.2 存储过程返回游标的 mapper 骨架

存储过程是另一种写法,游标作为普通 OUT 参数放在参数列表里,没有等号。

<select id="findAllPhotoProc" parameterType="java.util.Map" statementType="CALLABLE"> {call P_QUERY_APP_AD_PIC( #{agencyId,mode=IN,jdbcType=INTEGER}, #{result,mode=OUT,jdbcType=CURSOR,resultMap=cursorMap} )} </select>

两种写法的共同点:parameterType都是java.util.Map,result这个名字随便起,不需要和存储过程里的形参名一致,它只是 Map 里的一个 key。

3.3 Cline 的 settings.json 配置片段

如果你用 Cline 辅助排查,把模型通道指向 TaoToken,Key 填你生成的那个。

{ "cline.apiProvider": "openai", "cline.openAiBaseUrl": "https://taotoken.net/api", "cline.openAiApiKey": "sk-你的TaoTokenKey", "cline.model": "claude-sonnet-4-20250514" }

3.4 CC Switch 的 config.toml 配置片段

CC Switch 用 TOML,结构类似,核心是 base_url 和 api_key 两项。

[provider] name = "taotoken" base_url = "https://taotoken.net/api" api_key = "sk-你的TaoTokenKey" model = "claude-sonnet-4-20250514"

配好之后,你让 AI 读 mapper XML,它就能基于真实文件给出修改建议,而不是凭空猜。

4. 验证请求:一次调用后打印 Map 里的游标键值

配置写完,必须验证游标真的回填了。写个最小测试,调用后把 Map 里的result取出来打印。

@Autowired private PhotoMapper photoMapper; public void testCursor() { Map<String, Object> param = new HashMap<>(); param.put("agencyId", 1001); photoMapper.findAllPhoto(param); Object cursor = param.get("result"); System.out.println("游标类型: " + cursor.getClass().getName()); if (cursor instanceof List) { List<Photo> list = (List<Photo>) cursor; System.out.println("游标记录数: " + list.size()); for (Photo p : list) { System.out.println("photoId=" + p.getPhotoId() + ", url=" + p.getUrl()); } } }

跑通后你会看到类似输出:

游标类型: java.util.ArrayList 游标记录数: 3 photoId=101, url=https://cdn.example.com/a.jpg photoId=102, url=https://cdn.example.com/b.jpg photoId=103, url=https://cdn.example.com/c.jpg

关键点:param.get("result")拿到的就是游标记录,类型是ArrayList,里面是按cursorMap映射好的Photo对象。如果你打印出来是null,说明 OUT 参数没回填,八成是parameterType写错了,或者传参时没传 Map。

注意:result这个名字只是 Map 的 key,和存储过程里的形参名没有任何对应关系。你叫它cursorData、outRows都行,只要 mapper 里的占位符名和map.get()的 key 一致即可。

5. 本篇常见错排查

5.1 报 "无效的列类型: 1111"

这是最高频的错。1111 是Types.OTHER,Oracle 游标就注册成这个。原因通常是parameterType写成了实体类,或者jdbcType=CURSOR漏了。检查两处:mapper 的parameterType必须是java.util.Map,OUT 参数必须带jdbcType=CURSOR。

5.2 游标取出来是 null

传参时传了实体对象而不是 Map,或者 Map 里没有对应的 key。MyBatis 回填 OUT 参数时,是往你传入的那个 Map 里 put,如果你传的是new Photo(),它没有地方放游标。统一用HashMap。

5.3 达梦数据库上的差异

达梦的游标类型和 Oracle 基本兼容,但驱动版本不同时,jdbcType可能需要写成OTHER而不是CURSOR。如果CURSOR报错,换成jdbcType=OTHER试试。另外达梦的存储过程调用语法和 Oracle 一致,mapper 骨架不用改。

5.4 resultMap 映射不上,字段全是 null

检查resultMap里的column是否和游标返回的列名完全一致,包括大小写。Oracle 默认返回大写列名,如果你写的是小写,映射不上。达梦可能返回大写也可能返回小写,取决于建表时的设置。用column="AD_PIC_ID"这种大写形式最稳。

5.5 函数和存储过程写法混了

函数用等号:{#{result,...}=call FN_xxx(...)}。存储过程不用等号:{call P_xxx(..., #{result,...})}。写混了会报语法错误。如果你不确定,先看数据库里是 FUNCTION 还是 PROCEDURE。

6. 把 Key 和游标配置一起固化下来

排查完这一轮,你会发现真正花时间的不是写 mapper,而是"改一处配置、跑一次、看报错、再改"的循环。把 TaoToken 的 Key 固化到 Cline 的 settings.json 和 CC Switch 的 config.toml 里,让 AI 工具随时能读你的工程文件,这个循环会快很多。API 通道统一走 https://taotoken.net/api ,Key 在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 生成一次就够。

回到游标本身,记住三件事:parameterType必须是java.util.Map,OUT 参数必须带jdbcType=CURSOR和resultMap,取结果用map.get("result")。这三件事做对,Oracle 和达梦的游标都能正常回填。至于result这个名字,真的随便起,别被它和存储过程形参名的"看起来对应"骗了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 7:35:37

Python采集中国天气网天气数据:JSON接口与城市ID实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 7:35:11

嵌入式MCU开发全链路:编译、烧录与仿真流程详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 7:34:49

Atlas 300V 24G推理卡跑YOLO:从环境搭建到部署调优全指南

1. 一台推理卡&#xff0c;为什么值得单独写一篇先说结论&#xff1a;Atlas 300V 24G是华为昇腾生态里一款纯推理场景的加速卡&#xff0c;目标对象非常明确——跑YOLO这类检测模型&#xff0c;做视频流分析、边缘智能、工业质检、园区安防等任务。很多刚接触昇腾的人会被一堆名…

作者头像 李华
网站建设 2026/9/25 7:33:46

Windows11本地部署OpenClaw:从WSL2环境到飞书接入的完整实践

最近我在 Windows11 上折腾 OpenClaw&#xff0c;前前后后花了两天&#xff0c;把一个“装不上、跑不通”的状态调到了稳定运行&#xff0c;现在它每天定时抓资讯、生成摘要、发到飞书&#xff0c;基本替代了我早上刷新闻的习惯。OpenClaw 本质上不是又一个聊天框&#xff0c;而…

作者头像 李华
网站建设 2026/9/25 7:33:22

Word尾注脚注管理全攻略:插入、删除与去横线技巧

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华