node-oracledb 执行存储过程时出错 NJS-012
node-oracledb error when executing stored procedure NJS-012
我是 运行 节点 8.9.4、Hapi 17.4 和 Oracledb 2.2。
尝试调用存储过程时,出现错误 "NJS-012: encountered invalid bind data type in parameter 2"。我所做的一切似乎都无法解决问题。调用过程的代码是:
async function getSavedViews(req, h, server) {
let connection = await server.app.db.getConnection();
let bindVars = {
P_USER_NAME: req.payload.user_name,
P_CONTENT_TYPE: req.payload.content_type,
P_PROJECT_NUMBER: req.payload.project_number,
OP_GRID_TAB_TYP: { dir: server.app.db.BIND_OUT, type: server.app.db.ARRAY }
}
let res = server.methods.response();
try {
res.error = false;
res.msg = "Retrieved saved views.";
res.data = await connection.execute(
`BEGIN APPS.XXETA_GRID_USER_CONTEXT_PKG.EXTRACT_GRID_DETAILS(:P_USER_NAME, :P_CONTENT_TYPE, :P_PROJECT_NUMBER, :OP_GRID_TAB_TYP); END;`,
bindVars
);
} catch (err) {
server.app.logger.error(err.message);
res.error = true;
res.msg = err.message,
res.data = [];
}
return res;
}
存储过程描述为:
我从记录器得到的错误是:2018-08-06 15:02:20错误 NJS-012:在参数 2 中遇到无效的绑定数据类型
如有任何帮助,我们将不胜感激。
更新:
作为绑定变量的复杂类型如下所示...
CREATE OR REPLACE TYPE XXETA_GRID_CONTEXT_REC_TYP AS OBJECT
(
GRID_VIEW_ID NUMBER (15),
GRID_VIEW_NAME VARCHAR2 (240),
USER_NAME VARCHAR2 (30),
PROJECT_NUMBER VARCHAR2 (5)
)
2019/08/28 更新:
Node-oracledb 在 v4(2019/07/25 发布)中添加了对 SQL 对象类型和 PL/SQL 记录类型的支持。有关详细信息,请参阅文档的这一部分:
https://oracle.github.io/node-oracledb/doc/api.html#objects
鉴于与之前列出的完全相同的对象,现在可以使用以下 JavaScript 以比以前少得多的代码行来完成工作:
const oracledb = require('oracledb');
const config = require('./db-config.js');
async function runTest() {
let conn;
try {
const sql =
`call xxeta_grid_user_context_pkg.extract_grid_details(
p_user_name => :P_USER_NAME,
p_content_type => :P_CONTENT_TYPE,
p_project_number => :P_PROJECT_NUMBER,
op_grid_tab_typ => :OP_GRID_TAB_TYP
)`;
const binds = {
P_USER_NAME: 'Jane Doe',
P_CONTENT_TYPE: 'Some Content Type',
P_PROJECT_NUMBER: '123',
OP_GRID_TAB_TYP: {
dir: oracledb.BIND_OUT,
type: 'HR.XXETA_GRID_CONTEXT_TAB_TYP'
}
}
conn = await oracledb.getConnection(config);
const result = await conn.execute(
sql,
binds
);
const gridContexts = [];
for (let x = 0; x < result.outBinds.OP_GRID_TAB_TYP.length; x += 1) {
gridContexts.push({
gridViewId: result.outBinds.OP_GRID_TAB_TYP[x].GRID_VIEW_ID,
gridViewName: result.outBinds.OP_GRID_TAB_TYP[x].GRID_VIEW_NAME,
userName: result.outBinds.OP_GRID_TAB_TYP[x].USER_NAME,
projectNumber: result.outBinds.OP_GRID_TAB_TYP[x].PROJECT_NUMBER
});
}
console.log(gridContexts);
} catch (err) {
console.error(err);
} finally {
if (conn) {
try {
await conn.close();
} catch (err) {
console.error(err);
}
}
}
}
runTest();
上一个回答:
目前不支持复杂类型。您指定的 out bind 属于此类。在直接支持此类类型之前,您需要添加一些包装代码以将复杂类型分解为一种或多种简单类型。我在这里展示了一个例子:
https://jsao.io/2017/01/plsql-record-types-and-the-node-js-driver/
post 的目标是调用接受自定义记录类型数组的存储过程。要调用它,我首先必须声明一些要绑定到的简单数组类型。然后我可以使用这些数组创建更复杂的数组并调用过程。
对于您的情况,您需要执行相反的操作。在 PL/SQL 块中,声明一个 APPS.XXETA_GRID_CONTEXT_TAB_TYP 类型的局部变量。然后,在调用该过程后,遍历数组并使用它来填充一些简单的数组(VARCHAR2、NUMBER 或 DATE)并将它们用作您的输出绑定。
更新:
前提是你有以下对象:
create or replace type xxeta_grid_context_rec_typ as object (
grid_view_id number(15),
grid_view_name varchar2(240),
user_name varchar2(30),
project_number varchar2(5)
)
/
create or replace type xxeta_grid_context_tab_typ as table of xxeta_grid_context_rec_typ
/
create or replace package xxeta_grid_user_context_pkg
as
procedure extract_grid_details(
p_user_name in varchar2,
p_content_type in varchar2,
p_project_number in varchar2,
op_grid_tab_typ out xxeta_grid_context_tab_typ
);
end;
/
create or replace package body xxeta_grid_user_context_pkg
as
procedure extract_grid_details(
p_user_name in varchar2,
p_content_type in varchar2,
p_project_number in varchar2,
op_grid_tab_typ out xxeta_grid_context_tab_typ
)
is
l_xxeta_grid_context_rec xxeta_grid_context_rec_typ;
begin
op_grid_tab_typ := xxeta_grid_context_tab_typ();
for x in 1 .. 3
loop
l_xxeta_grid_context_rec := xxeta_grid_context_rec_typ(
grid_view_id => x,
grid_view_name => 'Some Grid View',
user_name => p_user_name,
project_number => p_project_number
);
op_grid_tab_typ.extend();
op_grid_tab_typ(x) := l_xxeta_grid_context_rec;
end loop;
end;
end;
/
以下 Node.js 代码可以调用存储过程并从复杂的输出参数中获取值。
const oracledb = require('oracledb');
const config = require('./dbConfig.js');
async function runTest() {
let conn;
try {
const userName = 'Jane Doe';
const contentType = 'Some Content Type';
const projectNumber = '123';
// This is what we want to populate with records/objects that come out
// of the procedure.
const gridContexts = [];
// We start by declaring some other arrays, one for each field in the
// xxeta_grid_context_rec_typ type.
const gridViewIds = [];
const gridViewNames = [];
const userNames = [];
const projectNumbers = [];
conn = await oracledb.getConnection(config);
// Then we execute the procedure with a little wrapper code to populate
// the individual arrays.
let result = await conn.execute(
`declare
-- This is a local variable that you'll use to get the out data from
-- the procedure.
l_xxeta_grid_context_tab xxeta_grid_context_tab_typ;
begin
xxeta_grid_user_context_pkg.extract_grid_details(
p_user_name => :user_name,
p_content_type => :content_type,
p_project_number => :project_number,
op_grid_tab_typ => l_xxeta_grid_context_tab
);
-- Now that the local variable is populated, iterate over it to
-- populate the individual out binds.
for x in 1 .. l_xxeta_grid_context_tab.count
loop
:grid_view_ids(x) := l_xxeta_grid_context_tab(x).grid_view_id;
:grid_view_names(x) := l_xxeta_grid_context_tab(x).grid_view_name;
:user_names(x) := l_xxeta_grid_context_tab(x).user_name;
:project_numbers(x) := l_xxeta_grid_context_tab(x).project_number;
end loop;
end;`,
{
user_name: userName,
content_type: contentType,
project_number: projectNumber,
grid_view_ids: {
dir: oracledb.BIND_OUT,
type: oracledb.NUMBER,
maxArraySize: 200
},
grid_view_names: {
dir: oracledb.BIND_OUT,
type: oracledb.STRING,
maxArraySize: 200
},
user_names: {
dir: oracledb.BIND_OUT,
type: oracledb.STRING,
maxArraySize: 200
},
project_numbers: {
dir: oracledb.BIND_OUT,
type: oracledb.STRING,
maxArraySize: 200
}
}
);
// At this point you can access the individual arrays to populate the
// original target array with objects. This is optional, you can work
// with the individual arrays directly as well.
for (let x = 0; x < result.outBinds.grid_view_ids.length; x += 1) {
gridContexts.push({
gridViewId: result.outBinds.grid_view_ids[x],
gridViewName: result.outBinds.grid_view_names[x],
userName: result.outBinds.user_names[x],
projectNumber: result.outBinds.project_numbers[x]
});
}
console.log(gridContexts);
} catch (err) {
console.error(err);
} finally {
if (conn) {
try {
await conn.close();
} catch (err) {
console.error(err);
}
}
}
}
runTest();
希望对您有所帮助!对复杂类型的直接支持在增强列表中,只是不能说什么时候登陆。
我是 运行 节点 8.9.4、Hapi 17.4 和 Oracledb 2.2。
尝试调用存储过程时,出现错误 "NJS-012: encountered invalid bind data type in parameter 2"。我所做的一切似乎都无法解决问题。调用过程的代码是:
async function getSavedViews(req, h, server) {
let connection = await server.app.db.getConnection();
let bindVars = {
P_USER_NAME: req.payload.user_name,
P_CONTENT_TYPE: req.payload.content_type,
P_PROJECT_NUMBER: req.payload.project_number,
OP_GRID_TAB_TYP: { dir: server.app.db.BIND_OUT, type: server.app.db.ARRAY }
}
let res = server.methods.response();
try {
res.error = false;
res.msg = "Retrieved saved views.";
res.data = await connection.execute(
`BEGIN APPS.XXETA_GRID_USER_CONTEXT_PKG.EXTRACT_GRID_DETAILS(:P_USER_NAME, :P_CONTENT_TYPE, :P_PROJECT_NUMBER, :OP_GRID_TAB_TYP); END;`,
bindVars
);
} catch (err) {
server.app.logger.error(err.message);
res.error = true;
res.msg = err.message,
res.data = [];
}
return res;
}
存储过程描述为:
我从记录器得到的错误是:2018-08-06 15:02:20错误 NJS-012:在参数 2 中遇到无效的绑定数据类型
如有任何帮助,我们将不胜感激。
更新:
作为绑定变量的复杂类型如下所示...
CREATE OR REPLACE TYPE XXETA_GRID_CONTEXT_REC_TYP AS OBJECT
(
GRID_VIEW_ID NUMBER (15),
GRID_VIEW_NAME VARCHAR2 (240),
USER_NAME VARCHAR2 (30),
PROJECT_NUMBER VARCHAR2 (5)
)
2019/08/28 更新:
Node-oracledb 在 v4(2019/07/25 发布)中添加了对 SQL 对象类型和 PL/SQL 记录类型的支持。有关详细信息,请参阅文档的这一部分: https://oracle.github.io/node-oracledb/doc/api.html#objects
鉴于与之前列出的完全相同的对象,现在可以使用以下 JavaScript 以比以前少得多的代码行来完成工作:
const oracledb = require('oracledb');
const config = require('./db-config.js');
async function runTest() {
let conn;
try {
const sql =
`call xxeta_grid_user_context_pkg.extract_grid_details(
p_user_name => :P_USER_NAME,
p_content_type => :P_CONTENT_TYPE,
p_project_number => :P_PROJECT_NUMBER,
op_grid_tab_typ => :OP_GRID_TAB_TYP
)`;
const binds = {
P_USER_NAME: 'Jane Doe',
P_CONTENT_TYPE: 'Some Content Type',
P_PROJECT_NUMBER: '123',
OP_GRID_TAB_TYP: {
dir: oracledb.BIND_OUT,
type: 'HR.XXETA_GRID_CONTEXT_TAB_TYP'
}
}
conn = await oracledb.getConnection(config);
const result = await conn.execute(
sql,
binds
);
const gridContexts = [];
for (let x = 0; x < result.outBinds.OP_GRID_TAB_TYP.length; x += 1) {
gridContexts.push({
gridViewId: result.outBinds.OP_GRID_TAB_TYP[x].GRID_VIEW_ID,
gridViewName: result.outBinds.OP_GRID_TAB_TYP[x].GRID_VIEW_NAME,
userName: result.outBinds.OP_GRID_TAB_TYP[x].USER_NAME,
projectNumber: result.outBinds.OP_GRID_TAB_TYP[x].PROJECT_NUMBER
});
}
console.log(gridContexts);
} catch (err) {
console.error(err);
} finally {
if (conn) {
try {
await conn.close();
} catch (err) {
console.error(err);
}
}
}
}
runTest();
上一个回答:
目前不支持复杂类型。您指定的 out bind 属于此类。在直接支持此类类型之前,您需要添加一些包装代码以将复杂类型分解为一种或多种简单类型。我在这里展示了一个例子: https://jsao.io/2017/01/plsql-record-types-and-the-node-js-driver/
post 的目标是调用接受自定义记录类型数组的存储过程。要调用它,我首先必须声明一些要绑定到的简单数组类型。然后我可以使用这些数组创建更复杂的数组并调用过程。
对于您的情况,您需要执行相反的操作。在 PL/SQL 块中,声明一个 APPS.XXETA_GRID_CONTEXT_TAB_TYP 类型的局部变量。然后,在调用该过程后,遍历数组并使用它来填充一些简单的数组(VARCHAR2、NUMBER 或 DATE)并将它们用作您的输出绑定。
更新:
前提是你有以下对象:
create or replace type xxeta_grid_context_rec_typ as object (
grid_view_id number(15),
grid_view_name varchar2(240),
user_name varchar2(30),
project_number varchar2(5)
)
/
create or replace type xxeta_grid_context_tab_typ as table of xxeta_grid_context_rec_typ
/
create or replace package xxeta_grid_user_context_pkg
as
procedure extract_grid_details(
p_user_name in varchar2,
p_content_type in varchar2,
p_project_number in varchar2,
op_grid_tab_typ out xxeta_grid_context_tab_typ
);
end;
/
create or replace package body xxeta_grid_user_context_pkg
as
procedure extract_grid_details(
p_user_name in varchar2,
p_content_type in varchar2,
p_project_number in varchar2,
op_grid_tab_typ out xxeta_grid_context_tab_typ
)
is
l_xxeta_grid_context_rec xxeta_grid_context_rec_typ;
begin
op_grid_tab_typ := xxeta_grid_context_tab_typ();
for x in 1 .. 3
loop
l_xxeta_grid_context_rec := xxeta_grid_context_rec_typ(
grid_view_id => x,
grid_view_name => 'Some Grid View',
user_name => p_user_name,
project_number => p_project_number
);
op_grid_tab_typ.extend();
op_grid_tab_typ(x) := l_xxeta_grid_context_rec;
end loop;
end;
end;
/
以下 Node.js 代码可以调用存储过程并从复杂的输出参数中获取值。
const oracledb = require('oracledb');
const config = require('./dbConfig.js');
async function runTest() {
let conn;
try {
const userName = 'Jane Doe';
const contentType = 'Some Content Type';
const projectNumber = '123';
// This is what we want to populate with records/objects that come out
// of the procedure.
const gridContexts = [];
// We start by declaring some other arrays, one for each field in the
// xxeta_grid_context_rec_typ type.
const gridViewIds = [];
const gridViewNames = [];
const userNames = [];
const projectNumbers = [];
conn = await oracledb.getConnection(config);
// Then we execute the procedure with a little wrapper code to populate
// the individual arrays.
let result = await conn.execute(
`declare
-- This is a local variable that you'll use to get the out data from
-- the procedure.
l_xxeta_grid_context_tab xxeta_grid_context_tab_typ;
begin
xxeta_grid_user_context_pkg.extract_grid_details(
p_user_name => :user_name,
p_content_type => :content_type,
p_project_number => :project_number,
op_grid_tab_typ => l_xxeta_grid_context_tab
);
-- Now that the local variable is populated, iterate over it to
-- populate the individual out binds.
for x in 1 .. l_xxeta_grid_context_tab.count
loop
:grid_view_ids(x) := l_xxeta_grid_context_tab(x).grid_view_id;
:grid_view_names(x) := l_xxeta_grid_context_tab(x).grid_view_name;
:user_names(x) := l_xxeta_grid_context_tab(x).user_name;
:project_numbers(x) := l_xxeta_grid_context_tab(x).project_number;
end loop;
end;`,
{
user_name: userName,
content_type: contentType,
project_number: projectNumber,
grid_view_ids: {
dir: oracledb.BIND_OUT,
type: oracledb.NUMBER,
maxArraySize: 200
},
grid_view_names: {
dir: oracledb.BIND_OUT,
type: oracledb.STRING,
maxArraySize: 200
},
user_names: {
dir: oracledb.BIND_OUT,
type: oracledb.STRING,
maxArraySize: 200
},
project_numbers: {
dir: oracledb.BIND_OUT,
type: oracledb.STRING,
maxArraySize: 200
}
}
);
// At this point you can access the individual arrays to populate the
// original target array with objects. This is optional, you can work
// with the individual arrays directly as well.
for (let x = 0; x < result.outBinds.grid_view_ids.length; x += 1) {
gridContexts.push({
gridViewId: result.outBinds.grid_view_ids[x],
gridViewName: result.outBinds.grid_view_names[x],
userName: result.outBinds.user_names[x],
projectNumber: result.outBinds.project_numbers[x]
});
}
console.log(gridContexts);
} catch (err) {
console.error(err);
} finally {
if (conn) {
try {
await conn.close();
} catch (err) {
console.error(err);
}
}
}
}
runTest();
希望对您有所帮助!对复杂类型的直接支持在增强列表中,只是不能说什么时候登陆。