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 抱怨

springMVC4(12)复杂对象和集合类型入参绑定

阶段3 3.SpringMVC·_02.参数绑定及自定义类型转换_2 请求参数绑定实体类型

阶段3 3.SpringMVC·_02.参数绑定及自定义类型转换_4 请求参数绑定集合类型

php如何输出object类型

模型绑定