OCCI簡介
Oracle? C++ Call Interface (OCCI) 是一套應用程序編程接口,它允許C++程序與一個或者多個Oracle數(shù)據(jù)庫進行交互.OCCI給予你強大的數(shù)據(jù)庫操作能力,比如說執(zhí)行SQL,處理存儲過程等.
OCCI主要是提供給有以下需求的用戶:
- 希望借助系統(tǒng)內(nèi)存和網(wǎng)絡連接的合理使用,開發(fā)高性能的應用程序.
- 開發(fā)可伸縮的應用程序,以滿足數(shù)據(jù)庫用戶和請求的不斷增長.
- 支持訪問數(shù)據(jù)庫對象.
- 簡化用戶認證和密碼管理.
- 支持多層認證模式
- 兩層C/S環(huán)境或者多層環(huán)境下,動態(tài)連接管理和事務管理的一致性接口.
注: OCCI不支持Non-Blocking(非阻塞)模式.
由于OCCI是以動態(tài)庫方式導出的類庫,因此對平臺和編譯器的依賴性比較強,所以需要根據(jù)不同平臺,不同編譯器取得對應的OCCI接口,并且在自己的程序中鏈接OCCI的庫:
安裝OCCI
一般情況下OCCI會隨著oracle數(shù)據(jù)安裝,附帶安裝到某個目錄下,不過對于僅僅需要做相關開發(fā)的人員來說,這個動作”太大了”,選擇安裝即時客戶端是一個不錯的選擇,小巧,便于配置,功能完善(對于應用程序開發(fā)來說).
我們需要到oracle的網(wǎng)站上下載以下幾個東西:
- instantclient-basic (一些基礎組件)
- instantclient-sdk (開發(fā)包,即相關的頭文件和庫文件)
- occi (能正確匹配你的開發(fā)環(huán)境的OCCI庫)
雖然instantclient-sdk里已經(jīng)有了lib和頭文件,但occi仍然需要單獨下載,因為
instantclient-sdk 只有發(fā)行版本的lib和動態(tài)庫,對于開發(fā)和調(diào)試來說是不夠的.
instantclient-sdk 中的動態(tài)庫不能直觀的判斷其CRT庫的依賴版本
有一個地方還需要注意,OCCI版本并不是很全,因為它有太多的東西需要對號入座,如開發(fā)者的編譯器類型,編譯器版本,操作系統(tǒng)(x86,x64),Oracle客戶端(包含即使客戶端)版本.所以盡管OCCI有不少的發(fā)行版本,但輪到你具體選擇的時候就不多了,比如說你想找支持VC2008的OCCI,你就只能選Oracle11的客戶端了,因為Oracle只發(fā)行了這樣一個版本同支持VC2008.
回到安裝的話題上,假設你用windows +VC2008,你需要下載 instantclient-basic-win32-11.1.0.6.0.zip,instantclient-sdk-win32-11.1.0.6.0.zip,occivc9win32_111060.zip.為什么要選11.0.6這個版本?因為我們的開發(fā)環(huán)境決定了只能找到11.1.6.0這個版本的OCCI庫.
- 先將下載到的instantclient-basic 解壓到C:\ora_env.那么它下面應該有sdk;vc8;vc8;vc71這4個文件夾.
- 將instantclient-sdk-win32-11.1.0.6.0.zip中的sdk目錄放在C:\ora_env下.
- 在C:\ora_env下新建文件夾vc9,將occivc9win32_111060.zip中的4個文件(oraocci11.dll,oraocci11d.dll,oraocci11.dll.manifest,oraocci11d.dll.manifest)放在里面.
- 在C:\ora_env\sdk\lib\msvc下新建目錄vc9,將occivc9win32_111060.zip中的 oraocci11.lib,oraocci11d.lib放在這個目錄里.
- 將C:\ora_env\vc9;C:\ora_env;加入PATH環(huán)境變量(最好放在前面).
- 新建一個環(huán)境變量TNS_AMDIN,值為C:\ora_env\;tnsnames.ora文件應該放在這個目錄下.
- 新建一個環(huán)境變量NLS_LANG,值為SIMPLIFIED CHINESE_CHINA.ZHS16GBK(這個值根據(jù)自己的情況定).
- 刪除C:\ora_env下的oraocci11.dll和oraocci11.sym,這個庫文件不是鏈接到VC2008的CRT,C:\ora_env\vc9才是正確的DLL,同時也能更明確表達我們的是使用VC2008開發(fā)環(huán)境的這樣一個情況.
- C:\ora_env\sdk\lib\msvc下的oraocci11.lib 也應該刪除,原理同上.
- 將C:\ora_env\sdk\lib\msvc\vc9加入VC2008庫文件搜索路徑.
- 將C:\ora_env\sdk\include加入VC2008頭文件搜索路徑.
linux下的配置過程基本一樣,但動態(tài)庫文件(.so)的路徑(對應于windows環(huán)境下的C:\ora_env\vc9)應該加入到LD_LIBRARY_PATH環(huán)境變量中.
開始編碼
配置工作做完后,可以寫一點代碼,測試一下

1 /*
2 * A simple OCCI test application
3 * This file contains the Employees class declaration
4 */
5
6 #include <occi.h>
7 #include <iostream>
8 #include <iomanip>
9
10 using namespace oracle::occi;
11 using namespace std;
12
13 class Employees {
14 public:
15 Employees();
16 virtual ~Employees();
17
18 void List();
19
20 private:
21 Environment *env;
22 Connection *con;
23
24 string user;
25 string passwd;
26 string db;
27 };
28
29 In Solution Explorer, right-click Source Files, select Add, select New Item…
30
31 In Add New Item, select C++ File (.cpp), enter Employees.cpp (or any name you prefer) in Name, and click Add.
32
33 Here's the content of the file on my system:
34
35 /*
36 * A simple OCCI test application
37 * This file contains the Employees class implementation
38 */
39
40 #include "Employees.h"
41
42 using namespace std;
43 using namespace oracle::occi;
44
45 int main (void)
46 {
47 /*
48 * create an instance of the Employees class,
49 * invoke the List member, delete the instance,
50 * and prompt to continue...
51 */
52
53 Employees *pEmployees = new Employees();
54
55 pEmployees->List();
56
57 delete pEmployees;
58
59 cout << "ENTER to continue...";
60
61 cin.get();
62
63 return 0;
64 }
65
66 Employees::Employees()
67 {
68 /*
69 * connect to the test database as the HR
70 * sample user and use the EZCONNECT method
71 * of specifying the connect string. Be sure
72 * to adjust for your environment! The format
73 * of the string is host:port/service_name
74 */
75
76 user = "hr";
77 passwd = "hr";
78 db = "oel01:1521/OEL11GR1.SAND";
79
80 env = Environment::createEnvironment(Environment::DEFAULT);
81
82 try
83 {
84 con = env->createConnection(user, passwd, db);
85 }
86 catch (SQLException& ex)
87 {
88 cout << ex.getMessage();
89
90 exit(EXIT_FAILURE);
91 }
92 }
93
94 Employees::~Employees()
95 {
96 env->terminateConnection (con);
97
98 Environment::terminateEnvironment (env);
99 }
100
101 void Employees::List()
102 {
103 /*
104 * simple test method to select data from
105 * the employees table and display the results
106 */
107
108 Statement *stmt = NULL;
109 ResultSet *rs = NULL;
110 string sql = "select employee_id, first_name, last_name " \
111 "from employees order by last_name, first_name";
112
113 try
114 {
115 stmt = con->createStatement(sql);
116 }
117 catch (SQLException& ex)
118 {
119 cout << ex.getMessage();
120 }
121
122 if (stmt)
123 {
124 try
125 {
126 stmt->setPrefetchRowCount(32);
127
128 rs = stmt->executeQuery();
129 }
130 catch (SQLException& ex)
131 {
132 cout << ex.getMessage();
133 }
134
135 if (rs)
136 {
137 cout << endl << setw(8) << left << "ID"
138 << setw(22) << left << "FIRST NAME"
139 << setw(27) << left << "LAST NAME"
140 << endl;
141 cout << setw(8) << left << "======"
142 << setw(22) << left << "===================="
143 << setw(27) << left << "========================="
144 << endl;
145
146 while (rs->next()) {
147 cout << setw(8) << left << rs->getString(1)
148 << setw(22) << left << (rs->isNull(2) ? "n/a" : rs->getString(2))
149 << setw(27) << left << rs->getString(3)
150 << endl;
151 }
152
153 cout << endl;
154
155 stmt->closeResultSet(rs);
156 }
157
158 con->terminateStatement(stmt);
159 }
160 }
代碼中的數(shù)據(jù)庫連接字符串跟一般我們常用的有點不同,連接字符串可以有下面兩種寫法:
- host:[port][/service name]
- (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp) (HOST=127.0.0.1) (PORT=5521))
(CONNECT_DATA=(SERVICE_NAME=SRV_NAME)))
第二種就是跟tnsnames.ora中的內(nèi)容一樣的了.
常見錯誤處理
分享一些在開發(fā)和測試的過程中,經(jīng)常會遇見一些常見的問題(win + VC).
編譯代碼時,發(fā)現(xiàn)大量錯誤,都是說某些CRT函數(shù)名稱重復.
原因中多半是同一個項目靜態(tài)鏈接了兩個不同版本的CRT庫.比如VC2005的程序A依賴VC2008的程序B(B使用OCCI),B的代碼生成方式為/MT或者/MTd.
將上面的場景中,程序B的代碼生成方式改成/MD或者/MDd后,編譯通過,但是程序運行發(fā)生崩潰.
很可能是代碼中的某個對象產(chǎn)生和釋放分別調(diào)用了不同的CRT(一個是VC2005的,一個是VC2008的).解決辦法:沒有什么好的解決辦法,最好還是統(tǒng)一開發(fā)環(huán)境吧,C++導出的類不適合跨環(huán)境.
出現(xiàn)ORA-24960錯誤
你的項目是debug版本,但鏈接了release版本的occi庫.
出現(xiàn)R6034錯誤
OCCI庫跟你的VC版本不一致.
調(diào)試時,發(fā)現(xiàn)堆棧被莫名其妙的破壞
同上,或者OCCI的debug與release版本跟當前項目的設置不匹配.
找不到MSVCR90.DLL
記得讓你的客戶安裝VC9的redist.
找不到MSVCR90D.DLL
你發(fā)布的程序是調(diào)試版本的,雖然你的用戶安裝了VC9的redist,那也是沒用的,因為redist只會安裝release版本的運行時庫.