1. 为什么存储过程返回游标在 MyBatis 里总踩坑MyBatis 调用存储过程返回游标是很多做企业级报表、对账、批量校验的同学绕不开的场景。存储过程里open v_Cursor for select ...把结果集通过OUT参数吐回来Java 侧拿到的其实是一个 JDBC 游标对象而不是普通的List。如果你按普通select的写法去接大概率会拿到一个空集合或者直接抛ORA-01000、Invalid column type之类的异常。这篇聚焦一个真实可跑的实例Oracle 存储过程Fsp_Plan_CheckPrj接收两个入参、返回一个sys_refcursorMyBatis 的 mapper XML 用statementTypeCALLABLE声明OUT参数用jdbcTypeCURSOR加resultMap映射列别名。同时我会把 MySQL 场景下没有原生游标、需要用OUT结果集或临时表替代的差异也讲清楚避免你从 Oracle 迁到 MySQL 时直接照抄报错。适合谁看已经会写基础 MyBatis CRUD、但第一次接触CALLABLE和游标映射的后端同学或者手上有个老项目存储过程是 DBA 写好的你只负责在 Java 层把它调通。整篇的配置骨架我会用 TaoToken 统一 Key 来管理模型侧和编码侧的调用凭证这样你在调试 SQL 映射的同时也能顺手把 AI 辅助编码的入口配好不用在多个平台之间来回切 Key。核心检索词先摆出来MyBatis 调用存储过程返回游标关键三件套是statementTypeCALLABLE、modeOUT jdbcTypeCURSOR、resultMap列映射。记住这三个后面所有报错基本都能定位到其中之一。2. TaoToken 前置统一 Key 与配置骨架在动手写 mapper 之前先把调用凭证这块理顺。TaoToken 的作用是把模型对话、编码计划、API Key 管理收敛到一个入口你只需要维护一份 Key就能在 IDE 插件、命令行工具、脚本里复用。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 注意 API 地址不带 UTM 参数配置里填这个就行。先拿 Key进入控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 在 API Keys 页面创建一个新 Key复制出来。这个 Key 就是后面所有配置里的apiKey字段。如果你用的是 Claude Code 这类编码 Agent可以直接走 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它会把长期编码任务的额度单独管理不会和你临时调试模型对话的消耗混在一起。配置骨架分两种格式按你用的工具选。VS Code 系插件一般读settings.json命令行工具或 Rust 系工具读config.toml。下面两份都是可复制的骨架把sk-你的Key替换成刚才创建的值即可。settings.json骨架{ taotoken.apiKey: sk-你的Key, taotoken.baseUrl: https://taotoken.net/api, taotoken.model: claude-sonnet, taotoken.timeout: 60000, taotoken.maxTokens: 8192 }config.toml骨架[taotoken] api_key sk-你的Key base_url https://taotoken.net/api model claude-sonnet timeout 60000 max_tokens 8192注意base_url只写到/api不要在后面拼/v1或/chat/completions具体路径由客户端自己补。填错这一层最常见的表现是 404而不是鉴权失败排查时先看状态码。Key 创建页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。文档里有各语言 SDK 的调用示例遇到参数名对不上时以文档为准。模型对话调试入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 你可以先在网页里发一条消息确认 Key 有效再去配本地工具这样能把「Key 问题」和「工具配置问题」分开。3. 可复制配置mapper XML 与 Java 调用3.1 Oracle 存储过程与 mapper XML先看存储过程本身它接收v_grantno、v_deptcode两个入参第三个是OUT游标create or replace procedure Fsp_Plan_CheckPrj( v_grantno varchar2, v_deptcode number, v_cursor out sys_refcursor ) is begin open v_cursor for select s.plan_code, s.plan_dept, s.plan_amount, s.exec_amount, p.cname as plan_name, d.cname as dept_name from Snap_plan_check s left join v_plan p on s.plan_code p.plan_code left join org_office d on s.plan_dept d.off_org_code group by s.plan_code, s.plan_dept, s.plan_amount, s.exec_amount, p.cname, d.cname; end Fsp_Plan_CheckPrj;mapper XML 的关键在于resultMap把列别名映射到 Java 属性select标签声明statementTypeCALLABLEOUT参数写modeOUT jdbcTypeCURSOR resultMapcursorMapresultMap typejava.util.HashMap idcursorMap result columnplan_code propertyplan_code/ result columnplan_dept propertyplan_dept/ result columnplan_amount propertyplan_amount/ result columnexec_amount propertyexec_amount/ result columnplan_name propertyplan_name/ result columndept_name propertydept_name/ /resultMap select idcall_Fsp_Plan_CheckPrj parameterTypemap statementTypeCALLABLE {call Fsp_Plan_CheckPrj( #{grantNo, jdbcTypeVARCHAR, modeIN}, #{offOrgCode, jdbcTypeINTEGER, modeIN}, #{v_cursor, modeOUT, jdbcTypeCURSOR, resultMapcursorMap} )} /select这里有几个容易写错的点。jdbcTypeCURSOR是 Oracle 驱动识别的类型MySQL 没有这个类型后面会单独说。resultMap的type用java.util.HashMap是为了让列名直接作为 key如果你有实体类换成实体类全限定名也行但属性名要和property对上。modeIN的两个参数必须显式写jdbcType否则某些驱动版本会报Invalid column type。3.2 Java 调用代码Java 侧把OUT参数先塞一个空ArrayList占位调用后 MyBatis 会把游标结果填回来MapString, Object params new HashMap(); GrantSetting gs this.grantSettingDao.get(grantCode); params.put(grantNo, StringUtils.substring(gs.getGrantNo(), 0, 2)); params.put(offOrgCode, SecurityUtils.getPersonOffOrgCode()); params.put(v_cursor, new ArrayListMapString, Object()); this.batisDao.getSearchList(call_Fsp_Plan_CheckPrj, params); ListMapString, Object rows (ListMapString, Object) params.get(v_cursor); for (MapString, Object row : rows) { System.out.println(row.get(plan_code) / row.get(plan_name)); }getSearchList是你 DAO 里封装的方法内部走sqlSession.selectList(call_Fsp_Plan_CheckPrj, params)。调用完成后params.get(v_cursor)就是可遍历的List每个元素是一行key 是resultMap里的property名。实测下来这个 List 是懒加载的如果你在sqlSession关闭后才去遍历可能拿不到数据所以遍历动作要放在同一个会话内。3.3 MySQL 场景的差异MySQL 存储过程没有sys_refcursor这种原生游标类型通常用两种替代一是存储过程直接select结果集MyBatis 用普通select接二是用OUT参数返回结果集但驱动支持有限。如果你从 Oracle 迁过来最稳的做法是把OUT游标改成存储过程内的selectmapper 里去掉CALLABLE按普通查询写。下面是一个 MySQL 的等价写法delimiter // create procedure Fsp_Plan_CheckPrj( in v_grantno varchar(32), in v_deptcode int ) begin select s.plan_code, s.plan_dept, s.plan_amount, s.exec_amount, p.cname as plan_name, d.cname as dept_name from Snap_plan_check s left join v_plan p on s.plan_code p.plan_code left join org_office d on s.plan_dept d.off_org_code group by s.plan_code, s.plan_dept, s.plan_amount, s.exec_amount, p.cname, d.cname; end // delimiter ;对应的 mapper 就是普通selectstatementType用默认的PREPARED不需要resultMap里的游标映射。这个差异一定要在迁移前确认否则你会对着jdbcTypeCURSOR报错半天最后发现是数据库类型不对。4. 验证请求与成功结果配置写完后先别急着上业务代码用最小验证跑一遍。第一步确认 TaoToken 的 Key 有效在模型对话入口发一条测试消息能正常返回就说明凭证没问题。第二步验证数据库连接和存储过程本身用 SQL 客户端直接call Fsp_Plan_CheckPrj(01, 1001, ?)看游标能不能出数据。第三步才是走 MyBatis。MyBatis 侧的验证建议写一个独立的单元测试不要混在业务链路里Test public void testCallProcedure() { MapString, Object params new HashMap(); params.put(grantNo, 01); params.put(offOrgCode, 1001); params.put(v_cursor, new ArrayListMapString, Object()); sqlSession.selectList(call_Fsp_Plan_CheckPrj, params); ListMapString, Object rows (ListMapString, Object) params.get(v_cursor); assertNotNull(rows); assertTrue(rows.size() 0); System.out.println(返回行数: rows.size()); System.out.println(首行: rows.get(0)); }成功的结果长这样控制台打印出返回行数和首行内容首行的 key 是plan_code、plan_name这些别名value 是对应字段值。如果rows是空 List先别改代码去 SQL 客户端确认存储过程本身有没有数据。如果rows是null说明OUT参数没被正确回填重点查jdbcType和resultMap是否写对。提示Oracle 驱动版本不同CURSOR类型的处理方式略有差异。如果你用的是ojdbc8以上jdbcTypeCURSOR一般没问题老版本ojdbc14可能需要换成jdbcTypeOTHER并配合typeHandler。这个坑我在老项目里踩过换驱动比改代码省事。5. 本篇常见报错排查5.1 ORA-01000 游标数超限报错信息类似ORA-01000: maximum open cursors exceeded。原因是游标没关闭每次调用都开一个新的。排查方向确认sqlSession有没有正常关闭Spring 环境下检查事务边界如果用了连接池检查连接归还时游标是否释放。临时缓解可以调大数据库的open_cursors参数但根治还是要保证会话关闭。5.2 Invalid column type这个报错通常出现在OUT参数的jdbcType上。Oracle 的sys_refcursor必须写jdbcTypeCURSOR写成VARCHAR或OTHER都可能报错。另外IN参数如果没写jdbcType某些驱动也会报这个。逐个参数补上jdbcType基本能解决。5.3 返回结果为空 List分三种情况。一是存储过程本身没查到数据去 SQL 客户端验证。二是resultMap的column和存储过程里的列别名不一致比如存储过程写plan_nameresultMap写planName映射不上就是空值。三是OUT参数的占位对象类型不对必须是new ArrayList()不能是null或String。5.4 TaoToken 侧 401 或 404401 是 Key 无效或过期去 API Keys 页面重新生成。404 是base_url写错确认只写到https://taotoken.net/api不要带多余路径。如果两个都排除了还是不通去接入文档对照一下请求头格式有些客户端需要显式带Authorization: Bearer sk-xxx。5.5 MySQL 下 jdbcTypeCURSOR 报错MySQL 没有CURSOR这个 JDBC 类型写了必然报错。解决办法是按 3.3 节的方案把存储过程改成直接selectmapper 去掉CALLABLE和OUT游标参数。如果你必须保留OUT参数MySQL 需要用jdbcTypeOTHER配合自定义TypeHandler复杂度高不推荐。6. 把配置沉淀下来下次直接复用整套流程跑通后建议把三样东西沉淀成模板mapper XML 的CALLABLE骨架、Java 侧的params组装代码、TaoToken 的settings.json/config.toml配置。下次遇到新的存储过程只改存储过程名、参数名和resultMap列映射其余照抄能省掉大量试错时间。如果你在配 TaoToken 的过程中遇到 Key 或路径问题直接去 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 重新生成一个再对照接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 检查请求格式。长期做编码 Agent 任务的话Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 能把额度单独管理不会和临时调试混在一起。模型对话调试入口在 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 先用它确认 Key 有效再去配本地工具排查链路会清晰很多。最后留一个实用技巧存储过程的resultMap列映射建议和存储过程里的select别名逐字对照写不要凭记忆。我见过太多空 List 的案例最后都是plan_code和planCode这种大小写或下划线差异导致的。把这两处放在一起比对比任何调试工具都快。
企业数字化 ERP 产品动态
相关推荐
吉林百度seo实战:3个性能优化技巧让排名翻倍 吉林百度seo实战:3个性能优化技巧让排名翻倍 自己不会代码想做网站,又怕做出来慢、没流量?别慌。在吉林做企业站,光有内容不够,百度看的是综合体验。很多老板花几万块建站,结果打开要5秒,用户直接划走,搜索引擎自然不收录。 核心就俩字:… · 2026/9/27 12:30:37
找网站建制作公司别被坑,这份保姆级建站教程帮你省下5万 找网站建制作公司别被坑,这份保姆级建站教程帮你省下5万 找网站建制作公司,最让人头疼的不是技术难题,而是怕被坑高价。很多老板看着报价单上的五万、八万,心里直打鼓:这钱到底花得值不值?是不是被割了韭菜?别急,今天这篇保姆级建站教程,不灌鸡汤,… · 2026/9/27 12:30:31
做的网站百度排名没有图片显示最佳实践避坑指南 做的网站百度排名没有图片显示最佳实践避坑指南 自己不会代码想做网站,结果上线后百度搜索里全是“图裂”或者干脆没图?别急,这根本不是玄学,而是技术配置没到位。很多设计师转前端的朋友,或者想搞官网的老板,最头疼的就是这个:本地看挺好,一上线百度… · 2026/9/27 13:16:14
避坑指南:软件下载网站怎么做完整流程才不拖工期 避坑指南:软件下载网站怎么做完整流程才不拖工期 改个需求建站公司拖一周,这种憋屈事儿你是不是也遇过?明明只是换个图标、调下字体,对方却以“排期满了”为由让你再等三天。做网站最怕的就是这种黑盒操作,你看不见进度,只能干等。其实,软件下载网站怎… · 2026/9/27 13:16:14
微信域名防封跳转系统速查手册:避开高价坑的实操指南 微信域名防封跳转系统速查手册:避开高价坑的实操指南 找建站公司,最怕听到“这个得加钱”或者“这个技术很复杂,要定制开发”。尤其是涉及微信生态里的域名防封跳转系统,很多甲方对接人一开口问价,对方张口就是几千块起步,还告诉你这是“核心技术”。别… · 2026/9/27 13:16:07
网站设计与建设课后题答案汇总:别被坑,建站到底多少钱 网站设计与建设课后题答案汇总:别被坑,建站到底多少钱 模板网站太丑不够用?这是无数企业老板和运营人员的噩梦。花了几千块买个模板,上线后客户一眼看穿廉价感,转化率直接跳水。更扎心的是,很多所谓的“专家”拿着《网站设计与建设》的课后题答案忽悠人… · 2026/9/27 13:16:01
此案例中采用了什么样的网络营销方式:3步拆解完整流程 此案例中采用了什么样的网络营销方式:3步拆解完整流程 网站做好了没人访问,这才是最让人头疼的事。很多老板花几万块做了个漂亮的官网,上线半年,后台数据一片惨淡,连个咨询电话都没有。这时候问乙方,他们往往两手一摊,说“这是运营的事,不是建站的事… · 2026/9/27 13:15:37
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01
MATLAB雷达信号脉冲压缩仿真:LFM线性调频、匹配滤波与距离分辨率实现 简介:这套Matlab仿真工具完整呈现雷达信号脉冲压缩过程,从线性调频(LFM)信号生成、目标回波仿真到匹配滤波压缩处理均有可运行代码支撑,面向电子信息工程、计算机、数学等专业学生,适用于课程设计、期末大作… · 2026/9/27 0:00:01
汕头网站建设制作厂家避坑指南:5大注意事项救急 汕头网站建设制作厂家避坑指南:5大注意事项救急 改个需求建站公司拖一周,这种憋屈事我见得太多了。 很多汕头老板找本地建站团队,签合同前看着方案挺美,一上线就变脸。 今天不聊虚的,直接拆解找 汕头网站建设制作厂家 时的5个核心 注意事项… · 2026/9/27 0:00:01
多模态虚假新闻检测实战:BERT+ResNet双塔与对比学习 简介:基于PyTorch的多模态虚假新闻检测项目完整代码包,面向自然语言处理与计算机视觉交叉方向的开发者、科研人员及毕业设计选题者,解决社交媒体中文本与图像联合识别虚假新闻的问题。系统以BERT预训练模型提取文本语义特征,以Res… · 2026/9/27 0:00:01