OraLib - 一个轻量级的 C++ Oracle OCI 库封装






4.48/5 (14投票s)
2002年3月6日
6分钟阅读

560522

3670
6 个类,提供了一个轻量级的 C++ Oracle OCI 库封装
- 下载源代码和测试项目 - 32 Kb
- 最新源代码和一些文档也在这里 提供。
本页内容
简介
本页将介绍 OraLib - 一个轻量级的 C++ Oracle OCI 库封装 - 的一些基本用例。我的目的是创建一个易于使用但功能齐全的 C++ 库。
关于资源所有权说明:几乎所有类都有一个 release 方法,并且预计用户代码在不再需要对象实例时会调用该方法。有些情况下,release 方法体是空的,但这在未来可能会改变。下面的所有代码示例中都不会调用 release 方法。
先决条件:基本的 C++、SQL、PL/SQL 知识。
为了运行示例,您还需要 Microsoft 操作系统 (Windows 9x, NT 或 2000)、Microsoft Visual C++ 编译器 6.0、STLport 4.0.x 或 4.5.x、Oracle 8.1.6 及以上版本。我猜想可以在 Linux/Unix 上运行该库,但我目前不知道(也没有耐心和时间)如何去做。
什么是 OraLib?OraLib 是一个 C++ 库,是 Oracle OCI 库的封装。OraLib 可以让您避免进行低级函数 (API) 调用,并避免了您本应掌握的低级知识。使用 OraLib,您可以轻松完成以下操作:
- 连接/断开 Oracle 服务器;
- 执行 DDL 命令;
- 执行 SQL select/insert/update/delete 命令;
- 处理事务;
- 调用包/存储过程或执行匿名 PL/SQL 块;
- 处理(命名)绑定变量;
- 通过使用 C++ 异常来使用内置的错误处理。
以下主题将向您展示 OraLib 的各种用例。
连接处理
连接到 Oracle 服务器有两种方式:将服务器名称、登录名和密码作为参数传递给 connection 对象的构造函数,或者创建一个空对象实例然后稍后调用 open 方法。无论哪种情况,如果连接失败,都会抛出 error。
oralib::connection cn ("MyOracleServer", "MyLogin", "MyPassword"); ... cn.close (); // or oralib::connection cn (); cn.open ("MyOracleServer", "MyLogin", "MyPassword"); ... cn.close ();
可以通过调用 close 方法显式关闭连接,也可以在对象被删除或超出作用域时隐式关闭。第一种情况下,对象实例可以被重用,以便稍后连接到相同或另一个 Oracle 实例。
执行命令
数据定义语言 (DDL)
执行 DDL 命令是最简单的情况。可以直接调用 connection 对象的 execute 方法,并传入要执行的 SQL 字符串。
cn.execute ("create table a (id numeric, name varchar2 (10))"); ... cn.execute ("drop table a");
存储数据
将数据存储到 Oracle 数据库的最简单方法是使用 SQL insert 语句。更复杂的情况是调用存储过程,但通常有两种情况:(1)要存储的数据可能在 SQL 语句中 - 作为文本 - 并且,(2)要存储的数据可以通过绑定变量(或参数)传递。这两种方法都有优缺点。第一种情况下,您应该构建一个包含 SQL insert 语句(或存储过程名称)以及格式化好的数据值的文本字符串 - 例如通过调用 printf。第二种方法要求 SQL insert 语句仅包含绑定变量名(否则它将始终是常量),并且手动绑定命名变量并设置其值,而无需担心格式化。以下是两者的示例:
// common for both long id_column; TCHAR *name_column; // 1st approach char sql [100]; sprintf (sql, "insert into a (id, name) values (%d, '%s')", id_column, name_column); cn.execute (sql); // 2nd approach statement &st = *cn.prepare ("insert into a values (:n, :s)"); st.bind (":n") = id_column; st.bind (":s") = name_column; st.execute (); st.release ();
第二种方法更好,因为:(1)可以使用 Unicode 文本数据 - OCI 要求 SQL 语句是 ANSI 字符串,而 Unicode 文本不适合 ANSI 字符集;
wchar_t *name_in_unicode; // initialized somewhere below... ... statement &st = cn.prepare ("insert into a (name) values (:s)"); st.bind (":s") = name_in_unicode; st.execute ();
(2)可以按顺序执行多个 SQL insert 语句(其中只有插入值不同)(这比第一种方法快得多)。
statement &st = *cn.prepare ("insert into a (id) values (:n)"); parameter &p = st.bind (":n"); for (long i=0; i<1000; i++) { p = i; st.execute (); }
事务
通常在 Oracle 中,第一个与数据相关的 SQL 语句会创建一个隐式事务。例如,“insert into a (id) values (1)” 创建的事务应该被显式关闭(提交或回滚),否则当连接关闭时它会被隐式关闭。在事务关闭之前,所做的更改仅在此连接内部可见,并且在某些情况下其他连接可能会被阻塞。
connection 对象提供了两个用于事务处理的方法:commit 和 rollback。如果您阅读源代码,会发现它们只不过是 connection.execute 的简单调用。无论如何,您应该尽快考虑关闭事务,因为可能会发生冲突 - 通过调用 connection.commit / connection.rollback 中的一个,或者通过在存储过程中包含 commit / rollback。
检索数据
检索数据时有两个选项。选择取决于您希望检索多少数据。当所需数据是标志或计数时,可以使用命名变量。但是,如果您需要获取数据行,则应使用游标(resultset)。
命名变量用于检索数据的使用方式与其用于存储数据的方式类似。
statement &st = *cn.prepare ( "begin select count (id) into :n from a; end;"); st.bind (":n"); st.execute (); num_rows = st [":n"];
这种方法适用于您希望同时使用相同的命名变量进行输入和输出的情况。
要从显式 SQL select 语句中获取数据,请调用 connection.select 或 statement.select,具体取决于您是否需要提供某些输入数据(例如 select 条件)。
// connection.select case resultset &rs = *cn.select ("select name from a"); if (!rs.eod ()) do cout << (Pstr) rs ["NAME"] << '\n'; while (++rs); rs.release (); // statement.select case statement &st = *cn.prepare ("select id, name from a where id = :n"); st.bind (":n") = id_required; resultset &rs1 = *st.select (); cout << '#' << (long) rs1 ["ID"] << ' ' << rs1 [2].as_string (); rs1.release ();
当执行 SQL select 语句并返回 resultset 对象时,可以通过两种方式访问列:(1)按名称(区分大小写)和(2)按索引(索引是 0-based 还是 1-based 在 oralib.h 中配置)。
如果您需要执行多个 SQL select 语句,则应使用游标绑定变量(在下一节中介绍)。
执行 PL/SQL 块
Oracle 数据库的一个强大功能是 PL/SQL。通过使用 OraLib,您可以执行 PL/SQL 块,传递输入参数并接收输出参数。输出参数甚至是 resultset(在 Oracle 文档中是 cursor)。下面的示例将执行两个 SQL select 语句,并通过使用命名游标变量来获取行。
statement &st = *cn.prepare ( "begin\n" " open :c1 for select id, name from a;\n" " open :c2 for select * from a;\n" "end;"); st.bind (":c1"); st.bind (":c2"); st.execute (); resultset &rs = st [":c1"]; // id and name columns column &id_column = st [":c1"].as_resultset () ["ID"]; column &name_column = rs ["NAME"]; if (!rs.eod ()) do cout << '#' << (long) id_column << ' ' << (Pstr) name_column << '\n'; while (++rs); name_column.release (); id_column.release (); rs.release (); ...
resultset 列可以通过每次需要列的值时向 resultset 查询来访问,或者将其缓存到 column 对象中。第二种方法当然更快,但由于使用了晚期绑定,因此必须先执行语句。
用例示例
插入表行并检索序列值
Oracle 使用 sequence 的概念来允许同时向单个表进行 insert(Microsoft SQL Server 使用 autonumber 列)。由于几乎所有现代系统都由多个用户同时使用,“select max (id) from a_table” 的方式绝对是错误的。但实际上检索新创建行的 id 列很容易。
statement &st = *cn.prepare ( "begin\n" " insert into a (id, name) values (a_seq.nextval, :s);\n" " :n := a_seq.currval;\n" " commit;\n" "end;"); st.bind (":s") = name_column; st.bind (":n"); st.execute (); cout << "newly created row's id = " << (long) st [":n"]; st.release ();
当然,这应该放在一个存储过程中。
检索特定表行
statement &st = *cn.prepare ( "select col1, col2, col3 from table_name where id = :n"); st.bind (":n") = id_we_re_looking_for; resultset &rs = *st.select (); ... rs.release (); st.release ();
调用存储过程
statement &st = *cn.prepare ( "begin sp_name (:param1, :param2, :param3); end;"); st.bind (":param1", DT_TYPE) = param1_value; st.bind (":param2", DT_TYPE) = param2_value; st.bind (":param3", DT_TYPE) = param3_value; st.execute (); ... st.release ();
调用包中的函数
statement &st = *cn.prepare ( "begin :result := package_name.function_name (" ":param1, :param2, :param3); end;"); st.bind (":param1", DT_TYPE) = param1_value; st.bind (":param2", DT_TYPE) = param2_value; st.bind (":param3", DT_TYPE) = param3_value; st.bind (":result", DT_TYPE); st.execute (); // use st [":result"] here ... st.release ();
替代方案
OraLib 只包含少数几个类(确切地说 6 个),但支持 OCI 库提供的许多强大功能:命名变量、输出游标、PL/SQL 块执行。该库随完整源代码分发。
OCI 需要您编写 50 行格式化的代码才能开始连接 Oracle 服务器。更不用说执行 select 语句并获取结果或绑定命名变量这些“简单”的事情了。OO4O for C++ 只是一个 COM 封装。OCCI 随较新版本的 Oracle(9 及以上)一起提供,但看起来源代码是不可用的。
还有其他类似的可用项目 - 在 这里 可以找到一个更长的列表。
最后的几句话
希望您喜欢它。欢迎评论、反馈和请求。
免责声明
本软件不附带任何保证。请自行承担使用风险。
请在您的项目中包含库名称和我的电子邮件。并通知我。