You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 13:20:28