Node.js调用Oracle存储过程遇DPI-1010错误的原因及解决咨询
解决Node.js连接Oracle存储过程时的DPI-1010错误
我来帮你拆解一下问题的根源,然后给出具体的解决办法:
一、添加connection.close()直接报错的原因
你遇到的问题核心在于:Oracle返回的自定义数据对象(比如存储过程输出的p_ARRAYOUT、p_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




