You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Node.js调用Oracle存储过程遇DPI-1010错误的原因及解决咨询

解决Node.js连接Oracle存储过程时的DPI-1010错误

我来帮你拆解一下问题的根源,然后给出具体的解决办法:

一、添加connection.close()直接报错的原因

你遇到的问题核心在于:Oracle返回的自定义数据对象(比如存储过程输出的p_ARRAYOUTp_islemler这类数组)是绑定当前数据库连接的。如果在处理这些返回对象之前就调用connection.close(),连接断开后,这些对象就失去了数据源支撑,后续访问它们(比如执行convertArray(emir))时就会触发DPI-1010: not connected错误。

简单说就是你关闭连接的时机太早了,必须等所有从数据库返回的对象都转换成普通JS数据结构后,再关闭连接。

二、不添加close()后续仍报错的原因

每次调用dbCEI都会通过oracledb.getConnection()获取一个新连接,但如果不主动关闭,这些连接会一直占用Oracle的连接资源。当连接数达到Oracle数据库的最大连接数限制(默认通常是150左右),后续的连接请求会失败,或者Oracle会主动回收闲置连接,导致后续操作时连接已经失效,同样抛出DPI-1010错误。

三、具体解决方案

1. 调整连接关闭的时机

先完成所有返回数据的转换,确保不再依赖Oracle连接,再关闭连接。修改你的代码如下:

async dbCEI(inOut){ 
  let connection; // 提前声明,确保catch块也能访问到连接
  try{ 
    connection = await oracledb.getConnection(dbConfig); 
    var tp1 = await connection.getDbObjectClass(this.typeName1); 
    var tp2 = await connection.getDbObjectClass(this.typeName2); 
    var getbinds = infoEmirIslem(inOut,tp1,tp2); 
    await connection.execute(this.nlsLang); 
    await connection.execute(this.nlsDateFormat); 
    var result = await connection.execute(this.procedureCall,getbinds) 
    var firstinOut = result.outBinds.p_cq; 
    var emir = result.outBinds.p_ARRAYOUT; 
    var islem = result.outBinds.p_islemler; 

    // 先完成所有数据转换,脱离Oracle连接依赖
    var emirArray = convertArray(emir); 
    var islemArray = convertArray(islem); 

    // 现在可以安全关闭连接了
    await connection.close(); 

    return [firstinOut, emirArray, islemArray]; 
  }catch(err){ 
    console.log(err);
    // 出错时也要确保连接被关闭,避免资源泄漏
    if (connection) {
      try {
        await connection.close();
      } catch (closeErr) {
        console.error('关闭连接时出错:', closeErr);
      }
    }
    // 抛出错误让调用方处理,不要吞掉错误
    throw err;
  } 
}

关键调整:

  • connection变量提升到try外部,确保异常场景下也能关闭连接
  • 先执行数据转换,再关闭连接,避免转换过程中依赖已断开的连接
  • 在catch块中添加连接关闭逻辑,防止连接泄漏

2. 使用连接池优化连接管理

每次创建新连接的开销很大,而且容易导致连接数耗尽。推荐使用Oracle的连接池来复用连接资源:

第一步:初始化连接池(应用启动时执行一次)

async function initOraclePool() {
  try {
    await oracledb.createPool({
      ...dbConfig,
      poolMin: 2, // 最小空闲连接数
      poolMax: 10, // 最大连接数,根据你的Oracle配置调整
      poolIncrement: 1,
      poolTimeout: 60 // 连接闲置超时时间(秒)
    });
    console.log('Oracle连接池初始化成功');
  } catch (err) {
    console.error('初始化连接池失败:', err);
    process.exit(1);
  }
}

// 在应用启动阶段调用初始化
initOraclePool();

第二步:修改dbCEI函数使用连接池

async dbCEI(inOut){ 
  let connection; 
  try{ 
    // 从连接池获取连接,而不是创建新连接
    connection = await oracledb.getConnection(); 
    // 后续逻辑和调整后的代码一致
    var tp1 = await connection.getDbObjectClass(this.typeName1); 
    var tp2 = await connection.getDbObjectClass(this.typeName2); 
    var getbinds = infoEmirIslem(inOut,tp1,tp2); 
    await connection.execute(this.nlsLang); 
    await connection.execute(this.nlsDateFormat); 
    var result = await connection.execute(this.procedureCall,getbinds) 
    var firstinOut = result.outBinds.p_cq; 
    var emir = result.outBinds.p_ARRAYOUT; 
    var islem = result.outBinds.p_islemler; 

    var emirArray = convertArray(emir); 
    var islemArray = convertArray(islem); 

    await connection.close(); // 归还连接到池,不是真正关闭
    return [firstinOut, emirArray, islemArray]; 
  }catch(err){ 
    console.log(err);
    if (connection) {
      try {
        await connection.close();
      } catch (closeErr) {
        console.error('归还连接到池时出错:', closeErr);
      }
    }
    throw err;
  } 
}

使用连接池后,connection.close()是把连接归还到池中复用,而非销毁,既能提升性能,又能避免连接数耗尽的问题。

3. 额外注意事项

  • 确保convertArray函数是将Oracle自定义数组完全转换成纯JS数组,不要保留对Oracle对象的引用,否则即使关闭连接后仍可能报错。
  • 检查Oracle数据库的processes参数(最大连接数),确保连接池的poolMax值不超过该参数。可以通过查询SELECT value FROM v$parameter WHERE name = 'processes'查看当前配置。

内容的提问来源于stack exchange,提问作者safa aytan

火山引擎 最新活动