OCI:如何绑定对象类型的输出参数
Posted
技术标签:
【中文标题】OCI:如何绑定对象类型的输出参数【英文标题】:OCI: How to bind output parameter of object type 【发布时间】:2015-01-29 16:36:15 【问题描述】:我正在尝试从基于 OCI 的客户端调用存储过程(在 oracle db 11g 上)。该过程包含单个对象类型的 OUT 参数。
问题:我总是收到“ORA-21525:属性号或(索引处的集合元素)%s 违反了它的约束”错误。
如果有人能给我提示可能是什么原因,我将不胜感激。
注意:有趣的是,在以下情况下一切正常:
我将返回类型从对象类型替换为此类对象的嵌套表。 我将返回类型替换为一些原始类型,例如NUMBER 我将这个参数设置为方向 IN 并以同样的方式绑定它。另外,我发现了以下几点:
无论我是从过程中将结果作为 OUT 参数返回还是用户从 FUNCTION 中返回,都会出现相同的错误。 如果我尝试通过 PLSQL 脚本调用存储过程,一切都会按预期进行,没有错误。 我还尝试创建由两个 OCINumber 字段组成的“并行”C++ 结构并使用它的对象而不是调用 OCIObjectNew(),但得到了同样的错误。 我也尝试设置pOutParam = NULL
,并绑定它,但是我得到“访问冲突,从位置00000读取”。
这里是代码。 PLSQL 对象类型:
CREATE OR REPLACE TYPE dl_fake_type AS OBJECT
(
attr_one NUMBER(12,0),
attr_two NUMBER(12,0)
);
/
过程(为了简洁,我跳过过程声明)
PROCEDURE dl_fake_fun(out_result OUT dl_fake_type)
IS
l_result dl_fake_type;
BEGIN
SELECT dl_fake_type(23, 35) INTO l_result FROM DUAL;
out_result := l_result;
END dl_fake_fun;
C++ OCI 代码(为简洁起见,没有连接初始化)。注意:我使用 MSVS2013 和一些 C++11 特性,比如 std::string::front() 方法。
// ...
typedef basic_string<OraText, char_traits<OraText>, allocator<OraText> > OraTextString;
typedef basic_ostringstream<OraText, char_traits<OraText>, allocator<OraText> > OraOStringStream;
// Check if ociStatus == OCI_SUCCESS. If not, then print error and assert.
void checkOciStatus(const sword ociStatus, OCIError * errorHandle = NULL);
// ...
const OraTextString DL_FAKE_TYPE = (OraText const *)"DL_FAKE_TYPE";
const OraTextString outParamName = (OraText const *)":out_param";
OraOStringStream query(ios::ate);
query << "BEGIN my_dummy_pkg.dl_fake_fun(" << outParamName << "); END;";
const OraTextString & queryString = query.str();
OCIStmt * statement;
const ub4 executionMode = OCI_DEFAULT;
checkOciStatus(OCIHandleAlloc(envhp, (void **)&statement, OCI_HTYPE_STMT, /* xtramemsz */ 0, /* usrmempp */ NULL), errhp);
checkOciStatus(OCIStmtPrepare(statement, errhp, queryString.c_str(), queryString.length(), OCI_NTV_SYNTAX, executionMode), errhp);
const OraTextString schemaName = (OraText const *)"MY_SCHEMA_NAME";
OCIType * typeDescriptor = NULL;
checkOciStatus(
OCITypeByName(
envhp,
errhp,
svchp,
schemaName.c_str(),
schemaName.size(),
DL_FAKE_TYPE.c_str(),
DL_FAKE_TYPE.length(),
/* version name */ NULL,
/* version name length */ 0,
OCI_DURATION_SESSION,
OCI_TYPEGET_HEADER,
&typeDescriptor
),
errhp
);
OCIBind* bindHandle = NULL;
checkOciStatus(OCIBindByName(statement, &bindHandle, errhp, outParamName.c_str(), outParamName.length(), NULL, 0, SQLT_NTY, NULL, NULL, NULL, 0, NULL, executionMode), errhp);
void * pOutParam = NULL;
checkOciStatus(OCIObjectNew(envhp, errhp, svchp, OCI_TYPECODE_REF, typeDescriptor, NULL, OCI_DURATION_DEFAULT, /* true = value, false = ref */ FALSE, &pOutParam), errhp);
checkOciStatus(OCIBindObject(bindHandle, errhp, typeDescriptor, &pOutParam, NULL, NULL, 0), errhp);
checkOciStatus(OCIStmtExecute(svchp, statement, errhp, 1, 0, NULL, NULL, executionMode), errhp);
cout << "executed." << endl;
// ...
提前致谢。
【问题讨论】:
【参考方案1】:经过一段时间的努力,我找到了对我有用的解决方案。
有两点对于成功绑定至关重要:
-
要绑定的对象必须通过调用
OCIObjectNew()
来创建,因为它是在随问题提供的代码中完成的。注意:我还考虑了直接创建表示相应 C++ 类型的结构实例的选项。但即使满足第二个条件,这也不起作用。
创建对象后,必须初始化其所有字段。 IE。 OCINumber
类型的字段必须设置为某个特定值(例如,OCINumberFromInt()
)并且指向OCIString
的指针必须设置为NULL
。
输出对象绑定的另一个问题(在绑定输出嵌套表时也观察到)是传递给 OCIBindObject
的第二个指向对象的指针是 second 指针,原因是:看起来,OCI 认为它作为指向输出对象的指针数组。这意味着,在调用OCIExecuteStatement
之前,包含第一个指针(指向第二个指针)的变量必须有效。这意味着,如果您想将绑定过程提取到单独的函数,则必须将 second 指针传递给该对象,然后将其传递给OCIBindObject()
。否则将无法正常工作。
我希望,这些提示将帮助人们避免在 OCI(和 Oracle DB)文档丛林中数小时的挫败感和痛苦挣扎。
【讨论】:
以上是关于OCI:如何绑定对象类型的输出参数的主要内容,如果未能解决你的问题,请参考以下文章
长度参数化的传递对象到类型绑定过程有 gfortran 抱怨
阶段3 3.SpringMVC·_02.参数绑定及自定义类型转换_2 请求参数绑定实体类型