Express JS调用Oracle带OUT游标存储过程遇NJS-018错误求助
Oracle 11G存储过程调用:NJS-018无效ResultSet问题解决
问题背景
在Express JS中使用oracledb依赖调用Oracle 11G的SP_ASNRSAMERICA存储过程,该过程接收自定义类型ASN_TAB作为输入参数,返回SYS_REFCURSOR类型的输出游标。但尝试从ResultSet获取数据时,抛出Error: NJS-018: invalid ResultSet错误。
存储过程代码
-- 自定义对象类型 CREATE OR REPLACE TYPE ASN_OBJ AS OBJECT ( ASN_NUMBER VARCHAR2(50), SKU VARCHAR2(50), QUANTITY NUMBER ); / -- 自定义表类型 CREATE OR REPLACE TYPE ASN_TAB AS TABLE OF ASN_OBJ; / -- 存储过程 CREATE OR REPLACE PROCEDURE SP_ASNRSAMERICA( P_ASN IN ASN_TAB, P_RESULT OUT SYS_REFCURSOR ) AS BEGIN OPEN P_RESULT FOR SELECT * FROM TABLE(P_ASN); END SP_ASNRSAMERICA; /
JS调用代码
const oracledb = require('oracledb'); const dbConfig = { user: '你的数据库用户名', password: '你的数据库密码', connectString: '你的数据库连接字符串' }; async function callASNProc() { let connection; try { connection = await oracledb.getConnection(dbConfig); // 构造输入的ASN数据 const asnInput = [ { ASN_NUMBER: 'ASN001', SKU: 'PROD001', QUANTITY: 50 }, { ASN_NUMBER: 'ASN002', SKU: 'PROD002', QUANTITY: 30 } ]; const binds = { P_ASN: { type: 'ASN_TAB', dir: oracledb.BIND_IN, val: asnInput }, P_RESULT: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } }; const execResult = await connection.execute( `BEGIN SP_ASNRSAMERICA(:P_ASN, :P_RESULT); END;`, binds, { outFormat: oracledb.OUT_FORMAT_OBJECT } ); // 此处获取ResultSet时抛出NJS-018错误 const resultSet = execResult.outBinds.P_RESULT; let row; while ((row = await resultSet.getRow())) { console.log(row); } await resultSet.close(); } catch (err) { console.error('调用出错:', err); } finally { if (connection) { try { await connection.close(); } catch (closeErr) { console.error('关闭连接出错:', closeErr); } } } } callASNProc();
outBinds日志
{ P_RESULT: ResultSet {} }
排查与修复方案
1. 确认Oracle服务器版本
Oracle 11G必须升级到11.2.0.4及以上版本,早期11G版本对REF CURSOR的处理存在兼容性bug,会导致oracledb驱动无法正确识别ResultSet。oracledb官方明确要求配合11.2.0.4+的Oracle服务器使用,低版本大概率触发NJS-018错误。
2. 修正存储过程的游标查询
避免使用SELECT *,明确指定列名,同时确保TABLE类型的转换逻辑正确:
CREATE OR REPLACE PROCEDURE SP_ASNRSAMERICA( P_ASN IN ASN_TAB, P_RESULT OUT SYS_REFCURSOR ) AS BEGIN OPEN P_RESULT FOR SELECT obj.ASN_NUMBER, obj.SKU, obj.QUANTITY FROM TABLE(P_ASN) obj; END SP_ASNRSAMERICA; /
3. 调整JS代码的ResultSet处理逻辑
显式启用resultSet选项,改用getRows()批量获取数据,避免逐行读取的兼容性问题:
const execResult = await connection.execute( `BEGIN SP_ASNRSAMERICA(:P_ASN, :P_RESULT); END;`, binds, { outFormat: oracledb.OUT_FORMAT_OBJECT, resultSet: true // 显式开启ResultSet模式 } ); // 批量获取所有行 const allRows = await execResult.outBinds.P_RESULT.getRows(); console.log('查询结果:', allRows); // 务必关闭ResultSet await execResult.outBinds.P_RESULT.close();
4. 确保自定义类型绑定正确
如果自定义类型属于特定数据库用户,需要指定完整的类型名称(比如SCOTT.ASN_TAB),同时明确元素类型的属性映射:
const binds = { P_ASN: { type: 'SCOTT.ASN_TAB', // 替换为实际的用户.类型名 dir: oracledb.BIND_IN, val: asnInput, elemInfo: { type: 'SCOTT.ASN_OBJ', attributes: [ { name: 'ASN_NUMBER', type: oracledb.STRING }, { name: 'SKU', type: oracledb.STRING }, { name: 'QUANTITY', type: oracledb.NUMBER } ] } }, P_RESULT: { type: oracledb.CURSOR, dir: oracledb.BIND_OUT } };
5. 检查会话参数设置
在获取连接后,可添加会话参数调整,避免字符集或格式问题影响游标:
await connection.execute(`ALTER SESSION SET NLS_LANG = 'AMERICAN_AMERICA.AL32UTF8'`);
内容的提问来源于stack exchange,提问作者Kevin Valencia Cruz
相关产品推荐
相关产品推荐

