OTL使用指南


 

1 OTL簡介

OTL 是 Oracle, Odbcand DB2-CLI Template Library 的縮寫,是一個C++編譯中操控關系數據庫的模板庫,它目前幾乎支持當前所有的各種主流數據庫,例如Oracle, MS SQL Server, Sybase, Informix, MySQL, DB2, Interbase /Firebird, PostgreSQL, SQLite, SAP/DB, TimesTen, MS ACCESS等等。

OTL中直接操作Oracle主要是通過Oracle提供的OCI接口進行,操作DB2數據庫則是通過CLI接口進行,至於MS的數據庫和其它一些數據庫,OTL只提供了ODBC的操作方式。當然Oracle和DB2也可以由OTL間接使用ODBC的方式進行操縱。

在MS Windows and Unix 平台下,OTL目前支持的數據庫版本主要有:Oracle 7 (直接使用 OCI7), Oracle 8 (直接使用 OCI8), Oracle 8i (直接使用OCI8i), Oracle 9i (直接使用OCI9i), Oracle 10g (直接使用OCI10g), DB2 (直接使用DB2 CLI), ODBC 3.x ,ODBC 2.5。目前OTL的最新版本為4.0,參見http://otl.sourceforge.net/,下載地址http://otl.sourceforge.net/otlv4_h.zip

2 編譯OTL

OTL是一個集成庫,它包含了一個模板流框架(template stream framework)以及適配OCI7, OCI8, OCI8i, OCI9i, OCI10g, ODBC 2.5, ODBC 3.x, DB2 CLI和Informix CLI的適配器(OTL-adapters)。編譯時需要使用相應的宏定義向編譯器指明底層數據庫API的類型。例如,如果底層使用ORACLE10g的API,則需要使用宏定義”#defineOTL_ORA10G”。

另外,也可以使用相應的宏定義控制編譯器對OTL的編譯。 例如,如果需要和ACE庫一起編譯可以使用宏定義”#defineOTL_ACE”, 如果需要OTL為所分配並處理的字符串以空字符結尾成為C風格字符串則可以使用宏定義”#define OTL_ADD_NULL_TERMINATOR_TO_STRING_SIZE”等。

所有的相關宏請參見http://otl.sourceforge.net/otl3_compile.htm

3 基本使用

OTL的一般使用步驟包括:

(1)   使用宏指明底層數據庫API類型和控制編譯器對OTL的編譯。例如:

    #define OTL_ORA9I      // Compile OTL 4.0/OCI9i

#define OTL_UNICODE    //Enable Unicode OTL for OCI9i

(2)   創建otl_connect對象,該對象一般為全局共享的。

(3)   調用otl_connect的靜態方法otl_initialize()初始化OTL環境。

(4)   調用otl_connect的rlogon()方法連接數據庫。

(5)   創建otl_stream()對象,該對象一般為局部的。

(6)   調用otl_stream的open()方法打開SQL進行解析。

(7)   使用otl_stream的<<操作符綁定SQL中的變量。

(8)   使用otl_stream的>>操作符讀取返回結果。

(9)   調用otl_connect的logoff()方法從數據庫斷開。

下面將通過一個較為全面的示例說明使用OTL連接數據庫、創建表和存儲過程、調用存儲過程、查詢記錄以及插入記錄、從數據庫斷開的具體代碼實現。

  1 #include <stdio.h>
  2 
  3 #include <string.h>
  4 
  5 #include <iostream>
  6 
  7 #include <vector>
  8 
  9 
 10 
 11 #define OTL_ORA9I       // Compile OTL 4.0/OCI9i
 12 
 13 //#define OTL_UNICODE   // Enable Unicode OTL for OCI9i
 14 
 15 #include "otlv4.h"          // include the OTL 4.0 header file
 16 
 17 
 18 
 19 using namespace std;
 20 
 21 
 22 
 23 /**
 24 
 25 *連接數據庫
 26 
 27 */
 28 
 29 int OTLConnect(const char* pszConnStr, otl_connect& db)
 30 
 31 {
 32 
 33     try
 34 
 35     {
 36 
 37         otl_connect::otl_initialize(); // initialize OCI environment
 38 
 39         db.rlogon(pszConnStr);
 40 
 41         db.auto_commit_off();
 42 
 43         printf("CONNECT: OK!\n");
 44 
 45     }
 46 
 47     catch (otl_exception& p)
 48 
 49     {   // intercept OTL exceptions
 50 
 51         printf("Connect Error: (%s) (%s) (%s)\n", p.msg, p.stm_text, p.var_info);
 52 
 53         return -1;
 54 
 55     }
 56 
 57     return 0;
 58 
 59 }
 60 
 61 
 62 
 63 /**
 64 
 65 *從數據庫斷開
 66 
 67 */
 68 
 69 int OTLDisconnect(otl_connect& db)
 70 
 71 {
 72 
 73     db.commit();
 74 
 75     db.logoff();
 76 
 77 
 78 
 79     printf("DISCONNECT: OK!\n");
 80 
 81     return 0;
 82 
 83 }
 84 
 85 
 86 
 87 /**
 88 
 89 *創建數據庫表和存儲過程
 90 
 91 */
 92 
 93 int OTLExec(otl_connect& db)
 94 
 95 {
 96 
 97     try
 98 
 99     {
100 
101         int nCnt = 0;
102 
103         char strSql[] = "SELECT count(0) FROM user_tables "
104 
105             " WHERE table_name = 'TEST_FTP' ";
106 
107 
108 
109         otl_stream otlCur(1, (const char*)strSql, db);
110 
111         otlCur >> nCnt;
112 
113 
114 
115         if (nCnt == 0)
116 
117         {
118 
119             char strDDL[] =
120 
121                 "create table TEST_FTP                 "
122 
123                 "(                                                                          "
124 
125                 "  AREA_ID         VARCHAR2(100) not null,     "
126 
127                 "  FTP_FILE_NAME   VARCHAR2(100) not null,    "
128 
129                 "  FTP_TIME        VARCHAR2(14),               "
130 
131                 "  FTP_BEGIN_TIME  VARCHAR2(14),                   "
132 
133                 "  FTP_END_TIME    VARCHAR2(14),                   "
134 
135                 "  FTP_MOD_TIME    date,                                 "
136 
137                 "  FTP_SIZE        NUMBER(8),                      "
138 
139                 "  FTP_SOURCE_PATH VARCHAR2(100),                 "
140 
141                 "  FTP_LOCAL_PATH  VARCHAR2(100),                "
142 
143                 "  FTP_RESULT      VARCHAR2(4),                       "
144 
145                 "  FTP_REDO        VARCHAR2(1)                       )";
146 
147 
148 
149             otl_cursor::direct_exec(db, (const char*)strDDL);
150 
151         }
152 
153 
154 
155         char strSqlProc[] = "SELECT count(0) from user_objects "
156 
157             " WHERE object_type = 'PROCEDURE' and object_name = 'PR_REMOVE_FTP' ";
158 
159         otl_stream otlCurProc(1, (const char*)strSqlProc, db);
160 
161         otlCurProc >> nCnt;
162 
163 
164 
165         if (nCnt == 0)
166 
167         {
168 
169             char strProc[] =
170 
171                 "CREATE OR REPLACE procedure pr_remove_ftp             "
172 
173                 "      ( area in varchar2, out_flag out varchar )      "
174 
175                 "AS                                                                                    "
176 
177                 "strtmp varchar2(32);                                                  "
178 
179                 "BEGIN                                                                                    "
180 
181                 "  strtmp := area||'%';                                                        "
182 
183                 "  DELETE FROM TEST_FTP where area_id LIKE strtmp;       "
184 
185                 "  out_flag := 'OK';                                                   "
186 
187                 "END;                                                                                ";
188 
189 
190 
191             otl_cursor::direct_exec(db, (const char*)strProc);
192 
193         }
194 
195 
196 
197     }
198 
199     catch (otl_exception& p)
200 
201     {   // intercept OTL exceptions
202 
203         printf("EXECUTE Error: (%s) (%s) (%s)\n", p.msg, p.stm_text, p.var_info);
204 
205     }
206 
207     return 0;
208 
209 }
210 
211 
212 
213 /**
214 
215 *調用存儲過程
216 
217 */
218 
219 int OTLProcedure(otl_connect& db)
220 
221 {
222 
223     try
224 
225     {
226 
227 
228 
229         char szData[64], szData1[64], szData2[64], szData3[64];
230 
231         int nSize = 0;
232 
233         char strSql[] = " BEGIN "
234 
235             "  pr_remove_ftp ( :area<char[100],in>, :out<char[100],out> ); "
236 
237             " END; ";
238 
239         otl_stream otlCur(1, (const char*)strSql, db);
240 
241         otlCur.set_commit(0);
242 
243 
244 
245         strcpy(szData, "AREA");
246 
247         memset(szData1, 0, sizeof(szData1));
248 
249         memset(szData2, 0, sizeof(szData2));
250 
251         memset(szData3, 0, sizeof(szData3));
252 
253 
254 
255         otlCur << szData;
256 
257         otlCur >> szData1;
258 
259 
260 
261         printf("PROCEDURE: %s!\n", szData1);
262 
263     }
264 
265     catch (otl_exception& p)
266 
267     { // intercept OTL exceptions
268 
269         printf("PROCEDURE Error: (%s) (%s) (%s)\n", p.msg, p.stm_text, p.var_info);
270 
271     }
272 
273     return 0;
274 
275 }
276 
277 
278 
279 /**
280 
281 *查詢記錄
282 
283 */
284 
285 int OTLSelect(otl_connect& db)
286 
287 {
288 
289     try
290 
291     {
292 
293         char szData[64], szData1[64], szData2[64], szData3[64], szRedo[2];
294 
295         int nSize;
296 
297         char strSql[] = " SELECT area_id, ftp_time, ftp_file_name, "
298 
299             " to_char(ftp_mod_time, 'YYYY-MM-DD HH24:MI:SS'), ftp_size "
300 
301             "  FROM TEST_FTP "
302 
303             " WHERE ftp_redo = :ftp_redo<char[2]>";
304 
305         otl_stream otlCur(1, (const char*)strSql, db);
306 
307 
308 
309         strcpy(szRedo, "Y");
310 
311         otlCur << szRedo;
312 
313         while (!otlCur.eof())
314 
315         {
316 
317             memset(szData, 0, sizeof(szData));
318 
319             otlCur >> szData;
320 
321             otlCur >> szData1;
322 
323             otlCur >> szData2;
324 
325             otlCur >> szData3;
326 
327             otlCur >> nSize;
328 
329             printf("SELECT: (%s %s %s %s %d)\n",
330 
331                 szData, szData1, szData2, szData3, nSize);
332 
333         }
334 
335     }
336 
337     catch (otl_exception& p)
338 
339     { // intercept OTL exceptions
340 
341         printf("Select Error: (%s) (%s) (%s)\n", p.msg, p.stm_text, p.var_info);
342 
343     }
344 
345     return 0;
346 
347 }
348 
349 
350 
351 /**
352 
353 *插入記錄
354 
355 */
356 
357 int OTLInsert(otl_connect& db)
358 
359 {
360 
361     try
362 
363     {
364 
365         char szData[64], szData1[64], szData2[9], szData3[64], szRedo[2];
366 
367         int nSize;
368 
369         char strSql[] = " INSERT into TEST_FTP "
370 
371             " ( area_id, ftp_file_name, ftp_time, ftp_mod_time, ftp_size, ftp_redo )"
372 
373             " VALUES ( :area_id<char[100]>, "
374 
375             "      :ftp_file_name<char[100]>, "
376 
377             "      to_char(sysdate,'YYYYMMDDHH24MISS'), "
378 
379             "   to_date(:ftp_mod_time<char[20]>,'YYYYMMDD'), "
380 
381             "      :ftp_size<int>, "
382 
383             "   :ftp_redo<char[2]> ) ";
384 
385         otl_stream otlCur(1, (const char*)strSql, db);
386 
387 
388 
389         otlCur.set_commit(0);
390 
391 
392 
393         for (int i = 1; i < 10; i++)
394 
395         {
396 
397             sprintf(szData, "AREA_%d", i);
398 
399             sprintf(szData1, "FILE_NAME_%d", i);
400 
401             if (i < 5)
402 
403             {
404 
405                 sprintf(szData2, "20070415");
406 
407                 strcpy(szRedo, "Y");
408 
409             }
410 
411             else
412 
413             {
414 
415                 sprintf(szData2, "20070416");
416 
417                 strcpy(szRedo, "N");
418 
419             }
420 
421 
422 
423             memset(szData3, 0, sizeof(szData3));
424 
425             nSize = i * 100;
426 
427 
428 
429             otlCur << szData << szData1 << szData2 << nSize << szRedo;
430 
431         }
432 
433 
434 
435         printf("INSERT: OK!\n");
436 
437     }
438 
439     catch (otl_exception& p)
440 
441     { // intercept OTL exceptions
442 
443         printf("INSERT Error: (%s) (%s) (%s)\n", p.msg, p.stm_text, p.var_info);
444 
445     }
446 
447     return 0;
448 
449 }
450 
451 
452 
453 /**
454 
455 *主函數
456 
457 */
458 
459 int main(int argc, char *argv[])
460 
461 {
462 
463     otl_connect db;
464 
465     char szConn[64];
466 
467 
468 
469 
470 
471     if (argc >= 2)
472 
473         strcpy(szConn, argv[1]);
474 
475     else
476 
477     {
478 
479         printf("otltest conn_str");
480 
481         return -1;
482 
483     }
484 
485 
486 
487     if (OTLConnect(szConn, db) < 0)
488 
489         return 0;
490 
491     OTLExec(db);
492 
493     OTLProcedure(db);
494 
495     OTLInsert(db);
496 
497     OTLSelect(db);
498 
499     OTLDisconnect(db);
500 
501 
502 
503     return 0;
504 
505 }

 

4 OTL流的概念

OTL設計者認為,任何SQL語句、PL/SQL塊或存儲過程調用都被輸入和輸出變量特征化。例如:

l       一個SELECT語句在其WHERE子句中擁有標量的輸入變量,而在其SELECT子句則定義了輸出的列,如果SELECT語句返回的是多行記錄則輸出列是個向量參數。

l       一個INSERT和UPDATE語句需要將數據寫入表中,它們擁有輸入參數。另外,一個DELETE語句由於需要指明刪除記錄的類型,同樣擁有輸入。工業強度的數據庫服務器通常也支持批量操作,例如批量的查詢、更新、刪除和插入,因此INSERT/UPDATE/DELETE語句的參數在批量操作的情況下也可能是向量。

l       一個存儲過程可能含有輸入和(或)輸出參數。通常存儲過程的參數是標量,但是也有特例,例如返回的是引用游標(Oracle)或者記錄集(MS SQL SERVER或者Sybase)。

l       一個PL/SQL塊可能含有輸入和(或)輸出參數,這些參數可能是標量也可能是向量。

 

圖4-1 OTL的流

因此,任何的SQL或者其程序上的擴展在交互過程中都可以如圖4-1所示看作擁有輸入和輸出的黑盒。OTL通過將數據流和SQL的概念聯合起來,用otl_stream類表達這種抽象。

由於SQL語句可能以批量的方式執行,otl_stream是一個緩沖流。它擁有兩個獨立的緩沖區:輸入和輸出。輸入緩沖區由所有集中到一起的輸入參數組成,輸出緩沖區則由所有集中到一起的輸出變量組成。

OTL流和C++的緩沖流很相似。一個SQL語句或存儲過程調用被當作一個普通的緩沖流被打開。OTL流的操作邏輯和C++流操作邏輯基本相同,但是OTL流的輸出和輸出緩沖區可能重疊。

OTL流擁有flush()方法在輸入緩沖區寫滿的時候將其自動刷新,也含有一系列的<<和>>運算符來讀和寫不同數據類型的對象。它最重要的優點是為任何類型的SQL語句和存儲過程調用提供了統一的接口。應用開發者能夠通過熟悉少量的語法和函數名稱像使用C++流一樣來使用OTL流。

在OTL流的內部擁有一個小型的解析器來解析所聲明的綁定變量以及綁定變量的數據類型。因此,免去了使用特殊的綁定函數來綁定已聲明的C/C++主機變量(hostvariables)。由於所有必須的緩沖區在OTL流中會自動創建,因此OTL僅僅需要被打開來進行讀和寫相應的數值。

OTL流接口要求使用OTL異常。OTL流操作都能可能拋擲otl_exception異常。因此為了攔截異常並阻止程序異常終止,必須使用try/catch塊來包裹OTL流的使用代碼。

OTL流的實現otl_stream具有較高的自動化功能,當OTL流的所有的輸入變量被定義好(也就是輸入緩沖區被填滿),它會觸發OTL流中的黑盒來執行。在黑盒執行的過程中輸出緩沖區被填充。在執行完成后,輸出緩沖區中的值能夠從OTL流中被讀取。如果執行的是一個SELECT語句並且返回多於輸出緩沖區大小的行,那么在輸出緩沖區的內容被讀取后,OTL會自動讀取下一批行記錄到輸出緩沖區。

5 主要類及方法說明

5-1 OTL主要類說明

類名

說明

otl_connect

負責創建和處理連接對象以及事務管理。

otl_stream

OTL流概念(參見第4小節)的具體實現。任何具有輸入輸出的SQL語句,匿名的PL/SQL塊或者存儲過程能夠使用otl_stream類進行C++編程。

一般傳統的數據庫API擁有綁定主機變量到SQL語句中占位符的函數。因此,開發者需要在程序中聲明host array,解析SQL語句,調用綁定函數,填充輸入變量,執行SQL語句,讀輸出變量等。這些操作結束后又繼續填充輸入變量,執行SQL語句,讀輸出變量。

以上的所有事情能夠在otl_stream中全部自動完成。otl_stream在保證性能的情況下提供了完全自動的與數據庫的交互。

otl_stream的性能主要被緩沖區大小arr_size一個參數控制。緩沖區大小定義了插入表的邏輯行以及與數據庫一次往反交互(one round-trip to the database)過程中從表或視圖中查詢的邏輯行。

otl_exception

可能代表數據庫錯誤也可能代表OTL自身的錯誤。OTL函數如果在使用底層的數據庫API時返回非0的錯誤碼,則會產生otl_exception類型的異常。

4.1otl_stream的主要方法

5-2 類otl_stream的主要方法說明

主要方法

說明

otl_stream(

const int arr_size,

     const char* sqlstm, 
     otl_connect& db,

     const char* ref_cur_placeholder=0,
     const char* sqlstm_label=0

);

構造函數,負責創建otl_stream對象並調用open()方法。

參數arr_size為流的緩沖區大小,

參數db為otl_connect連接對象的引用,

參數ref_cur_placeholder為reference cursor的占位符名稱,

參數sqlstm_label為SQL語句的標簽,用於取代異常描述消息otl_exception::stm中默認填充的SQL語句。

void open(
const int arr_size,

     const char* sqlstm, 
     otl_connect& db,

     const char* ref_cur_placeholder=0,
     const char* sqlstm_label=0

);

打開SQL語句對其進行解析,所有的輸入和輸出變量都在流內部別動態分配,並且自動綁定到占位符。

void close(void);

#ifdef OTL_STREAM_POOLING_ON
 void close(

constbool save_in_stream_pool=true

);
#endif

關閉流。如果使用了流緩沖池,則可以使用帶save_in_stream_pool參數的close()函數。如果參數save_in_stream_pool為true則流並不會真正關閉,而是被緩沖池回收。

int good(void);

測試流是否打開。

int eof(void);

測試是否所有的數據都已經從流中讀取。

void rewind(void);

重繞流。如果流不含有任何輸入變量,該方法將強制流執行SQL語句。

operator int(void);

流到int的轉換操作符,返回從!eof()獲得的流狀態。它允許運算符>>返回!EOF的流狀態並且能夠在while()循環中像下面的代碼那樣使用:

while(s>>f1>>f2) {
  cout<<”f1=”<<f1<<”, f2=”<<f2<<endl;

}

 int is_null(void);

判斷是否從流中獲得NULL

void flush(void);
void flush // OTL/OCI8,8i,9i,10g only 
(

const int row_offset=0,                             

const bool force_flush=false

);

刷新流的輸出緩沖區。這實際上意味着批量執行SQL語句,其執行的批量和流輸出緩沖區中已經填充的SQL數相等。當輸出緩沖區被填滿時,緩沖區將被自動刷新。如果流的auto_commit標志被置上,則在刷新完畢后當前事務被提交。

OTL/OCI8,8i,9i,10g擁有另外一個版本的flush()方法。能夠通過參數row_offset指定緩沖區刷新的開始位置,通過參數force_flush則能夠指定是否在出現錯誤拋出otl_exception的情況下仍然強制繼續刷新,忽略之前的錯誤。

long get_rpc(void);

獲得處理的行數(Rows Processed Count, RPC)。

void set_column_type(

const int column_ndx,
const int col_type,

const int col_size=0

);

設置SELECT輸出列的數據類型。

參數column_ndx為列的索引,如1,2,3…

參數col_type為OTL定義的數據類型常量(參見11.2小節)

參數col_size為新數據類型的大小,只被otl_var_char類型使用。

void set_commit(int auto_commit=0);

設置流的auto_commit標志。默認情況下,該標志被置上,即當輸出緩沖區刷新時,當前的事務被自動提交。

注意流的auto_commit標志和數據庫庫的自動提交模型沒有任何關系。

如果需要默認方式為不自動提交的流,則可以使用otl_stream的子類otl_nocommit_stream。

void set_flush(const bool auto_flush=true);

設置auto_flush標志。默認情況下auto_flush的值為true, 即如果緩沖區出現臟數據則在流的析構函數中刷新緩沖區。如果自動刷新標志被關閉,則需要使用close()方法或者flush()方法對流進行刷新。

注意該函數僅僅能夠設置流的析構函數中是否自動刷新,並不是通常意義上的緩沖區刷新。

int setBufSize(const int buf_size);     

設置流緩沖區的大小。

4.2 otl_connect的主要方法

4-3 類otl_connect的主要方法說明

主要方法

說明

static int otl_initialize(

const int threaded_mode=0

);

初始化OTL環境。需要在程序最開始連接數據庫之前調用一次。

參數threaded_mode指明程序是否運行在多線程環境,注意由於OTL並沒有使用同步鎖或者臨界段,線程安全並不能夠自動得到保證。

otl_connect(

const char* connect_str,

const int auto_commit=0

);

構造函數。

參數connect_str為連接字符串,OTL/OCIx風格的連接字符串為:

“USER/PASSWORD”(本地Oracle連接)

“USER/PASSWORD@TNS_ALLAS”(通過SQL*Net進行的遠程連接)

參數auto_commit指明是否每一個在連接中執行的SQL語句都會自動提交。如果需要自動提交則為1,默認情況下為0表示不需要自動提交。注意該auto_commit參數和otl_stream的自動提交沒有任何關系。

void rlogon(

const char* connect_str,

const int auto_commit=0

);

連接數據庫。參數同構造函數。

void logoff(void);

斷開數據庫連接。

static int otl_terminate(void);

終止Oracle 8i/9i的OCI環境。需要在程序最后的數據庫連接進行關閉后調用一次。該方法僅僅是OCI Terminate()調用的包裝。通常在多線程環境中,為了終止主線程的控制,該方法需要被調用使得進程能夠從OCI客戶端的共享內存中脫離以及做其他事情。

void cancel(void);// OTL/OCI8/8i/9i only

取消連接對象或者數據庫會話中的正在執行或者活動的操作或數據庫調用。

void commit(void);

#if defined(OTL_ORA10G_R2)
   void commit_nowait(void);
#endif

同步或異步的方式提交事務。

void rollback(void);

回滾事務。

void auto_commit_off(void);

void auto_commit_on(void);

設置otl_connect對象的auto_commit標志。

void set_stream_pool_size(

const int max_size

=otl_max_default_pool_size

);

如果使用了流緩沖池,則該方法重新分配被默認的流緩沖池和之前的set_stream_pool_size()調用分配的所有資源。

void set_character_set(

const int char_set=SQLCS_IMPLICIT

);

如果使用了UNICODE,則該方法設置默認或國家的字符集:

SQLCS_IMPLICIT為數據庫默認字符集。

SQLCS_NCHAR為數據庫國家的字符集。

otl_connect& operator<<(const char* str);

發送字符串到otl_connect對象。如果該otl_connect對象還沒有連接到數據庫則字符串為"userid/passwd@db"格式的連接字符串,它使得otl_connect對象能夠連接數據庫。如果該otl_connect對象已經連接到數據庫則字符串為靜態SQL語句,該語句被馬上執行。

otl_connect& operator<<=(const char* str);

發送字符串到otl_connect對象。otl_connect對象將保存該字符串並被下一個>>操作符使用。該字符串是一個擁有占位符並且能夠發送到otl_stream對象的SQL語句。

otl_connect& operator>>(otl_stream& s);

發送之前使用操作符<<=保存的SQL語句到otl_stream對象。它使得該SQL語句被otl_stream打開。

注意如果並沒有被>>=操作符保存的字符串,則字符串"*** INVALID COMMAND ***"被發送到otl_stream,最終會將導致解析錯誤,拋擲otl_exception異常。

long direct_exec(

const char *sqlstm,

int ignore_error

otl_exception::enabled 

);

直接執行靜態的SQL語句,返回處理的行數。

void syntax_check(

const char *sqlstm 

);

解析靜態的SQL語句,如果出現SQL錯誤將拋擲otl_exception異常。

void server_attach(const char* tnsname=0, 
 const char* xa_server_external_name=0,
 const char* xa_server_internal_name=0,
#if defined(OTL_ORA_OCI_ENV_CREATE)
 bool threaded_mode=false
#endif);

附加到Oralcle。

void server_detach(void);

從Oracle分離。

void session_begin (

const char* username,

const char* password,

const int auto_commit=0,      

const int session_mode=OCI_DEFAULT

);

開始Oracle8會話。

void session_end(void);

結束Oracle8會話。

6 SQL的變量綁定和常量SQL

6.1 SQL的變量綁定

OTL擁有一個小型的解析器,負責在流的內部動態的為SQL語句、PL/SQL 塊或存儲過程調用中聲明的綁定變量分配空間。OTL將Oracle傳統的使用命名符號作為占位符的變量綁定機制進行了擴展,增加了數據類型說明,例如:

INSERT INTO my_table2values(:employee_id<int>,:supervisor_name<char[32]>)

OTL占位符中的支持的數據類型如表6-1所示。

表6-1 OTL占位符中支持的數據類型

bigint

64-bit signed integer, for binding with BIGINT table columns (or stored procedure parameters) in MS SQL Server, DB2, MySQL, PostrgeSQL, etc. ODBC, and DB2 CLI support this kind bind variables natively, so does OTL.

OCIs do not have native support for 64-bit integers, so OTL has to emulate it via string (<char[XXX]>) bind variables internally and does string-to-bigint and bigint-to-string conversion.

blob

for Oracle 8/9; BLOB

char[length]

OTL 4.0.118 and higher:char(length)

null terminated string; length is database dependent; for Oracle in [3,32545]; for ODBC it depends on the database backend and the ODBC driver; for DB2-CLI >2.

In Unicode OTL, this type of bind variable declaration means a null terminated Unicode character string (two bytes per character). Thelength field of this declarator needs to include an extra byte / Unicode character, in order to accomodate the null  terminator itself (for example char[11] can be used in binding with a VARCHAR(9) column), unless #define OTL_ADD_NULL_TERMINATOR_TO_STRING_SIZE is enabled.

charz

Same as char[] for OTL_ORA7, OTL_ORA8, OTL_ORA8IOTL_ORA9IOTL_ORA10G. Should be used only when PL/SQL tables of type CHAR(XXX) are used.

charz is actually a workaround for the following Oracle error: PLS-00418: array bind type must match PL/SQL table row type.Normally, the internal OCI datatype that is used to bind VARCHAR2/CHAR table columns / scalar PL/SQL procedure parameters works fine, except for PL/SQL tables of CHAR(XXX). PL/SQL engine does not like what OTL tries to bind with a PL/SQL table of CHAR(XXX).charz[] should be used instead of char[] in cases like that.

clob

for Oracle 8/9: CLOB, NCLOB

db2date

for DB2 DATEs; should be used in the binding of a placeholder with a DB2 DATE column in case of both

db2time

for DB2 TIMEs; should be used in the binding of a placeholder with a DB2 TIME column in case of both OTL/DB2-CLI and OTL/ODBC for DB2; requiresotl_datetime as a data container. See example91 for more detail.

double

8-byte floating point number

float 

4-byte floating point number

int 

32-bit signed int

ltz_timestamp

Oracle 9i TIMESTAMP WITH LOCAL TIME ZONE, in a combination with #define OTL_ORA_TIMESTAMP, and otl_datetime

nchar[length]

Same as char[] + otl_connect::set_character_set(SQLCS_NCHAR) for Oracle 8i/9i/10g only, under #define OTL_UNICODE., or #define OTL_ORA_UTF8. nchar[] is required only when both VARCHAR2/CHAR and NVARCHAR2/NCHAR need to be declared  in the same SQL statement, or PL/SQL block.

nclob

Same as clob + otl_connect::set_character_set(SQLCS_NCHAR) for Oracle 8i/9i/10g only, under #define OTL_UNICODE, or #defineOTL_ORA_UTF8. nclob is required only when both CLOB and NCLOB need to be declared  in the same SQL statement, or PL/SQL block.

raw[length]

 

raw_long

 

short

short int (16-bit signed integer)

timestamp

MS SQL Server/Sybase DATETIME, DB2 TIMESTAMP, Oracle DATE, Oracle 9i TIMESTAMP (when #defineOTL_ORA_TIMESTAMP is enabled) ; it  requires TIMESTAMP_STRUCT (OTL/ODBC, OTL/DB2-CLI), orotl_datetime (ODBC, DB2-CLI, and OCIx).  OTL/DB2-CLI and OTL/ODBC for DB2; requiresotl_datetime as a data container. See example91 for more detail

tz_timestamp

Oracle 9i TIMESTAMP WITH TIME ZONE, in a combination with #define OTL_ORA_TIMESTAMP, and otl_datetime

unsigned

unsigned int (32-bit unsigned integer)

varchar_long

for Oracle 7: LONG; for Oracle 8/9: LONG; for ODBC: SQL_LONGVARCHAR; for DB2: CLOB

為了區分PL/SQL 塊或存儲過程中的輸入和輸出變量,OTL引入了以下限定詞:

l       in – 輸入變量

l       out – 輸出變量

l       inout – 輸入輸出變量

其用法如下的Oracle示例代碼片斷所示。

1 BEGIN
2    :rc<int,out> := my_func(:salary<float,in>,  
3                            :ID<int,inout>, 
4                            :name<char[32],out>
5                           );
6  END;

 

6.2 常量SQL

如果SQL語句 、PL/SQL 塊或存儲過程調用中不含有任何綁定變量,則可以稱之為靜態的。OTL包含了靜態方法執行靜態語句,例如:

 1 otl_cursor::direct_exec
 2 
 3    (db, // connect object
 4 
 5     "create table test_tab(f1 number, f2 varchar2(30))"
 6 
 7 );  // create table
 8 
 9  
10 
11   otl_cursor::direct_exec
12 
13    (db, // connect object
14 
15     "drop table test_tab", // SQL statement or PL/SQL block
16 
17     otl_exception::disabled // disable OTL exceptions,
18 
19                             // in other words, ignore any
20 
21                             // database error
22 
23    ); // drop table

otl_cursor是OTL4.0的一個internalclass。OTL雖然並不推薦使用低級別的類,但是otl_cursor的direct_exec()方法是一個特例。該方法的返回值可能為:

l       -1, 如果otl_exception異常被禁止使用(第二個參數被設置成otl_exception::disabled),並且底層的API返回了錯誤。

l       >=0, 如果成功執行SQL命令,在執行INSERT、DELETE或UPDATE語句時實際返回的是已處理行數。

7迭代器

7.1 OTL流的讀迭代器

OTL提供了模板類otl_stream_read_iterator來擴展OTL流接口以支持迭代,該模板類提供了類似JDBC的傳統getter接口,可以使用名稱訪問返回列。

以下是使用讀迭代器的示例代碼。

  1 #include <iostream>
  2 using namespace std;
  3   
  4 #include <stdio.h>
  5 //#define OTL_ORA7 // Compile OTL 4.0/OCI7
  6 //#define OTL_ORA8 // Compile OTL 4.0/OCI8
  7 //#define OTL_ORA8I // Compile OTL 4.0/OCI8i
  8 #define OTL_ORA9I // Compile OTL 4.0/OCI9i
  9 //#define OTL_ORA10G // Compile OTL 4.0/OCI10g
 10 #define OTL_STREAM_READ_ITERATOR_ON
 11 #define OTL_STL
 12 #include <otlv4.h> // include the OTL 4.0 header file
 13 
 14 otl_connect db; // connect object
 15 
 16 void insert()
 17 // insert rows into table
 18 { 
 19  otl_stream o(50, // buffer size
 20               "insert into test_tab values(:f1<int>,:f2<char[31]>)", // SQL statement
 21               db // connect object
 22              );
 23  char tmp[32];
 24 
 25  for(int i=1;i<=100;++i){
 26   sprintf(tmp,"Name%d",i);
 27   o<<i<<tmp;
 28  }
 29 }
 30 
 31 void select()
 32 { 
 33  otl_stream i(50, // buffer size
 34               "select * from test_tab "
 35          "where f1>=:f11<int> and f1<=:f12<int>*2",
 36                  // SELECT statement
 37               db // connect object
 38              ); 
 39    // create select stream
 40  
 41  int f1;
 42  char f2[31];
 43  otl_stream_read_iterator<otl_stream,otl_exception,otl_lob_stream> rs;
 44 
 45  rs.attach(i); // attach the iterator "rs" to the stream "i".
 46  i<<8<<8; // assigning :f11 = 8, :f12 = 8
 47    // SELECT automatically executes when all input variables are
 48    // assigned. First portion of output rows is fetched to the buffer
 49 
 50  while(rs.next_row()){// while not end-of-data
 51     rs.get("F2",f2);
 52     rs.get("F1",f1);
 53     cout<<"f1="<<f1<<", f2="<<f2<<endl;
 54  }
 55 
 56  rs.detach(); // detach the itertor from the stream
 57 
 58  i<<4<<4; // assigning :f11 = 4, :f12 = 4
 59    // SELECT automatically executes when all input variables are
 60    // assigned. First portion of output rows is fetched to the buffer
 61 
 62  while(!i.eof()){ // while not end-of-data
 63     i>>f1>>f2;
 64     cout<<"f1="<<f1<<", f2="<<f2<<endl;
 65  }
 66 }
 67 
 68 int main()
 69 {
 70  otl_connect::otl_initialize(); // initialize OCI environment
 71  try{
 72 
 73   db.rlogon("scott/tiger"); // connect to Oracle
 74 
 75   otl_cursor::direct_exec
 76    (
 77     db,
 78     "drop table test_tab",
 79     otl_exception::disabled // disable OTL exceptions
 80    ); // drop table
 81 
 82   otl_cursor::direct_exec
 83    (
 84     db,
 85     "create table test_tab(f1 number, f2 varchar2(30))"
 86     );  // create table
 87 
 88   insert(); // insert records into table
 89   select(); // select records from table
 90 
 91  }
 92 
 93  catch(otl_exception& p){ // intercept OTL exceptions
 94   cerr<<p.msg<<endl; // print out error message
 95   cerr<<p.stm_text<<endl; // print out SQL that caused the error
 96   cerr<<p.var_info<<endl; // print out the variable that caused the error
 97  }
 98 
 99  db.logoff(); // disconnect from Oracle
100 
101  return 0;
102 
103 }

 

輸出結果

f1=8, f2=Name8
f1=9, f2=Name9
f1=10, f2=Name10
f1=11, f2=Name11
f1=12, f2=Name12
f1=13, f2=Name13
f1=14, f2=Name14
f1=15, f2=Name15
f1=16, f2=Name16
f1=4, f2=Name4
f1=5, f2=Name5
f1=6, f2=Name6
f1=7, f2=Name7
f1=8, f2=Name8

7.2 STL兼容的迭代器

OTL將泛型編程和Oracle緊密結合以構築小容量的、可靠的、高性能並且容易維護的C++數據庫應用,為此分別提供了兩個STL兼容的迭代器:otl_output_iterator<T>和otl_input_iterator<T,Distance>。

otl_output_iterator<T>是一種輸出迭代器(Output Iterator),它將類型為T的對象輸出到otl_stream。其構造函數為otl_output_iterator(otl_stream&s)。

otl_input_iterator<T,Distance>是一種輸入迭代器(InputIterator),它從otl_stream中讀出將類型為T的對象,另外一個模板參數Distance為otl_input_iterator的指針偏移類型。當流的末尾到達時,otl_input_iterator會得到一個特殊的值即past-the-end迭代器。

otl_input_iterator的構造函數分別為otl_output_iterator(otl_stream&s)和otl_output_iterator(), 其中無參數的默認構造函數otl_output_iterator()將創建一個past-the-end迭代器用以指示otl_input_iterator到達流尾。

以下是使用STL兼容迭代器的示例代碼。

  1 #include <iostream>
  2 #include <vector>
  3 #include <iterator>
  4 #include <string>
  5 #define OTL_ORA8 // Compile OTL 4.0/OCI8
  6 #define OTL_STL // Turn on STL features
  7 #define OTL_ANSI_CPP // Turn on ANSI C++ typecasts
  8 #include <otlv4.h> // include the OTL 4.0 header file
  9 
 10 using namespace std;
 11 
 12 otl_connect db; // connect object
 13 
 14                 // row container class
 15 class row {
 16 public:
 17     int f1;
 18     string f2;
 19 
 20     // default constructor
 21     row() { f1 = 0; }
 22 
 23     // destructor 
 24     ~row() {}
 25 
 26     // copy constructor
 27     row(const row& row)
 28     {
 29         f1 = row.f1;
 30         f2 = row.f2;
 31     }
 32     // assignment operator
 33     row& operator=(const row& row)
 34     {
 35         f1 = row.f1;
 36         f2 = row.f2;
 37         return *this;
 38     }
 39 };
 40 
 41 // redefined operator>> for reading row& from otl_stream
 42 otl_stream& operator >> (otl_stream& s, row& row)
 43 {
 44     s >> row.f1 >> row.f2;
 45     return s;
 46 }
 47 
 48 // redefined operator<< for writing row& into otl_stream
 49 otl_stream& operator<<(otl_stream& s, const row& row)
 50 {
 51     s << row.f1 << row.f2;
 52     return s;
 53 }
 54 
 55 // redefined operator<< writing row& into ostream
 56 ostream& operator<<(ostream& s, const row& row)
 57 {
 58     s << "f1=" << row.f1 << ", f2=" << row.f2;
 59     return s;
 60 }
 61 
 62 void insert()
 63 // insert rows into table
 64 {
 65     otl_stream o(50, // buffer size
 66         "insert into test_tab values(:f1<int>,:f2<char[31]>)",
 67         // SQL statement
 68         db // connect object
 69     );
 70 
 71     row r; // single row buffer
 72     vector<row> vo; // vector of rows
 73 
 74                     // populate the vector
 75     for (int i = 1; i <= 100; ++i) {
 76         r.f1 = i;
 77         r.f2 = "NameXXX";
 78         vo.push_back(r);
 79     }
 80 
 81     cout << "vo.size=" << vo.size() << endl;
 82 
 83     // insert vector into table
 84     copy(vo.begin(), vo.end(), otl_output_iterator<row>(o));
 85 }
 86 
 87 void select()
 88 {
 89     otl_stream i(50, // buffer size
 90         "select * from test_tab where f1>=:f<int> and f1<=:f*2",
 91         // SELECT statement
 92         db // connect object
 93     );
 94     // create select stream
 95 
 96     vector<row> v; // vector of rows
 97 
 98                    // assigning :f = 8
 99     i << 8;
100 
101     // SELECT automatically executes when all input variables are
102     // assigned. First portion of out rows is fetched to the buffer
103 
104     // copy all rows to be fetched into the vector
105     copy(otl_input_iterator<row, ptrdiff_t>(i),
106         otl_input_iterator<row, ptrdiff_t>(),
107         back_inserter(v));
108 
109     cout << "Size=" << v.size() << endl;
110 
111     // send the vector to cout
112     copy(v.begin(), v.end(), ostream_iterator<row>(cout, "\n"));
113 
114     // clean up the vector
115     v.erase(v.begin(), v.end());
116 
117     i << 4; // assigning :f = 4
118             // SELECT automatically executes when all input variables are
119             // assigned. First portion of out rows is fetched to the buffer
120 
121             // copy all rows to be fetched to the vector
122     copy(otl_input_iterator<row, ptrdiff_t>(i),
123         otl_input_iterator<row, ptrdiff_t>(),
124         back_inserter(v));
125 
126     cout << "Size=" << v.size() << endl;
127 
128     // send the vector to cout
129     copy(v.begin(), v.end(), ostream_iterator<row>(cout, "\n"));
130 
131 }
132 
133 int main()
134 {
135     otl_connect::otl_initialize(); // initialize OCI environment
136     try {
137 
138         db.rlogon("scott/tiger"); // connect to Oracle
139 
140         otl_cursor::direct_exec
141         (
142             db,
143             "drop table test_tab",
144             otl_exception::disabled // disable OTL exceptions
145         ); // drop table
146 
147         otl_cursor::direct_exec
148         (
149             db,
150             "create table test_tab(f1 number, f2 varchar2(30))"
151         );  // create table
152 
153         insert(); // insert records into table
154         select(); // select records from table
155 
156     }
157 
158     catch (otl_exception& p) { // intercept OTL exceptions
159         cerr << p.msg << endl; // print out error message
160         cerr << p.stm_text << endl; // print out SQL that caused the error
161         cerr << p.var_info << endl; // print out the variable that caused the error
162     }
163 
164     db.logoff(); // disconnect from Oracle
165 
166     return 0;
167 
168 }

 

輸出結果

f1=8, f2=Name8
f1=9, f2=Name9
f1=10, f2=Name10
f1=11, f2=Name11
f1=12, f2=Name12
f1=13, f2=Name13
f1=14, f2=Name14
f1=15, f2=Name15
f1=16, f2=Name16
f1=4, f2=Name4
f1=5, f2=Name5
f1=6, f2=Name6
f1=7, f2=Name7
f1=8, f2=Name8

8 資源池

8.1 連接緩沖池

對於Oracle數據庫API,OTL的otl_connect類提供了server_attach()、server_detached()、session_begin()、session_end()四個方法(見4.2小節),它們可以聯合使用以創建類似連接緩沖池機制。使用session_begin()比直接使用rlogon()快大約50到100倍。

以下是聯合使用otl_connect的以上四個方法創建連接緩沖池機制的示例代碼。

  1 #include <iostream>
  2 
  3 using namespace std;
  4 
  5 
  6 
  7 #include <stdio.h>
  8 
  9 #define OTL_ORA8 // Compile OTL 4.0/OCI8
 10 
 11 #include <otlv4.h> // include the OTL 4.0 header file
 12 
 13 
 14 
 15 otl_connect db; // connect object
 16 
 17 
 18 
 19 void insert()
 20 
 21 // insert rows into table
 22 
 23 {
 24 
 25     otl_stream o(50, // buffer size
 26 
 27         "insert into test_tab values(:f1<float>,:f2<char[31]>)",
 28 
 29         // SQL statement
 30 
 31         db // connect object
 32 
 33     );
 34 
 35     char tmp[32];
 36 
 37 
 38 
 39     for (int i = 1; i <= 100; ++i) {
 40 
 41         sprintf(tmp, "Name%d", i);
 42 
 43         o << (float)i << tmp;
 44 
 45     }
 46 
 47 }
 48 
 49 
 50 
 51 void select()
 52 
 53 {
 54 
 55     otl_stream i(50, // buffer size
 56 
 57         "select * from test_tab where f1>=:f<int> and f1<=:f*2",
 58 
 59         // SELECT statement
 60 
 61         db // connect object
 62 
 63     );
 64 
 65     // create select stream
 66 
 67 
 68 
 69     float f1;
 70 
 71     char f2[31];
 72 
 73 
 74 
 75     i << 4; // assigning :f = 4
 76 
 77             // SELECT automatically executes when all input variables are
 78 
 79             // assigned. First portion of output rows is fetched to the buffer
 80 
 81 
 82 
 83     while (!i.eof()) { // while not end-of-data
 84 
 85         i >> f1 >> f2;
 86 
 87         cout << "f1=" << f1 << ", f2=" << f2 << endl;
 88 
 89     }
 90 
 91 
 92 
 93 }
 94 
 95 
 96 
 97 int main()
 98 
 99 {
100 
101     otl_connect::otl_initialize(); // initialize OCI environment
102 
103     try {
104 
105 
106 
107         db.rlogon("scott/tiger"); // connect to Oracle
108 
109 
110 
111         otl_cursor::direct_exec
112 
113         (
114 
115             db,
116 
117             "drop table test_tab",
118 
119             otl_exception::disabled // disable OTL exceptions
120 
121         ); // drop table
122 
123 
124 
125         otl_cursor::direct_exec
126 
127         (
128 
129             db,
130 
131             "create table test_tab(f1 number, f2 varchar2(30))"
132 
133         );  // create table
134 
135 
136 
137         insert(); // insert records into table
138 
139 
140 
141         db.logoff(); // disconnect from Oracle
142 
143 
144 
145         db.server_attach(); // attach to the local Oracle server
146 
147                             // In order to connect to a remote server,
148 
149                             // a TNS alias needs to be specified
150 
151 
152 
153         for (int i = 1; i <= 100; ++i) {
154 
155             cout << "Session begin ==> " << i << endl;
156 
157             db.session_begin("scott", "tiger");
158 
159             // begin session; this function is much faster
160 
161             // than rlogon() and should be used (see the Oracle
162 
163             // manuals for more detail) in high-speed processing
164 
165             // systems, possibly with thousands of users.
166 
167             // this technique can be used instead of traditional
168 
169             // connection pooling.
170 
171 
172 
173             select(); // select records from table
174 
175 
176 
177             cout << "Session end ==> " << i << endl;
178 
179             db.session_end(); // end session
180 
181         }
182 
183 
184 
185         db.server_detach();// detach from the Oracle server
186 
187     }
188 
189     catch (otl_exception& p) { // intercept OTL exceptions
190 
191         cerr << p.msg << endl; // print out error message
192 
193         cerr << p.stm_text << endl; // print out SQL that caused the error
194 
195         cerr << p.var_info << endl; // print out the variable that caused the error
196 
197     }
198 
199 
200 
201     db.logoff(); // make sure that the program gets disconnected from Oracle
202 
203 
204 
205     return 0;
206 
207 
208 
209 }

 

輸出結果:

Session begin ==> XXX
f1=4, f2=Name4
f1=5, f2=Name5
f1=6, f2=Name6
f1=7, f2=Name7
f1=8, f2=Name8
Session end ==> XXX

8.2 OTL流緩沖池

流緩沖池是OTL的一個新機制,當otl_stream實例關閉時,otl_stream變量實例將被保存到流緩沖池中,使得程序能夠繼續重用。otl_stream的每次實例化將觸發數據庫后台對OTL流中SQL語句的重新解析,這是相對耗時的操作,而流緩沖池機制將減少這方面的耗時並簡化編碼技術。

緩沖池中的流可以是局部變量也可以是分配在堆上的動態變量。流之間的相似型和流緩沖區大小以及SQL語句文本有關,擁有相同緩沖區大小和SQL語句文本的流將保存在緩沖池中相同的桶中如圖8-1所示。由於流緩沖池的底層使用STL的map和vector實現,因此使用時應該用”#defineOTL_STL”向編譯器指明。

圖8-1 OTL的流緩沖池

以下是采用流緩沖池機制的示例代碼。

  1 #include <iostream>
  2 using namespace std;
  3 
  4 #include <stdio.h>
  5 
  6 // Uncomment the line below when OCI7 is used with OTL
  7 // #define OTL_ORA7 // Compile OTL 4.0/OCI7 
  8 #define OTL_ORA8 // Compile OTL 4.0/OCI8
  9 #define OTL_STL // turn on OTL in the STL compliance mode
 10 #define OTL_STREAM_POOLING_ON 
 11 // turn on OTL stream pooling.
 12 // #define OTL_STREAM_POOLING_ON line 
 13 // can be commented out the number of iterations in
 14 // the select() loop can be increased, and the difference 
 15 // in performace with and without OTL_STREAM_POOLING_ON can
 16 // be benchmarked. The difference should grow with the overall
 17 // number of streams to be used in one program.
 18 
 19 #include <otlv4.h> // include the OTL 4.0 header file
 20 
 21 otl_connect db; // connect object
 22 
 23 void insert()
 24 // insert rows into table
 25 {
 26     otl_stream o(50, // buffer size
 27         "insert into test_tab values(:f1<int>,:f2<char[31]>)",
 28         // SQL statement
 29         db // connect object
 30     );
 31     char tmp[32];
 32 
 33     for (int i = 1; i <= 100; ++i) {
 34         sprintf(tmp, "Name%d", i);
 35         o << i << tmp;
 36     }
 37 #ifdef OTL_STREAM_POOLING_ON
 38     o.close(false); // do not save the stream in the stream pool.
 39                     // in other words, destroy it on the spot, since
 40                     // the stream is not going to be reused later.
 41 #else
 42     o.close();
 43 #endif
 44 }
 45 
 46 void select()
 47 { // when this function is called in a loop,
 48   // on the second iteration of the loop the streams i1, i2 will
 49   // will get the instances of the OTL stream from the stream
 50   // pool, "fast reopen", so to speak.
 51 
 52     otl_stream i1(50, // buffer size
 53         "select * from test_tab where f1>=:f11<int> and f1<=:f12<int>*2",
 54         // SELECT statement
 55         db // connect object
 56     );
 57     // create select stream
 58 
 59     otl_stream i2(33, // buffer size
 60         "select f1,f2 from test_tab where f1>=:f11<int> and f1<=:f12<int>*2",
 61         // SELECT statement
 62         db // connect object
 63     );
 64     // create select stream
 65 
 66     // i1 and i2 are NOT similar, because their buffer sizes as well
 67     // as SQL statements are not equal. It will generate two entry points in the
 68     // OTL stream pool.
 69 
 70     int f1;
 71     char f2[31];
 72 
 73     i1 << 2 << 2; // assigning :f11 = 2, :f12 = 2
 74                   // SELECT automatically executes when all input variables are
 75                   // assigned. First portion of output rows is fetched to the buffer
 76 
 77     while (!i1.eof()) { // while not end-of-data
 78         i1 >> f1 >> f2;
 79         cout << "I1==> f1=" << f1 << ", f2=" << f2 << endl;
 80     }
 81 
 82     i2 << 3 << 3; // assigning :f11 = 2, :f12 = 2
 83                   // SELECT automatically executes when all input variables are
 84                   // assigned. First portion of output rows is fetched to the buffer
 85 
 86     while (!i2.eof()) { // while not end-of-data
 87         i2 >> f1 >> f2;
 88         cout << "I2==> f1=" << f1 << ", f2=" << f2 << endl;
 89     }
 90 
 91 } // destructors of i1, i2 will call the close()
 92   // function for both of the streams and the OTL stream
 93   // instances will be placed in the stream pool.
 94 
 95 int main()
 96 {
 97     otl_connect::otl_initialize(); // initialize the environment
 98     try {
 99 
100         db.rlogon("scott/tiger"); // connect to the database
101 #ifdef OTL_STREAM_POOLING_ON
102         db.set_stream_pool_size(2);
103         // set the maximum stream pool size and actually initializes 
104         // the stream pool.
105         // if this function is not called, the stream pool
106         // gets initialized anyway, with the default size of 32 entries.
107 #endif
108 
109         otl_cursor::direct_exec
110         (
111             db,
112             "drop table test_tab",
113             otl_exception::disabled // disable OTL exceptions
114         ); // drop table
115 
116         otl_cursor::direct_exec
117         (
118             db,
119             "create table test_tab(f1 int, f2 varchar(30))"
120         );  // create table
121 
122         insert(); // insert records into table
123         for (int i = 1; i <= 10; ++i) {
124             cout << "===================> Iteration: " << i << endl;
125             select(); // select records from table
126         }
127     }
128 
129     catch (otl_exception& p) { // intercept OTL exceptions
130         cerr << p.msg << endl; // print out error message
131         cerr << p.stm_text << endl; // print out SQL that caused the error
132         cerr << p.var_info << endl; // print out the variable that caused the error
133     }
134 
135     db.logoff(); // disconnect from the database
136 
137     return 0;
138 
139 }

 

輸出結果:

===================> Iteration: 1
I1==> f1=2, f2=Name2
I1==> f1=3, f2=Name3
I1==> f1=4, f2=Name4
I2==> f1=3, f2=Name3
I2==> f1=4, f2=Name4
I2==> f1=5, f2=Name5
I2==> f1=6, f2=Name6
===================> Iteration: 2
I1==> f1=2, f2=Name2
I1==> f1=3, f2=Name3
I1==> f1=4, f2=Name4
I2==> f1=3, f2=Name3
I2==> f1=4, f2=Name4
I2==> f1=5, f2=Name5
I2==> f1=6, f2=Name6
===================> Iteration: 3
I1==> f1=2, f2=Name2
I1==> f1=3, f2=Name3
I1==> f1=4, f2=Name4
I2==> f1=3, f2=Name3
I2==> f1=4, f2=Name4
I2==> f1=5, f2=Name5
I2==> f1=6, f2=Name6
===================> Iteration: 4
I1==> f1=2, f2=Name2
I1==> f1=3, f2=Name3
I1==> f1=4, f2=Name4
I2==> f1=3, f2=Name3
I2==> f1=4, f2=Name4
I2==> f1=5, f2=Name5
I2==> f1=6, f2=Name6
===================> Iteration: 5
I1==> f1=2, f2=Name2
I1==> f1=3, f2=Name3
I1==> f1=4, f2=Name4
I2==> f1=3, f2=Name3
I2==> f1=4, f2=Name4
I2==> f1=5, f2=Name5
I2==> f1=6, f2=Name6
===================> Iteration: 6
I1==> f1=2, f2=Name2
I1==> f1=3, f2=Name3
I1==> f1=4, f2=Name4
I2==> f1=3, f2=Name3
I2==> f1=4, f2=Name4
I2==> f1=5, f2=Name5
I2==> f1=6, f2=Name6
===================> Iteration: 7
I1==> f1=2, f2=Name2
I1==> f1=3, f2=Name3
I1==> f1=4, f2=Name4
I2==> f1=3, f2=Name3
I2==> f1=4, f2=Name4
I2==> f1=5, f2=Name5
I2==> f1=6, f2=Name6
===================> Iteration: 8
I1==> f1=2, f2=Name2
I1==> f1=3, f2=Name3
I1==> f1=4, f2=Name4
I2==> f1=3, f2=Name3
I2==> f1=4, f2=Name4
I2==> f1=5, f2=Name5
I2==> f1=6, f2=Name6
===================> Iteration: 9
I1==> f1=2, f2=Name2
I1==> f1=3, f2=Name3
I1==> f1=4, f2=Name4
I2==> f1=3, f2=Name3
I2==> f1=4, f2=Name4
I2==> f1=5, f2=Name5
I2==> f1=6, f2=Name6
===================> Iteration: 10
I1==> f1=2, f2=Name2
I1==> f1=3, f2=Name3
I1==> f1=4, f2=Name4
I2==> f1=3, f2=Name3
I2==> f1=4, f2=Name4
I2==> f1=5, f2=Name5
I2==> f1=6, f2=Name6

 

9 操作大型對象

   OTL提供了otl_longstring、otl_long_unicode_string以及otl_lob_stream三個類操作大型對象。其中otl_long string、otl_long_unicode_string用來存儲大型對象,otl_lob_stream用來讀寫大型對象。

9.1大型對象的存儲

9.1.1 otl_long_string

OTL提供了類otl_long_string來存放ANSI字符集編碼的大型對象,其定義如下。

 1 class otl_long_string{
 2 public:
 3      unsigned char* v;
 4      otl_long_string(
 5 const int buffer_size=32760,
 6               const int input_length=0
 7       );
 8      otl_long_string(const void* external_buffer,
 9              const int buffer_size,
10              const int input_length=0
11     );
12      void set_len(const int len=0);
13     void set_last_piece(const bool last_piece=false); 
14      int len(void);
15      unsigned char& operator[](int ndx);
16  
17 otl_long_string& operator=(const otl_long_string&);
18 otl_long_string(const otl_long_string&);
19 }; // end of otl_long_string

 

otl_long_string的成員變量v存放大型對象的緩存起始位置。構造函數中的參數說明如下:

l       buffer_size參數指明存放大型對象的緩存大小,默認為32760,可以通過otl_connect的set_max_long_size()方法來改變默認的大小值 。

l       input_length參數則指明實際輸入的大小,如果該參數被設定則set_len()成員方法就沒有必要使用了。

l       另外,如果使用參數external_buffer則otl_long_string不再為大型對象實際分配存儲空間,而是直接使用用戶傳入的以external_buffer為起始地址的緩存。

9.1.2 otl_long_unicode_string

OTL提供了類otl_long_string來存放UNICODE字符集編碼的大型對象,其定義如下。

 1 class otl_long_unicode_string: public otl_long_string{
 2 public:
 3 otl_long_unicode_string(
 4             const int buffer_size=32760,
 5             const int input_leng
 6 );
 7  
 8 otl_long_unicode_string(
 9             const void* external_buffer,
10             const int buffer_size,
11             const int input_length=0
12      );
13  
14 void set_len(const int len=0);
15           int len(void);
16           unsigned short& operator[](int ndx);
17 }; // end of otl_long_unicode_string

 

otl_long_unicode_string的成員變量、方法以及構造函數和父類相同,但是注意緩沖區大小為UNICODE字符數量而不是字節數。

9.2 大型對象的讀寫

    大型對象的讀寫通過類otl_lob_stream實現,其定義如下。

 1 class otl_lob_stream {
 2 public:
 3     void set_len(const int alen);
 4     otl_lob_stream& operator<<(const std::string& s);
 5     otl_lob_stream& operator<<(const ACE_TString& s);
 6 
 7     otl_lob_stream& operator >> (std::string& s);
 8     otl_lob_stream& operator >> (ACE_TString& s);
 9 
10     void setStringBuffer(const int chunk_size);
11     otl_lob_stream& operator<<(const otl_long_string& s);
12     otl_lob_stream& operator<<(const otl_long_unicode_string& s);
13     otl_lob_stream& operator >> (otl_long_string& s);
14 
15     otl_lob_stream& operator >> (otl_long_unicode_string& s);
16 
17     int len(void);
18     int eof(void);
19     void close(void);
20     bool is_initialized(void);
21 }; // end of otl_lob_stream

otl_lob_stream重載了<<和>>運算符來操作存放在std::string、otl_long_string、otl_long_unicode_string以及ACE_TString中的大型對象。

以下是操作大型對象的示例代碼。包括INSERT、UPDATE、SELECT操作。注意在使用INSERT和UPDATE時流緩沖區的大小必須為1,另外必須將otl_stream的auto_commit標志設置為false。初始化otl_lob_stream是通過將otl_lob_stream對象作為otl_stream的<<操作符參數來實現的。

  1 #include <iostream>
  2 using namespace std;
  3 
  4 #include <stdio.h>
  5 #define OTL_ORA8 // Compile OTL 4.0/OCI8
  6 #include <otlv4.h> // include the OTL 4.0 header file
  7 
  8 otl_connect db; // connect object
  9 
 10 void insert()
 11 // insert rows into table
 12 {
 13     otl_long_string f2(60000); // define long string variable
 14     otl_stream o(1, // buffer size has to be set to 1 for operations with LOBs
 15         "insert into test_tab values(:f1<int>,empty_clob()) "
 16         "returning f2 into :f2<clob> ", // SQL statement
 17         db // connect object
 18     );
 19     o.set_commit(0); // setting stream "auto-commit" to "off". It is required
 20                      // when LOB stream mode is used.
 21 
 22     otl_lob_stream lob; // LOB stream for reading/writing unlimited number
 23                         // of bytes regardless of the buffer size.
 24 
 25     for (int i = 1; i <= 20; ++i) {
 26         for (int j = 0; j<50000; ++j)
 27             f2[j] = '*';
 28         f2[50000] = '?';
 29         f2.set_len(50001);
 30 
 31         o << i;
 32 
 33         o << lob; // Initialize otl_lob_stream by writing it
 34                   // into otl_stream. Weird, isn't it?
 35 
 36         lob.set_len(50001 + 23123); // setting the total  size of
 37                                     // the CLOB to be written.
 38                                     // It is required for compatibility
 39                                     // with earlier releases of OCI8: OCI8.0.3, OCI8.0.4.
 40 
 41         lob << f2; // writing first chunk of the CLOB into lob
 42 
 43         f2[23122] = '?';
 44         f2.set_len(23123); // setting the size of the second chunk
 45 
 46         lob << f2; // writing the second chunk of the CLOB into lob
 47         lob.close(); // closing the otl_lob_stream
 48     }
 49 
 50     db.commit(); // committing transaction.
 51 }
 52 void update()
 53 // insert rows in table
 54 {
 55     otl_long_string f2(6200); // define long string variable
 56 
 57     otl_stream o(1, // buffer size has to be set to 1 for operations with LOBs
 58         "update test_tab "
 59         "   set f2=empty_clob() "
 60         "where f1=:f1<int> "
 61         "returning f2 into :f2<clob> ",
 62         // SQL statement
 63         db // connect object
 64     );
 65 
 66     otl_lob_stream lob;
 67 
 68     o.set_commit(0); // setting stream "auto-commit" to "off". 
 69 
 70 
 71     for (int j = 0; j<6000; ++j) {
 72         f2[j] = '#';
 73     }
 74 
 75     f2[6000] = '?';
 76     f2.set_len(6001);
 77 
 78     o << 5;
 79     o << lob; // Initialize otl_lob_stream by writing it
 80               // into otl_stream.
 81 
 82     lob.set_len(6001 * 4); // setting the total size of of the CLOB to be written
 83     for (int i = 1; i <= 4; ++i)
 84         lob << f2; // writing chunks of the CLOB into the otl_lob_stream
 85 
 86     lob.close(); // closing the otl_lob_stream
 87 
 88     db.commit(); // committing transaction
 89 
 90 }
 91 
 92 void select()
 93 {
 94     otl_long_string f2(20000); // define long string variable
 95 
 96     otl_stream i(10, // buffer size. To read CLOBs, it can be set to a size greater than 1
 97         "select * from test_tab where f1>=:f<int> and f1<=:f*2",
 98         // SELECT statement
 99         db // connect object
100     );
101     // create select stream
102 
103     float f1;
104     otl_lob_stream lob; // Stream for reading CLOB
105 
106     i << 4; // assigning :f = 4
107             // SELECT automatically executes when all input variables are
108             // assigned. First portion of output rows is fetched to the buffer
109 
110     while (!i.eof()) { // while not end-of-data
111         i >> f1;
112         cout << "f1=" << f1 << endl;
113         i >> lob; // initializing CLOB stream by reading the CLOB reference 
114                   // into the otl_lob_stream from the otl_stream.
115         int n = 0;
116         while (!lob.eof()) { // read while not "end-of-file" -- end of CLOB
117             ++n;
118             lob >> f2; // reading a chunk of CLOB
119             cout << "   chunk #" << n;
120             cout << ", f2=" << f2[0] << f2[f2.len() - 1] << ", len=" << f2.len() << endl;
121         }
122         lob.close(); // closing the otl_lob_stream. This step may be skipped.
123     }
124 }
125 
126 int main()
127 {
128     otl_connect::otl_initialize(); // initialize OCI environment
129     try {
130 
131         db.rlogon("scott/tiger"); // connect to Oracle
132 
133         otl_cursor::direct_exec
134         (
135             db,
136             "drop table test_tab",
137             otl_exception::disabled // disable OTL exceptions
138         ); // drop table
139 
140         otl_cursor::direct_exec
141         (
142             db,
143             "create table test_tab(f1 number, f2 clob)"
144         );  // create table
145 
146         insert(); // insert records into table
147         update(); // update records in table
148         select(); // select records from table
149 
150     }
151 
152     catch (otl_exception& p) { // intercept OTL exceptions
153         cerr << p.msg << endl; // print out error message
154         cerr << p.stm_text << endl; // print out SQL that caused the error
155         cerr << p.var_info << endl; // print out the variable that caused the error
156     }
157 
158     db.logoff(); // disconnect from Oracle
159 
160     return 0;
161 
162 }

 

輸出結果:

f1=4
   chunk #1, f2=**, len=20000
   chunk #2, f2=**, len=20000
   chunk #3, f2=**, len=20000
   chunk #4, f2=*?, len=13124
f1=5
   chunk #1, f2=##, len=20000
   chunk #2, f2=#?, len=4004
f1=6
   chunk #1, f2=**, len=20000
   chunk #2, f2=**, len=20000
   chunk #3, f2=**, len=20000
   chunk #4, f2=*?, len=13124
f1=7
   chunk #1, f2=**, len=20000
   chunk #2, f2=**, len=20000
   chunk #3, f2=**, len=20000
   chunk #4, f2=*?, len=13124
f1=8
   chunk #1, f2=**, len=20000
   chunk #2, f2=**, len=20000
   chunk #3, f2=**, len=20000
   chunk #4, f2=*?, len=13124

10國際化

   OTL的國際化支持主要是通過支持編碼類型為UNICODE或UTF8的字符串操作實現。

10.1 使用UNICODE字符串

可以通過宏”#define OTL_UNICODE”指示OTL內部使用UNICODE字符串。以下是使用UNICODE字符串的示例代碼。

示例代碼中使用unsigned short類型數組存放UNICODE字符串,由於otl_stream的操作符<<並不支持unsigned short*類型,因此在變量綁定使用<<操作符時,將其強制轉換成unsigned char*類型進行操作。與此類似,由於otl_stream的操作符>>並不支持unsigned short*類型,讀取結果使用>>操作符時也是將其強制轉換成unsigned char*類型進行操作。

  1 #include <iostream>
  2 using namespace std;
  3 
  4 #include <stdio.h>
  5 
  6 #define OTL_ORA9I // Compile OTL 4.0/OCI9i
  7 #define OTL_UNICODE // Enable Unicode OTL for OCI9i
  8 #include <otlv4.h> // include the OTL 4.0 header file
  9 
 10 otl_connect db; // connect object
 11 
 12 void insert()
 13 // insert rows into table
 14 {
 15     otl_stream o(50, // buffer size
 16         "insert into test_tab values(:f1<float>,:f2<char[31]>)",
 17         // SQL statement
 18         db // connect object
 19     );
 20     char tmp[32];
 21     unsigned short tmp2[32]; // Null terminated Unicode character array.
 22 
 23     for (int i = 1; i <= 100; ++i) {
 24         sprintf(tmp, "Name%d", i);
 25         unsigned short* c2 = tmp2;
 26         char* c1 = tmp;
 27         // Unicode's first 128 characters are ASCII (0..127), so
 28         // all is needed for converting ASCII into Unicode is as follows:
 29         while (*c1) {
 30             *c2 = (unsigned char)*c1;
 31             ++c1; ++c2;
 32         }
 33         *c2 = 0; // target Unicode string is null terminated,
 34                  // only the null terminator is a two-byte character, 
 35                  // not one-byte
 36         o << (float)i;
 37         o << (unsigned char*)tmp2;
 38         // overloaded operator<<(const unsigned char*) in the case of Unicode
 39         // OTL accepts a pointer to a Unicode character array.
 40         // operator<<(const unsigned short*) wasn't overloaded
 41         // in order to avoid ambiguity in C++ type casting.
 42     }
 43 
 44 }
 45 
 46 void select()
 47 {
 48     otl_stream i(50, // buffer size
 49         "select * from test_tab where f1>=:f<int> and f1<=:f*2", // SELECT statement
 50         db // connect object
 51     );
 52     // create select stream
 53 
 54     float f1;
 55     unsigned short f2[32];
 56 
 57     i << 8; // assigning :f = 8
 58             // SELECT automatically executes when all input variables are
 59             // assigned. First portion of output rows is fetched to the buffer
 60 
 61     while (!i.eof()) { // while not end-of-data
 62         i >> f1;
 63         i >> (unsigned char*)f2;
 64         // overloaded operator>>(unsigned char*) in the case of Unicode
 65         // OTL accepts a pointer to a Unicode chracter array.
 66         // operator>>(unsigned short*) wasn't overloaded 
 67         // in order to avoid ambiguity in C++ type casting.
 68         cout << "f1=" << f1 << ", f2=";
 69         // Unicode's first 128 characters are ASCII, so in order
 70         // to convert Unicode back to ASCII all is needed is
 71         // as follows:
 72         for (int j = 0; f2[j] != 0; ++j) {
 73             cout << (char)f2[j];
 74         }
 75         cout << endl;
 76     }
 77 
 78     i << 4; // assigning :f = 4
 79             // SELECT automatically executes when all input variables are
 80             // assigned. First portion of output rows is fetched to the buffer
 81 
 82     while (!i.eof()) { // while not end-of-data
 83         i >> f1 >> (unsigned char*)f2;
 84         cout << "f1=" << f1 << ", f2=";
 85         for (int j = 0; f2[j] != 0; ++j) {
 86             cout << (char)f2[j];
 87         }
 88         cout << endl;
 89     }
 90 
 91 }
 92 
 93 int main()
 94 {
 95     otl_connect::otl_initialize(); // initialize OCI environment
 96     try {
 97 
 98         db.rlogon("scott/tiger"); // connect to Oracle
 99 
100         otl_cursor::direct_exec
101         (
102             db,
103             "drop table test_tab",
104             otl_exception::disabled // disable OTL exceptions
105         ); // drop table
106 
107         otl_cursor::direct_exec
108         (
109             db,
110             "create table test_tab(f1 number, f2 varchar2(30))"
111         );  // create table
112 
113         insert(); // insert records into table
114         select(); // select records from table
115 
116     }
117 
118     catch (otl_exception& p) { // intercept OTL exceptions
119         cerr << p.msg << endl; // print out error message
120         cerr << p.stm_text << endl; // print out SQL that caused the error
121         cerr << p.var_info << endl; // print out the variable that caused the error
122     }
123 
124     db.logoff(); // disconnect from Oracle
125 
126     return 0;
127 
128 }

 

輸出結果:

f1=8, f2=Name8
f1=9, f2=Name9
f1=10, f2=Name10
f1=11, f2=Name11
f1=12, f2=Name12
f1=13, f2=Name13
f1=14, f2=Name14
f1=15, f2=Name15
f1=16, f2=Name16
f1=4, f2=Name4
f1=5, f2=Name5
f1=6, f2=Name6
f1=7, f2=Name7
f1=8, f2=Name8

10.2 使用UTF8字符串

可以通過宏”#define OTL_ORA_UTF8”指示OTL內部使用UTF8字符串。以下是使用UTF字符串的示例代碼。

示例代碼中使用unsigned char類型數組存放UTF8字符串, 在使用otl_stream的操作符<<進行變量綁定時,通過轉型並使用copy()函數將其轉成char*類型進行操作。另外需要注意到環境變量的設定NLS_LANG=.AL32UTF8。

  1 #include <iostream>
  2 using namespace std;
  3 
  4 #include <stdio.h>
  5 
  6 #define OTL_ORA9I // Compile OTL 4.0/OCI9i
  7 #define OTL_ORA_UTF8 // Enable UTF8 OTL for OCI9i
  8 #include <otlv4.h> // include the OTL 4.0 header file
  9 
 10 otl_connect db; // connect object
 11 
 12                 // Sample UTF8 based string
 13 unsigned char utf8_sample[] =
 14 { 0x61,0x62,0x63,0xd0,0x9e,0xd0,0x9b,0xd0,
 15 0xac,0xd0,0x93,0xd0,0x90,0x0 };
 16 
 17 void insert()
 18 // insert rows into table
 19 {
 20     otl_stream o(50, // buffer size
 21         "insert into test_tab values(:f1<int>,:f2<char[31]>)",
 22         // SQL statement
 23         db // connect object
 24     );
 25 
 26     unsigned char tmp[31];
 27 
 28     for (int i = 1; i <= 100; ++i) {
 29         strcpy(reinterpret_cast<char*>(tmp), reinterpret_cast<const char*>(utf8_sample));
 30         o << i;
 31         o << tmp;
 32     }
 33 
 34 }
 35 
 36 void select()
 37 {
 38     otl_stream i(50, // buffer size
 39         "select * from test_tab where f1>=:f<int> and f1<=:f*2",
 40         // SELECT statement
 41         db // connect object
 42     );
 43     // create select stream
 44 
 45     int f1;
 46     unsigned char f2[31];
 47 
 48     i << 8; // assigning :f = 8
 49             // SELECT automatically executes when all input variables are
 50             // assigned. First portion of output rows is fetched to the buffer
 51 
 52     while (!i.eof()) { // while not end-of-data
 53         i >> f1;
 54         i >> f2;
 55         cout << "f1=" << f1 << ", f2=";
 56         for (int j = 0; f2[j] != 0; ++j)
 57             printf("%2x ", f2[j]);
 58         cout << endl;
 59     }
 60 
 61 }
 62 
 63 int main()
 64 {
 65     putenv(const_cast<char*>("NLS_LANG=.AL32UTF8"));
 66 
 67     // set your Oracle Client NLS_LANG 
 68     // if its default was set to something else
 69     otl_connect::otl_initialize(); // initialize OCI environment
 70     try {
 71 
 72         db.rlogon("scott/tiger"); // connect to Oracle
 73 
 74         otl_cursor::direct_exec
 75         (
 76             db,
 77             "drop table test_tab",
 78             otl_exception::disabled // disable OTL exceptions
 79         ); // drop table
 80 
 81         otl_cursor::direct_exec
 82         (
 83             db,
 84             "create table test_tab(f1 number, f2 varchar2(30))"
 85         );  // create table
 86 
 87         insert(); // insert records into table
 88         select(); // select records from table
 89 
 90     }
 91 
 92     catch (otl_exception& p) { // intercept OTL exceptions
 93         cerr << p.msg << endl; // print out error message
 94         cerr << p.stm_text << endl; // print out SQL that caused the error
 95         cerr << p.var_info << endl; // print out the variable that caused the error
 96     }
 97 
 98     db.logoff(); // disconnect from Oracle
 99 
100     return 0;
101 }

 

輸出結果:

f1=8, f2=61 62 63 d0 9e d0 9b d0 ac d0 93 d0 90
f1=9, f2=61 62 63 d0 9e d0 9b d0 ac d0 93 d0 90
f1=10, f2=61 62 63 d0 9e d0 9b d0 ac d0 93 d0 90
f1=11, f2=61 62 63 d0 9e d0 9b d0 ac d0 93 d0 90
f1=12, f2=61 62 63 d0 9e d0 9b d0 ac d0 93 d0 90
f1=13, f2=61 62 63 d0 9e d0 9b d0 ac d0 93 d0 90
f1=14, f2=61 62 63 d0 9e d0 9b d0 ac d0 93 d0 90
f1=15, f2=61 62 63 d0 9e d0 9b d0 ac d0 93 d0 90
f1=16, f2=61 62 63 d0 9e d0 9b d0 ac d0 93 d0 90

11 Reference Cursor流

OTL為OTL/OCI8/8i/9i/10g提供了類otl_refcur_stream,它可以從reference cursor類型的綁定變量中讀取結果行,其定義如下所示。

 1 class otl_refcur_stream {
 2 public:
 3  void set_column_type(const int column_ndx,
 4                       const int col_type,
 5                       const int col_size=0);
 6  void set_all_column_types(const unsigned mask=0);
 7   
 8  void rewind(void);
 9  int is_null(void);
10  int eof(void);
11  void close(void);
12   
13  otl_column_desc* describe_select(int& desc_len);
14   
15  otl_var_desc* describe_out_vars(int& desc_len);
16  otl_var_desc* describe_next_out_var(void); 
17   
18  otl_refcur_stream& operator>>(char& c);
19  otl_refcur_stream& operator>>(unsigned char& c);
20  otl_refcur_stream& operator>>(char* s);
21  otl_refcur_stream& operator>>(unsigned char* s);
22  otl_refcur_stream& operator>>(int& n);
23  otl_refcur_stream& operator>>(unsigned& u);
24  otl_refcur_stream& operator>>(short& sh);
25  otl_refcur_stream& operator>>(long int& l);
26  otl_refcur_stream& operator>>(float& f);
27  otl_refcur_stream& operator>>(double& d);
28   
29   
30 //for large whole number
31  otl_refcur_stream& operator>>(OTL_BIGINT& n);
32     
33  //for LOBs
34  otl_refcur_stream& operator>>(otl_long_string& s);
35  otl_refcur_stream& operator>>(otl_datetime& dt);
36  otl_refcur_stream& operator>>(otl_lob_stream& lob);                      
37  otl_refcur_stream& operator>>(std::string& s); 
38  
39 //for UNICODE
40  otl_stream& operator>>(unsigned char* s);
41  otl_stream& operator>>(otl_long_unicode_string& s); 
42      
43 }; // end of otl_refcur_stream

 

otl_refcur_stream的初始化通過將otl_refcur_stream對象作為otl_stream的>>操作符參數來實現。以下是其基本使用的示例代碼。

  1 #include <iostream>
  2 using namespace std;
  3 
  4 #include <stdio.h>
  5 #define OTL_ORA8 // Compile OTL 4.0/OCI8
  6 //#define OTL_ORA8I // Compile OTL 4.0/OCI8i
  7 //#define OTL_ORA9I // Compile OTL 4.0/OCI9i
  8 #include <otlv4.h> // include the OTL 4.0 header file
  9 
 10 otl_connect db; // connect object
 11 
 12 void insert()
 13 // insert rows into table
 14 {
 15     otl_stream o(50, // buffer size
 16         "insert into test_tab values(:f1<float>,:f2<char[31]>)", // SQL statement
 17         db // connect object
 18     );
 19     char tmp[32];
 20 
 21     for (int i = 1; i <= 100; ++i) {
 22         sprintf(tmp, "Name%d", i);
 23         o << (float)i << tmp;
 24     }
 25 }
 26 
 27 void select()
 28 {
 29 
 30     // :cur is a bind variable name, refcur -- its type, 
 31     // out -- output parameter, 50 -- the buffer size when this
 32     // reference cursor will be attached to otl_refcur_stream
 33     otl_stream i(1, // buffer size
 34         "begin "
 35         " open :cur<refcur,out[50]> for "
 36         "  select * "
 37         "  from test_tab "
 38         "  where f1>=:f<int,in> and f1<=:f*2; "
 39         "end;", // PL/SQL block returns a referenced cursor
 40         db // connect object
 41     );
 42     // create select stream with referenced cursor
 43 
 44     i.set_commit(0); // set stream "auto-commit" to OFF.
 45 
 46     float f1;
 47     char f2[31];
 48     otl_refcur_stream s; // reference cursor stream for reading rows.
 49 
 50     i << 8; // assigning :f = 8
 51     i >> s;// initializing the refrence cursor stream with the output 
 52            // reference cursor.
 53 
 54     while (!s.eof()) { // while not end-of-data
 55         s >> f1 >> f2;
 56         cout << "f1=" << f1 << ", f2=" << f2 << endl;
 57     }
 58 
 59     s.close(); // closing the reference cursor
 60 
 61     i << 4; // assigning :f = 4
 62     i >> s;
 63 
 64     while (!s.eof()) { // while not end-of-data
 65         s >> f1 >> f2;
 66         cout << "f1=" << f1 << ", f2=" << f2 << endl;
 67     }
 68     // there is no need to explicitly calls s.close() since s's destructor 
 69     // will take care of closing the stream
 70 }
 71 
 72 int main()
 73 {
 74     otl_connect::otl_initialize(); // initialize OCI environment
 75     try {
 76 
 77         db.rlogon("scott/tiger"); // connect to Oracle
 78 
 79         otl_cursor::direct_exec
 80         (
 81             db,
 82             "drop table test_tab",
 83             otl_exception::disabled // disable OTL exceptions
 84         ); // drop table
 85 
 86         otl_cursor::direct_exec
 87         (
 88             db,
 89             "create table test_tab(f1 number, f2 varchjar2(30))"
 90         );  // create table
 91 
 92         insert(); // insert records into table
 93         select(); // select records from table
 94 
 95     }
 96     catch (otl_exception& p) { // intercept OTL exceptions
 97         cerr << p.msg << endl; // print out error message
 98         cerr << p.stm_text << endl; // print out SQL that caused the error
 99         cerr << p.var_info << endl; // print out the variable that caused the error
100     }
101 
102     db.logoff(); // disconnect from Oracle
103 
104     return 0;
105 
106 }

 

輸出結果:

f1=8, f2=Name8
f1=9, f2=Name9
f1=10, f2=Name10
f1=11, f2=Name11
f1=12, f2=Name12
f1=13, f2=Name13
f1=14, f2=Name14
f1=15, f2=Name15
f1=16, f2=Name16
f1=4, f2=Name4
f1=5, f2=Name5
f1=6, f2=Name6
f1=7, f2=Name7
f1=8, f2=Name8    

12 雜項

12.1 使用otl_nocommit_stream避免SQL執行成功后立刻提交事務

otl_stream提供了方法set_commit()方法(見4.1小節)設置auto_commit標志。該標志控制SQL執行成功后,是否立刻提交當前事務。auto_commit默認為true, 即提交事務。可以通過設置auto_commit標志值為false避免這種情況。

otl_stream的派生類otl_nocimmit_stream提供了SQL執行成功后不立刻提交事務的功能,該類通過將auto_commit標記默認設置為true, 以簡化實現該功能時的代碼編寫。

以下是使用otl_nocommit_stream類避免SQL執行成功后立刻提交事務的示例代碼。

 1 #include <iostream>
 2 using namespace std;
 3 
 4 #include <stdio.h>
 5 #define OTL_ORA8 // Compile OTL 4.0/OCI8
 6 #include <otlv4.h> // include the OTL 4.0 header file
 7 
 8 otl_connect db; // connect object
 9 
10 void insert()
11 // insert rows into table
12 {
13     otl_nocommit_stream o
14     (50, // buffer size
15         "insert into test_tab values(:f1<float>,:f2<char[31]>)", // SQL statement
16         db // connect object
17     );
18     char tmp[32];
19 
20     for (int i = 1; i <= 100; ++i) {
21         sprintf(tmp, "Name%d", i);
22         o << (float)i << tmp;
23     }
24 
25     o.flush();
26     db.commit();
27 
28 }
29 
30 void select()
31 {
32     otl_stream i(50, // buffer size
33         "select * from test_tab where f1>=:f<int> and f1<=:f*2",
34         // SELECT statement
35         db // connect object
36     );
37     // create select stream
38 
39     float f1;
40     char f2[31];
41 
42     i << 8; // assigning :f = 8
43             // SELECT automatically executes when all input variables are
44             // assigned. First portion of output rows is fetched to the buffer
45 
46     while (!i.eof()) { // while not end-of-data
47         i >> f1 >> f2;
48         cout << "f1=" << f1 << ", f2=" << f2 << endl;
49     }
50 
51     i << 4; // assigning :f = 4
52             // SELECT automatically executes when all input variables are
53             // assigned. First portion of output rows is fetched to the buffer
54 
55     while (!i.eof()) { // while not end-of-data
56         i >> f1 >> f2;
57         cout << "f1=" << f1 << ", f2=" << f2 << endl;
58     }
59 
60 }
61 
62 int main()
63 {
64     otl_connect::otl_initialize(); // initialize OCI environment
65     try {
66 
67         db.rlogon("scott/tiger"); // connect to Oracle
68 
69         otl_cursor::direct_exec
70         (
71             db,
72             "drop table test_tab",
73             otl_exception::disabled // disable OTL exceptions
74         ); // drop table
75 
76         otl_cursor::direct_exec
77         (
78             db,
79             "create table test_tab(f1 number, f2 varchar2(30))"
80         );  // create table
81 
82         insert(); // insert records into table
83         select(); // select records from table
84 
85     }
86 
87     catch (otl_exception& p) { // intercept OTL exceptions
88         cerr << p.msg << endl; // print out error message
89         cerr << p.stm_text << endl; // print out SQL that caused the error
90         cerr << p.var_info << endl; // print out the variable that caused the error
91     }
92 
93     db.logoff(); // disconnect from Oracle
94 
95     return 0;
96 
97 }

 

輸出結果:

f1=8, f2=Name8
f1=9, f2=Name9
f1=10, f2=Name10
f1=11, f2=Name11
f1=12, f2=Name12
f1=13, f2=Name13
f1=14, f2=Name14
f1=15, f2=Name15
f1=16, f2=Name16
f1=4, f2=Name4
f1=5, f2=Name5
f1=6, f2=Name6
f1=7, f2=Name7
f1=8, f2=Name8

12.2 SELECT中的數據類型映射覆寫

otl_stream在執行SELECT語句返回結果列時,具有如下表12-1所示的數據庫數據類型(Database datatype)到默認數據類型(Default datatype)的映射。

某些情況下往往需要對這種默認映射進行覆寫,即將數據庫數據類型映射為非默認的數據類型。例如當讀取超過16位的數字時,將其映射為字符串類型往往更方便。為此,otl_stream提供了set_colunm_type()方法(參見4.1小節)進行數據類型覆寫。

12-1 otl_stream中的數據類型映射覆寫

Database  datatype

Default datatype

Datatype override

NUMBER (Oracle)

otl_var_double

otl_var_char, otl_var_int, otl_var_float, otl_var_short, otl_var_unsigned_int

NUMERIC, FLOAT, REAL, MONEY, DECIMAL (MS SQL Server, Sybase, DB2)

otl_var_double

otl_var_char, otl_var_int, otl_var_float, otl_var_short, otl_var_unsigned_int, otl_var_long_int

INT (MS SQL Server, Sybase, DB2)

otl_var_int

otl_var_char, otl_var_double, otl_var_float, otl_var_short, otl_var_unsigned_int, otl_var_long_int

SMALLINT, TINYINT (MS SQL Server, Sybase, DB2)

otl_var_short

otl_var_char, otl_var_int, otl_var_float, otl_var_double, otl_var_unsigned_int, otl_var_long_int

DATE (Oracle), DATETIME (MS SQL Server, Sybase)

otl_timestamp

otl_var_char

LONG (Oracle)

otl_var_varchar_long

otl_var_char (<=32000 bytes)

TEXT (MS SQL Server, Sybase)

otl_var_varchar_long

otl_var_char(<= max. size of varchar, e.g. <=8000 in MS SQL 7.0)

 

以下是SELECT中的數據類型覆寫的示例代碼。

 1 #include <iostream>
 2 using namespace std;
 3 
 4 #include <stdio.h>
 5 #define OTL_ORA8 // Compile OTL 4.0/OCI8
 6 #include <otlv4.h> // include the OTL 4.0 header file
 7 
 8 otl_connect db; // connect object
 9 
10 void insert()
11 // insert rows into table
12 {
13     otl_stream o(50, // buffer size
14         "insert into test_tab values(12345678900000000+:f1<int>,:f2<char[31]>)",
15         // SQL statement
16         db // connect object
17     );
18     char tmp[32];
19 
20     for (int i = 1; i <= 100; ++i) {
21         sprintf(tmp, "Name%d", i);
22         o << i << tmp;
23     }
24 }
25 
26 void select()
27 {
28     otl_stream i;
29 
30     i.set_column_type(1, otl_var_char, 40);// use a string(40) instead of default double
31     i.open(50, // buffer size
32         "select * from test_tab "
33         "where f1>=12345678900000000+:f<int> "
34         "  and f1<=12345678900000000+:f*2",
35         // SELECT statement
36         db // connect object
37     );
38     // create select stream
39 
40     char f1[40];
41     char f2[31];
42 
43     i << 8; // assigning :f = 8
44             // SELECT automatically executes when all input variables are
45             // assigned. First portion of output rows is fetched to the buffer
46 
47     while (!i.eof()) { // while not end-of-data
48         i >> f1 >> f2;
49         cout << "f1=" << f1 << ", f2=" << f2 << endl;
50     }
51 
52     i << 4; // assigning :f = 4
53             // SELECT automatically executes when all input variables are
54             // assigned. First portion of output rows is fetched to the buffer
55 
56     while (!i.eof()) { // while not end-of-data
57         i >> f1 >> f2;
58         cout << "f1=" << f1 << ", f2=" << f2 << endl;
59     }
60 
61 }
62 
63 int main()
64 {
65     otl_connect::otl_initialize(); // initialize OCI environment
66     try {
67 
68         db.rlogon("scott/tiger"); // connect to Oracle
69 
70         otl_cursor::direct_exec
71         (
72             db,
73             "drop table test_tab",
74             otl_exception::disabled // disable OTL exceptions
75         ); // drop table
76 
77         otl_cursor::direct_exec
78         (
79             db,
80             "create table test_tab(f1 number, f2 varchar2(30))"
81         );  // create table
82 
83         insert(); // insert records into table
84         select(); // select records from table
85 
86     }
87 
88     catch (otl_exception& p) { // intercept OTL exceptions
89         cerr << p.msg << endl; // print out error message
90         cerr << p.stm_text << endl; // print out SQL that caused the error
91         cerr << p.var_info << endl; // print out the variable that caused the error
92     }
93 
94     db.logoff(); // disconnect from Oracle
95 
96     return 0;
97 
98 }

 

輸出結果:

f1=12345678900000008, f2=Name8
f1=12345678900000009, f2=Name9
f1=12345678900000010, f2=Name10
f1=12345678900000011, f2=Name11
f1=12345678900000012, f2=Name12
f1=12345678900000013, f2=Name13
f1=12345678900000014, f2=Name14
f1=12345678900000015, f2=Name15
f1=12345678900000016, f2=Name16
f1=12345678900000004, f2=Name4
f1=12345678900000005, f2=Name5
f1=12345678900000006, f2=Name6
f1=12345678900000007, f2=Name7
f1=12345678900000008, f2=Name8

12.3 使用OTL tracing跟蹤OTL的方法調用

OTL可以通過宏定義打開內部的跟蹤機制,方便程序的調試。通過宏OTL_TRACE_LEVEL指明跟蹤的級別,通過宏OTL_TRACE_STREAM指明跟蹤消息的輸出流,通過宏OTL_TRACE_LINE_PREFIX指明跟蹤消息的輸出頭。

以下是使用OTL tracing來跟蹤OTL方法調用的示例代碼。

  1 #include <iostream>
  2 using namespace std;
  3 #include <stdio.h>
  4 unsigned int my_trace_level=
  5    0x1 | // 1st level of tracing
  6    0x2 | // 2nd level of tracing
  7    0x4 | // 3rd level of tracing
  8    0x8 | // 4th level of tracing
  9    0x10; // 5th level of tracing
 10 // each level of tracing is represented by its own bit, 
 11 // so levels of tracing can be combined in an arbitrary order.
 12 
 13 #define OTL_TRACE_LEVEL my_trace_level
 14    // enables OTL tracing, and uses my_trace_level as a trace control variable.
 15 
 16 #define OTL_TRACE_STREAM cerr 
 17    // directs all OTL tracing to cerr
 18 
 19 #define OTL_TRACE_LINE_PREFIX "MY OTL TRACE ==> " 
 20    // redefines the default OTL trace line prefix. This #define is optional
 21 
 22 #define OTL_ORA9I // Compile OTL 4.0/OCI9i
 23 // #define OTL_ORA8I // Compile OTL 4.0/OCI8i
 24 // #define OTL_ORA8 // Compile OTL 4.0/OCI8
 25 #include <otlv4.h> // include the OTL 4.0 header file
 26 
 27 otl_connect db; // connect object
 28 
 29 void insert()
 30 // insert rows into table
 31 { 
 32  otl_stream o(10, // buffer size
 33               "insert into test_tab values(:f1<int>,:f2<char[31]>)", // SQL statement
 34               db // connect object
 35              );
 36  char tmp[32];
 37 
 38  for(int i=1;i<=23;++i){
 39   sprintf(tmp,"Name%d",i);
 40   o<<i<<tmp;
 41  }
 42 }
 43 
 44 void select()
 45 { 
 46  otl_stream i(5, // buffer size
 47               "select * from test_tab where f1>=:f<int> and f1<=:ff<int>*2",
 48                  // SELECT statement
 49               db // connect object
 50              ); 
 51    // create select stream
 52  
 53  float f1;
 54  char f2[31];
 55 
 56  i<<8<<8; // assigning :f = 8; :ff = 8
 57    // SELECT automatically executes when all input variables are
 58    // assigned. First portion of output rows is fetched to the buffer
 59 
 60  while(!i.eof()){ // while not end-of-data
 61   i>>f1>>f2;
 62   cout<<"f1="<<f1<<", f2="<<f2<<endl;
 63  }
 64 
 65 }
 66 
 67 int main()
 68 {
 69  otl_connect::otl_initialize(); // initialize OCI environment
 70  try{
 71 
 72   db.rlogon("scott/tiger"); // connect to the database
 73 
 74   otl_cursor::direct_exec
 75    (
 76     db,
 77     "drop table test_tab",
 78     otl_exception::disabled // disable OTL exceptions
 79    ); // drop table
 80 
 81   otl_cursor::direct_exec
 82    (
 83     db,
 84     "create table test_tab(f1 int, f2 varchar(30))"
 85     );  // create table
 86 
 87   insert(); // insert records into table
 88   select(); // select records from table
 89 
 90  }
 91  catch(otl_exception& p){ // intercept OTL exceptions
 92   cerr<<p.msg<<endl; // print out error message
 93   cerr<<p.stm_text<<endl; // print out SQL that caused the error
 94   cerr<<p.var_info<<endl; // print out the variable that caused the error
 95  }
 96 
 97  db.logoff(); // disconnect from the database
 98 
 99  return 0;
100 }

 

輸出結果:

MY OTL TRACE ==> otl_connect(this=004332D4)::rlogon(connect_str="scott/*****", auto_commit=0); 
MY OTL TRACE ==> otl_cursor::direct_exec(connect=004332CC,sqlstm="drop table test_tab",exception_enabled=0);
MY OTL TRACE ==> otl_cursor::direct_exec(connect=004332CC,sqlstm="create table test_tab(f1 int, f2 varchar(30))",exception_enabled=1);
MY OTL TRACE ==> otl_stream(this=0012FEFC)::open(buffer_size=10, sqlstm=insert into test_tab values(:f1<int>,:f2<char[31]>), connect=004332CC); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=1); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name1"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=2); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name2"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=3); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name3"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=4); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name4"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=5); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name5"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=6); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name6"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=7); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name7"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=8); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name8"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=9); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name9"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=10); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name10"); 
MY OTL TRACE ==> otl_stream, executing SQL Stm=insert into test_tab values(:f1     ,:f2          ), current batch size=10, row offset=0
MY OTL TRACE ==> otl_connect(this=004332CC)::commit(); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=11); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name11"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=12); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name12"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=13); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name13"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=14); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name14"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=15); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name15"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=16); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name16"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=17); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name17"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=18); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name18"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=19); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name19"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=20); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name20"); 
MY OTL TRACE ==> otl_stream, executing SQL Stm=insert into test_tab values(:f1     ,:f2          ), current batch size=10, row offset=0
MY OTL TRACE ==> otl_connect(this=004332CC)::commit(); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=21); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name21"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=22); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name22"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f1, value=23); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(char*: ftype=1, placeholder=:f2, value="Name23"); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::close(); 
MY OTL TRACE ==> otl_stream, executing SQL Stm=insert into test_tab values(:f1     ,:f2          ), current batch size=3, row offset=0
MY OTL TRACE ==> otl_connect(this=004332CC)::commit(); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::open(buffer_size=5, sqlstm=select * from test_tab where f1>=:f<int> and f1<=:ff<int>*2, connect=004332CC); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:f, value=8); 
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator <<(int: ftype=4, placeholder=:ff, value=8); 
MY OTL TRACE ==> otl_stream, executing SQL Stm=select * from test_tab where f1>=:f      and f1<=:ff     *2, buffer size=5
MY OTL TRACE ==> otl_stream, fetched the first batch of rows, SQL Stm=select * from test_tab where f1>=:f      and f1<=:ff     *2, RPC=5
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(float& : ftype=2, placeholder=F1, value=8);
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(char* : ftype=1, placeholder=F2, value="Name8");
f1=8, f2=Name8
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(float& : ftype=2, placeholder=F1, value=9);
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(char* : ftype=1, placeholder=F2, value="Name9");
f1=9, f2=Name9
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(float& : ftype=2, placeholder=F1, value=10);
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(char* : ftype=1, placeholder=F2, value="Name10");
f1=10, f2=Name10
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(float& : ftype=2, placeholder=F1, value=11);
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(char* : ftype=1, placeholder=F2, value="Name11");
f1=11, f2=Name11
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(float& : ftype=2, placeholder=F1, value=12);
MY OTL TRACE ==> otl_stream, fetched the next batch of rows, SQL Stm=select * from test_tab where f1>=:f      and f1<=:ff     *2, RPC=9
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(char* : ftype=1, placeholder=F2, value="Name12");
f1=12, f2=Name12
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(float& : ftype=2, placeholder=F1, value=13);
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(char* : ftype=1, placeholder=F2, value="Name13");
f1=13, f2=Name13
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(float& : ftype=2, placeholder=F1, value=14);
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(char* : ftype=1, placeholder=F2, value="Name14");
f1=14, f2=Name14
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(float& : ftype=2, placeholder=F1, value=15);
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(char* : ftype=1, placeholder=F2, value="Name15");
f1=15, f2=Name15
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(float& : ftype=2, placeholder=F1, value=16);
MY OTL TRACE ==> otl_stream(this=0012FEFC)::operator >>(char* : ftype=1, placeholder=F2, value="Name16");
f1=16, f2=Name16
MY OTL TRACE ==> otl_stream(this=0012FEFC)::close(); 
MY OTL TRACE ==> otl_connect(this=004332CC)::logoff(); 

12.4 獲取已處理行數(Rows Processed Count)

otl_stream提供了get_rpc()方法(參見4.1小節)獲取已處理行數。另外,類otl_cursor的direct_exec()方法如果處理成功也返回已處理行數。

已處理行數和INSERT、UPDATE以及DELETE語句有關。對於INSERT語句而言,已處理行數可能小於等於流的緩沖區大小。對於UPDATE以及DELETE而言,則和多少行被更新或刪除相關。

以下是獲取已處理行數的示例代碼。

 1 #include <iostream>
 2 using namespace std;
 3   
 4 #include <stdio.h>
 5 #define OTL_ORA8 // Compile OTL 4.0/OCI8
 6 #include <otlv4.h> // include the OTL 4.0 header file
 7 
 8 otl_connect db; // connect object
 9 
10 void insert()
11 // insert rows into table
12 { 
13  otl_stream o(200, // buffer size
14               "insert into test_tab values(:f1<float>,:f2<char[31]>)", 
15                  // SQL statement
16               db // connect object
17              );
18  char tmp[32];
19 
20  for(int i=1;i<=123;++i){
21   sprintf(tmp,"Name%d",i);
22   o<<(float)i<<tmp;
23  }
24  o.flush();
25 
26  cout<<"Rows inserted: "<<o.get_rpc()<<endl;
27 
28 }
29 
30 void delete_rows()
31 { 
32   long rpc=otl_cursor::direct_exec(db,"delete from test_tab where f1>=95");
33   
34   cout<<"Rows deleted: "<<rpc<<endl;
35 }
36 
37 int main()
38 {
39  otl_connect::otl_initialize(); // initialize OCI environment
40  try{
41 
42   db.rlogon("scott/tiger"); // connect to Oracle
43 
44   otl_cursor::direct_exec
45    (
46     db,
47     "drop table test_tab",
48     otl_exception::disabled // disable OTL exceptions
49    ); // drop table
50 
51   otl_cursor::direct_exec
52    (
53     db,
54     "create table test_tab(f1 number, f2 varchar2(30))"
55     );  // create table
56 
57   insert(); // insert records into table
58   delete_rows(); // select records from table
59 
60  }
61 
62  catch(otl_exception& p){ // intercept OTL exceptions
63   cerr<<p.msg<<endl; // print out error message
64   cerr<<p.stm_text<<endl; // print out SQL that caused the error
65   cerr<<p.var_info<<endl; // print out the variable that caused the error
66  }
67 
68  db.logoff(); // disconnect from Oracle
69 
70  return 0;
71 
72 }

 

輸出結果:

Rows inserted: 123
Rows deleted: 29

12.5 使用otl_connect的重載運算符<<, <<=, >>

otl_connect重載了<<, <<=和>>運算符(參見4.2小節),用於簡化編程操作。其中操作符<<發送字符串到otl_connect對象。如果該otl_connect對象還沒有連接到數據庫則字符串為"userid/passwd@db"格式的連接字符串,它使得otl_connect對象能夠立刻連接數據庫。如果該otl_connect對象已經連接到數據庫則字符串被當作為靜態SQL語句立刻執行。

操作符<<=發送字符串到otl_connect對象。otl_connect對象將保存該字符串並被下一個>>操作符使用。該字符串是一個擁有占位符並且能夠發送到otl_stream對象的SQL語句。操作符>>取出之前使用操作符<<=保存的SQL語句並則發送到otl_stream對象。它使得該SQL語句被otl_stream打開。

以下是使用otl_connect重載的<<, <<=和>>運算符示例代碼。

  1 #include <iostream>
  2 using namespace std;
  3 
  4 #include <stdio.h>
  5 
  6 // #define OTL_ORA8 // Compile OTL 4.0/OCI8
  7 // #define OTL_ORA8I // Compile OTL 4.0/OCI8i
  8 // #define OTL_ORA9I // Compile OTL 4.0/OCI9I
  9 // #define OTL_ORA10G // Compile OTL 4.0/OCI10g
 10 #define OTL_ORA10G_R2 // Compile OTL 4.0/OCI10gR2
 11 #include <otlv4.h> // include the OTL 4.0 header file
 12 
 13 otl_connect db; // connect object
 14 
 15 void insert()
 16 // insert rows into table
 17 { 
 18  otl_stream o;
 19  o.setBufSize(50);
 20 
 21 // Send a message (SQL statement) to the otl_connect object.
 22  db<<="insert into test_tab values(:f1<int>,:f2<char[31]>)";
 23 
 24 // Send a message (SQL statement) from the connect object 
 25 // to the otl_stream object. 
 26 
 27  db>>o;
 28 
 29 // By and large, this is all syntactical sugar, but "some like it hot".
 30 
 31  char tmp[32];
 32 
 33  for(int i=1;i<=100;++i){
 34   sprintf(tmp,"Name%d",i);
 35   o<<i<<tmp;
 36  }
 37 }
 38 
 39 void select()
 40 { 
 41  otl_stream i;
 42 
 43  i.setBufSize(50);
 44 
 45 // Send a message (SQL statement) to the otl_connect object.
 46  db<<="select * from test_tab where f1>=:f11<int> and f1<=:f12<int>*2";
 47 
 48 // Send a message (SQL statement) from the connect object 
 49 // to the otl_stream object. 
 50 
 51  db>>i;
 52 
 53 // By and large, this is all syntactical sugar, but "some like it hot".
 54  
 55  int f1;
 56  char f2[31];
 57 
 58  i<<8<<8; // assigning :f11 = 8, :f12 = 8
 59    // SELECT automatically executes when all input variables are
 60    // assigned. First portion of output rows is fetched to the buffer
 61 
 62  while(!i.eof()){ // while not end-of-data
 63   i>>f1>>f2;
 64   cout<<"f1="<<f1<<", f2="<<f2<<endl;
 65  }
 66 
 67  i<<4<<4; // assigning :f11 = 8, :f12 = 8
 68    // SELECT automatically executes when all input variables are
 69    // assigned. First portion of output rows is fetched to the buffer
 70 
 71  while(!i.eof()){ // while not end-of-data
 72   i>>f1>>f2;
 73   cout<<"f1="<<f1<<", f2="<<f2<<endl;
 74  }
 75 
 76 }
 77 
 78 int main()
 79 {
 80  otl_connect::otl_initialize(); // initialize the database API environment
 81  try{
 82 
 83   db<<"scott/tiger";// connect to the database
 84 
 85   // Send SQL statements to the connect obejct for immediate execution. 
 86   // Ignore any exception for the first statement.
 87   try{ db<<"drop table test_tab"; } catch(otl_exception&){}
 88   db<<"create table test_tab(f1 int, f2 varchar(30))";
 89 
 90   insert(); // insert records into table
 91   select(); // select records from table
 92 
 93  }
 94 
 95  catch(otl_exception& p){ // intercept OTL exceptions
 96   cerr<<p.msg<<endl; // print out error message
 97   cerr<<p.stm_text<<endl; // print out SQL that caused the error
 98   cerr<<p.var_info<<endl; // print out the variable that caused the error
 99  }
100 
101  db.logoff(); // disconnect from the database
102 
103  return 0;
104 
105 }

 

輸出結果:

f1=8, f2=Name8
f1=9, f2=Name9
f1=10, f2=Name10
f1=11, f2=Name11
f1=12, f2=Name12
f1=13, f2=Name13
f1=14, f2=Name14
f1=15, f2=Name15
f1=16, f2=Name16
f1=4, f2=Name4
f1=5, f2=Name5
f1=6, f2=Name6
f1=7, f2=Name7
f1=8, f2=Name8

12.6 手工刷新otl_stream緩沖區

otl_stream提供了方法set_flush()方法(見4.1小節)設置auto_flush標志。該標志僅僅控制析構函數中由於輸出緩沖區數據變臟引起的刷新,並不能控制緩沖區填滿引起自動刷新。auto_flush標志默認為true即進行刷新。

提供set_flush()方法控制otl_stream析構函數中的刷新操作和C++的異常機制有關。C++異常機制退出作用域時,局部對象會由於堆棧回退發生析構。如果在析構函數中又產生異常將導致std::terminate()被調用,從而使程序中止。

otl_stream析構函數中的輸出緩沖區刷新在某些情況下極有可能導致這種級聯異常發生,因此otl_stream提供了set_flush()方法進行控制。如果auto_flush被設置成false,則必須手工調用otl_stream的flush()或close()方法進行刷新。當然,前提是輸出緩沖區還沒有填滿,因為緩沖區填滿的自動刷新是不能通過set_flush()控制的。

以下是手工刷新otl_stream緩沖區的示例代碼。

  1 #include <iostream>
  2 #include <stdio.h>
  3 
  4 // Uncomment the line below when OCI7 is used with OTL
  5 // #define OTL_ORA7 // Compile OTL 4.0/OCI7 
  6 
  7 
  8 using namespace std;
  9 
 10 #define OTL_ORA8 // Compile OTL 4.0/OCI8
 11 #include <otlv4.h> // include the OTL 4.0 header file
 12 
 13 otl_connect db; // connect object
 14 
 15 void insert1()
 16 // insert rows into table
 17 {
 18     otl_stream o; // define an otl_stream variable
 19 
 20     o.set_flush(false); // set the auto-flush flag to OFF.
 21 
 22     o.open(200, // buffer size
 23         "insert into test_tab values(:f1<float>,:f2<char[31]>)", // SQL statement
 24         db // connect object
 25     );
 26     char tmp[32];
 27     for (int i = 1; i <= 100; ++i) {
 28         sprintf(tmp, "Name%d", i);
 29         o << (float)i << tmp;
 30         if (i % 55 == 0)
 31             throw "Throwing an exception";
 32     }
 33 
 34     o.flush();  // when the auto-flush flag is OFF, an explicit flush
 35                 // of the stream buffer is required in case of successful
 36                 // completion of execution of the INSERT statement.
 37                 // In case of a raised exception, the stream buffer would not be flushed.          
 38 }
 39 
 40 void insert2()
 41 // insert rows into table
 42 {
 43     otl_stream o; // define an otl_stream variable
 44 
 45     o.set_flush(false); // set the auto-flush flag to OFF.
 46 
 47     o.open(200, // buffer size
 48         "insert into test_tab values(:f1<float>,:f2<char[31]>)", // SQL statement
 49         db // connect object
 50     );
 51     char tmp[32];
 52 
 53     for (int i = 1; i <= 100; ++i) {
 54         sprintf(tmp, "Name%d", i);
 55         o << (float)i << tmp;
 56         //if(i%55==0)
 57         //   throw "Throwing an exception";
 58     }
 59     o.flush();   // when the auto-flush flag is OFF, an explicit flush
 60                  // of the stream buffer is required in case of successful
 61                  // completion of execution of the INSERT statement.
 62                  // In case of a raised exception, the stream buffer would not be flushed.          
 63 }
 64 
 65 void select()
 66 {
 67     otl_stream i(50, // buffer size
 68         "select * from test_tab where f1>=:f<int> and f1<=:f*2",
 69         // SELECT statement
 70         db // connect object
 71     );
 72     // create select stream
 73 
 74     float f1;
 75     char f2[31];
 76 
 77     i << 8; // assigning :f = 8
 78             // SELECT automatically executes when all input variables are
 79             // assigned. First portion of output rows is fetched to the buffer
 80 
 81     while (!i.eof()) { // while not end-of-data
 82         i >> f1 >> f2;
 83         cout << "f1=" << f1 << ", f2=" << f2 << endl;
 84     }
 85 
 86     i << 4; // assigning :f = 4
 87             // SELECT automatically executes when all input variables are
 88             // assigned. First portion of output rows is fetched to the buffer
 89 
 90     while (!i.eof()) { // while not end-of-data
 91         i >> f1 >> f2;
 92         cout << "f1=" << f1 << ", f2=" << f2 << endl;
 93     }
 94 
 95 }
 96 
 97 int main()
 98 {
 99     otl_connect::otl_initialize(); // initialize OCI environment
100     try {
101 
102         db.rlogon("scott/tiger"); // connect to Oracle
103 
104         otl_cursor::direct_exec
105         (
106             db,
107             "drop table test_tab",
108             otl_exception::disabled // disable OTL exceptions
109         ); // drop table
110 
111         otl_cursor::direct_exec
112         (
113             db,
114             "create table test_tab(f1 number, f2 varchar2(30))"
115         ); // create table
116 
117         try {
118             insert1(); // insert records into table
119         }
120         catch (const char* p) {
121             cout << p << endl;
122         }
123         cout << "Selecting the first time around:" << endl;
124         select(); // select records from table
125 
126         insert2();
127         cout << "Selecting the second time around:" << endl;
128         select();
129 
130     }
131 
132     catch (otl_exception& p) { // intercept OTL exceptions
133         cerr << p.msg << endl; // print out error message
134         cerr << p.stm_text << endl; // print out SQL that caused the error
135         cerr << p.var_info << endl; // print out the variable that caused the error
136     }
137 
138     db.logoff(); // disconnect from Oracle
139 
140     return 0;
141 }

 

輸出結果:

Throwing an exception
Selecting the first time around:
Selecting the second time around:
f1=8, f2=Name8
f1=9, f2=Name9
f1=10, f2=Name10
f1=11, f2=Name11
f1=12, f2=Name12
f1=13, f2=Name13
f1=14, f2=Name14
f1=15, f2=Name15
f1=16, f2=Name16
f1=4, f2=Name4
f1=5, f2=Name5
f1=6, f2=Name6
f1=7, f2=Name7
f1=8, f2=Name8

12.7 忽略INSERT操作時的重復鍵異常

底層操作為Oracle API時,otl_stream提供了特殊版本的flush()方法(參見4.1小節),該方法可以設置刷新的起始行以及忽略產生錯誤時拋出的otl_exception異常繼續刷新。在進行INSERT操作有時候需要忽略重復鍵值引起的錯誤繼續處理,這時可以利用該flush()方法進行處理。

以下是忽略INSERT操作時的重復鍵異常示例代碼。

  1 #include <iostream>
  2 using namespace std;
  3 
  4 #include <stdio.h>
  5 //#define OTL_ORA8I // Compile OTL 4.0/OCI8
  6 #define OTL_ORA8I // Compile OTL 4.0/OCI8i
  7 // #define OTL_ORA9I // Compile OTL 4.0/OCI9i
  8 #include <otlv4.h> // include the OTL 4.0 header file
  9 
 10 otl_connect db; // connect object
 11 
 12 void insert()
 13 // insert rows into table
 14 {
 15     otl_stream o(10, // make the buffer size larger than the actual 
 16                      // row set to inserted, so that the stream will not
 17                      // flush the buffer automatically
 18         "insert into test_tab values(:f1<int>,:f2<char[31]>)", // SQL statement
 19         db // connect object
 20     );
 21 
 22     o.set_commit(0); // set stream's auto-commit to OFF.
 23 
 24     long total_rpc = 0; // total "rows processed count"
 25     long rpc = 0; // rows successfully processed in one flush() call
 26     int iters = 0; // number of rows to be bypassed
 27 
 28     try {
 29         o << 1 << "Line1"; // Enter one row into the stream
 30         o << 1 << "Line1"; // Enter the same data into the stream
 31                            // and cause a "duplicate key" error.
 32         o << 2 << "Line2"; // Enter one row into the stream
 33         o << 3 << "Line3"; // Enter one row into the stream
 34         o << 4 << "Line4"; // Enter one row into the stream
 35         o << 4 << "Line4"; // Enter the same data into the stream
 36                            // and cause a "duplicate key" error.
 37         o.flush();
 38     }
 39     catch (otl_exception& p) {
 40         if (p.code == 1) { // ORA-0001: ...duplicate key...
 41             ++iters;
 42             rpc = o.get_rpc();
 43             total_rpc = rpc;
 44             do {
 45                 try {
 46                     cout << "TOTAL_RPC=" << total_rpc << ", RPC=" << rpc << endl;
 47                     o.flush(total_rpc + iters,// bypass the duplicate row and start
 48                                               // with the rows after that
 49                         true // force buffer flushing regardless
 50                     );
 51                     rpc = 0;
 52                 }
 53                 catch (otl_exception& p2) {
 54                     if (p2.code == 1) { // ORA-0001: ... duplicate key ...
 55                         ++iters;
 56                         rpc = o.get_rpc();
 57                         total_rpc += rpc;
 58                     }
 59                     else
 60                         throw;
 61                 }
 62             } while (rpc>0);
 63         }
 64         else
 65             throw; // re-throw the exception to the outer catch block.
 66     }
 67 
 68     db.commit(); // commit transaction
 69 
 70 }
 71 
 72 void select()
 73 {
 74     otl_stream i(10, // buffer size
 75         "select * from test_tab",
 76         // SELECT statement
 77         db // connect object
 78     );
 79     // create select stream
 80 
 81     int f1;
 82     char f2[31];
 83 
 84     while (!i.eof()) { // while not end-of-data
 85         i >> f1 >> f2;
 86         cout << "f1=" << f1 << ", f2=" << f2 << endl;
 87     }
 88 
 89 }
 90 
 91 int main()
 92 {
 93     otl_connect::otl_initialize(); // initialize OCI environment
 94     try {
 95 
 96         db.rlogon("scott/tiger"); // connect to Oracle
 97 
 98         otl_cursor::direct_exec
 99         (
100             db,
101             "drop table test_tab",
102             otl_exception::disabled // disable OTL exceptions
103         ); // drop table
104 
105         otl_cursor::direct_exec
106         (
107             db,
108             "create table test_tab(f1 number, f2 varchar2(30))"
109         );  // create table
110 
111         otl_cursor::direct_exec
112         (
113             db,
114             "create unique index ind001 on test_tab(f1)"
115         );  // create unique index 
116 
117         insert(); // insert records into table
118         select(); // select records from table
119 
120     }
121 
122     catch (otl_exception& p) { // intercept OTL exceptions
123         cerr << p.msg << endl; // print out error message
124         cerr << p.stm_text << endl; // print out SQL that caused the error
125         cerr << p.var_info << endl; // print out the variable that caused the error
126     }
127 
128     db.logoff(); // disconnect from Oracle
129 
130     return 0;
131 
132 }

 

輸出結果:

TOTAL_RPC=1, RPC=1
TOTAL_RPC=4, RPC=3
f1=1, f2=Line1
f1=2, f2=Line2
f1=3, f2=Line3
f1=4, f2=Line4

12.8 使用模板otl_value<T>創建數據容器

otl_value<T>是一個OTL模板類,能夠基於標量的數據類型包括int, unsigned, long, short, float, double, otl_datetime(OTL date&time container), std::string (STL string class)來創建派生的數據容器。

派生的數據容器具有內建的NULL指示器功能,即容器內部擁有一個NULL指示器域並且能夠從一個操作傳遞到另一個操作。例如從SELECT語句讀取並裝入容器otl_value<int>的值是NULL,在該值被寫入INSERT語句后,otl_value<int>容器中的NULL指示器域仍然保存該NULL值並且NULL值也從SELECT傳遞到了INSERT。

模板otl_value<T>的使用可以通過”#define OTL_STL”或者”#define OTL_VALUE_TEMPLATE_ON”激活。其定義如下:

 1 template<class TData> 
 2 
 3 class otl_value{
 4 
 5 public:
 6 
 7      TData v;
 8 
 9      bool ind;
10 
11  
12 
13      otl_value(); // default constructor
14 
15  
16 
17      otl_value(const otl_value<TData>& var); // copy constructor
18 
19      otl_value(const TData& var); // copy constructor
20 
21      otl_value(const otl_null& var); // copy constructor
22 
23  
24 
25      otl_value<TData>& operator=(const otl_value<TData>& var); //assignment operator
26 
27      otl_value<TData>& operator=(const TData& var); // assignment operator
28 
29      otl_value<TData>& operator=(const otl_null& var); // assignment operator
30 
31  
32 
33      bool is_null(void) const;
34 
35      void set_null(void);
36 
37      void set_non_null(void);
38 
39 }; // end of otl_value
40 
41  
42 
43 template <class TData>
44 
45 otl_stream& operator<<(otl_stream& s, const otl_value<TData>& var);
46 
47  
48 
49 template <class TData>
50 
51 otl_stream& operator>>(otl_stream& s, otl_value<TData>& var);
52 
53  
54 
55 template <class TData> std::ostream&
56 
57 operator<<(std::ostream& s, const otl_value<TData>& var);

 

其中方法set_null()和set_non_null()將otl_value設置成NULL或者非NULL,這兩個方法必須直接操作public數據成員TData v或bool ind的時候才使用。數據成員v為標量數據類型值的存放容器,成員ind則為NULL指示器。

以下為使用otl_value<T>模板的示例代碼。

  1 #define OTL_ORA8 // Compile OTL 4.0/OCI8
  2 #define OTL_STL // Turn on STL features and otl_value<T>
  3 #define OTL_ANSI_CPP // Turn on ANSI C++ typecasts
  4 #include <otlv4.h> // include the OTL 4.0 header file
  5  
  6 using namespace std;
  7  
  8 otl_connect db; // connect object
  9  
 10 void insert()
 11 // insert rows into table
 12 { 
 13  otl_stream o(50, // buffer size
 14               "insert into test_tab "
 15               "values(:f1<int>,:f2<char[31]>,:f3<timestamp>)", 
 16                  // SQL statement
 17               db // connect object
 18              );
 19  
 20  otl_value<string> f2; // otl_value container of STL string
 21  otl_value<otl_datetime> f3; // container of otl_datetime
 22  
 23  
 24  for(int i=1;i<=100;++i){
 25  
 26   if(i%2==0)
 27      f2="NameXXX";
 28   else
 29      f2=otl_null(); // Assign otl_null() to f2
 30  
 31   if(i%3==0){
 32      // Assign a value to f3 via the .v field directly
 33      f3.v.year=2001;
 34      f3.v.month=1;
 35      f3.v.day=1;
 36      f3.v.hour=13;
 37      f3.v.minute=10;
 38      f3.v.second=5;
 39      f3.set_non_null(); // Set f3 as a "non-NULL"
 40   }else
 41      f3.set_null(); // Set f3 as null via .set_null() function
 42  
 43   o<<i<<f2<<f3;
 44  
 45  }
 46 }
 47  
 48 void select()
 49 { 
 50  otl_stream i(50, // buffer size
 51               "select * from test_tab where f1>=:f<int> and f1<=:f*2",
 52                  // SELECT statement
 53               db // connect object
 54              ); 
 55    // create select stream
 56  
 57  int f1; 
 58  otl_value<string> f2;
 59  otl_value<otl_datetime> f3;
 60  
 61  
 62  i<<8; // assigning :f = 8
 63    // SELECT automatically executes when all input variables are
 64    // assigned. First portion of output rows is fetched to the buffer
 65  
 66  while(!i.eof()){ // while not end-of-data
 67     i>>f1>>f2>>f3;
 68     cout<<"f1="<<f1<<", f2="<<f2<<", f3="<<f3<<endl;
 69  }
 70  
 71  i<<4; // assigning :f = 4
 72    // SELECT automatically executes when all input variables are
 73    // assigned. First portion of output rows is fetched to the buffer
 74  
 75  while(!i.eof()){ // while not end-of-data
 76   i>>f1>>f2>>f3;
 77   cout<<"f1="<<f1<<", f2="<<f2<<", f3="<<f3<<endl;
 78  }
 79  
 80 }
 81  
 82 int main()
 83 {
 84  otl_connect::otl_initialize(); // initialize OCI environment
 85  try{
 86  
 87   db.rlogon("scott/tiger"); // connect to Oracle
 88  
 89   otl_cursor::direct_exec
 90    (
 91     db,
 92     "drop table test_tab",
 93     otl_exception::disabled // disable OTL exceptions
 94    ); // drop table
 95  
 96   otl_cursor::direct_exec
 97    (
 98     db,
 99     "create table test_tab(f1 number, f2 varchar2(30), f3 date)"
100     );  // create table
101  
102   insert(); // insert records into table
103   select(); // select records from table
104  
105  }
106  
107  catch(otl_exception& p){ // intercept OTL exceptions
108   cerr<<p.msg<<endl; // print out error message
109   cerr<<p.stm_text<<endl; // print out SQL that caused the error
110   cerr<<p.var_info<<endl; // print out the variable that caused the error
111  }
112  
113  db.logoff(); // disconnect from Oracle
114  
115  return 0;
116  
117 }

 

輸出結果:

f1=8, f2=NameXXX, f3=NULL
f1=9, f2=NULL, f3=1/1/2001 13:10:5
f1=10, f2=NameXXX, f3=NULL
f1=11, f2=NULL, f3=NULL
f1=12, f2=NameXXX, f3=1/1/2001 13:10:5
f1=13, f2=NULL, f3=NULL
f1=14, f2=NameXXX, f3=NULL
f1=15, f2=NULL, f3=1/1/2001 13:10:5
f1=16, f2=NameXXX, f3=NULL
f1=4, f2=NameXXX, f3=NULL
f1=5, f2=NULL, f3=NULL
f1=6, f2=NameXXX, f3=1/1/2001 13:10:5
f1=7, f2=NULL, f3=NULL
f1=8, f2=NameXXX, f3=NULL

12.9 使用OTL流的讀迭代器遍歷流返回的Reference Cursor

otl_stream的構造函數和open()方法(參見4.1小節)中參數sqlstm 可以為SQL, 也可以為PL/SQL塊或者存儲過程調用,如果流返回reference cursor,則需要在參數ref_cur_placeholder中指明占位符號。

以下是使用使用OTL流的讀迭代器遍歷OTL流返回的reference cursor的示例代碼。

  1 #include <iostream>
  2 using namespace std;
  3 
  4 #include <stdio.h>
  5 
  6 // #define OTL_ORA7 // Compile OTL 4.0/OCI7
  7 //#define OTL_ORA8 // Compile OTL 4.0/OCI8
  8 //#define OTL_ORA8I // Compile OTL 4.0/OCI8i
  9 #define OTL_ORA9I // Compile OTL 4.0/OCI9i
 10 //#define OTL_ORA10G // Compile OTL 4.0/OCI10g
 11 #define OTL_STREAM_READ_ITERATOR_ON
 12 #include <otlv4.h> // include the OTL 4.0 header file
 13 
 14 otl_connect db; // connect object
 15 
 16 void insert()
 17 // insert rows into table
 18 { 
 19  otl_stream o(50, // buffer size
 20               "insert into test_tab values(:f1<int>,:f2<char[31]>)", // SQL statement
 21               db // connect object
 22              );
 23  char tmp[32];
 24 
 25  for(int i=1;i<=100;++i){
 26   sprintf(tmp,"Name%d",i);
 27   o<<i<<tmp;
 28  }
 29 }
 30 
 31 void select()
 32 { 
 33  otl_stream i(50, // buffer size
 34               "begin "
 35 
 36              "   open :cur1 for "
 37 
 38              "   select * from test_tab "
 39 
 40            "   where f1>=:f11<int> "
 41 
 42               "     and f1<=:f12<int>*2; "
 43 
 44            "end;", // SELECT statement
 45               db, // connect object
 46               ":cur1"
 47              ); 
 48    // create select stream
 49  
 50  int f1;
 51  char f2[31];
 52  otl_stream_read_iterator<otl_stream,otl_exception,otl_lob_stream> rs;
 53 
 54  i<<8<<8; // assigning :f11 = 8, :f12 = 8
 55    // SELECT automatically executes when all input variables are
 56    // assigned. First portion of output rows is fetched to the buffer
 57 
 58  rs.attach(i); // attach the iterator "rs" to the stream "i"
 59                // after the PL/SQL block is executed.
 60  while(rs.next_row()){ // while not end-of-data
 61     rs.get(1,f1);
 62     rs.get(2,f2);
 63     cout<<"f1="<<f1<<", f2="<<f2<<endl;
 64  }
 65 
 66  rs.detach(); // detach the itertor from the stream
 67 
 68  i<<4<<4; // assigning :f11 = 4, :f12 = 4
 69    // SELECT automatically executes when all input variables are
 70    // assigned. First portion of output rows is fetched to the buffer
 71 
 72  while(!i.eof()){ // while not end-of-data
 73     i>>f1>>f2;
 74     cout<<"f1="<<f1<<", f2="<<f2<<endl;
 75  }
 76 
 77 }
 78 
 79 int main()
 80 {
 81  otl_connect::otl_initialize(); // initialize OCI environment
 82  try{
 83 
 84   db.rlogon("scott/tiger"); // connect to Oracle
 85 
 86   otl_cursor::direct_exec
 87    (
 88     db,
 89     "drop table test_tab",
 90     otl_exception::disabled // disable OTL exceptions
 91    ); // drop table
 92 
 93   otl_cursor::direct_exec
 94    (
 95     db,
 96     "create table test_tab(f1 number, f2 varchar2(30))"
 97     );  // create table
 98 
 99   insert(); // insert records into table
100   select(); // select records from table
101 
102  }
103 
104  catch(otl_exception& p){ // intercept OTL exceptions
105   cerr<<p.msg<<endl; // print out error message
106   cerr<<p.stm_text<<endl; // print out SQL that caused the error
107   cerr<<p.var_info<<endl; // print out the variable that caused the error
108  }
109 
110  db.logoff(); // disconnect from Oracle
111 
112  return 0;
113 
114 }

 

輸出結果:

f1=8, f2=Name8
f1=9, f2=Name9
f1=10, f2=Name10
f1=11, f2=Name11
f1=12, f2=Name12
f1=13, f2=Name13
f1=14, f2=Name14
f1=15, f2=Name15
f1=16, f2=Name16
f1=4, f2=Name4
f1=5, f2=Name5
f1=6, f2=Name6
f1=7, f2=Name7
f1=8, f2=Name8

12.10使用Reference Cursor流從存儲過程中返回多個Referece Cursor

otl_refcur_stream和reference cursor類型的占位符聯合,允許在存儲過程調用中返回多個reference cursor。以下為示例代碼。

  1 #include <iostream>
  2 using namespace std;
  3 
  4 #include <stdio.h>
  5 
  6 #define OTL_ORA8 // Compile OTL 4.0/OCI8
  7 //#define OTL_ORA8I // Compile OTL 4.0/OCI8i
  8 //#define OTL_ORA9I // Compile OTL 4.0/OCI9i
  9 #include <otlv4.h> // include the OTL 4.0 header file
 10 
 11 otl_connect db; // connect object
 12 
 13 void insert()
 14 // insert rows into table
 15 {
 16     otl_stream o(50, // buffer size
 17         "insert into test_tab values(:f1<int>,:f2<char[31]>)", // SQL statement
 18         db // connect object
 19     );
 20     char tmp[32];
 21 
 22     for (int i = 1; i <= 100; ++i) {
 23         sprintf(tmp, "Name%d", i);
 24         o << i << tmp;
 25     }
 26 }
 27 
 28 void select()
 29 {
 30 
 31     // :str1 is an output string parameter
 32     // :cur1, :cur2 are a bind variable names, refcur -- their types, 
 33     // out -- output parameter, 50 -- the buffer size when this
 34     // reference cursor will be attached to otl_refcur_stream
 35     otl_stream i(1,// buffer size
 36         "begin "
 37         " my_pkg.my_proc(:f1<int,in>,:f2<int,in>, "
 38         "         :str1<char[100],out>, "
 39         "         :cur1<refcur,out[50]>, "
 40         "         :cur2<refcur,out[50]>); "
 41 
 42         "end;", // PL/SQL block that calls a stored procedure
 43         db // connect object
 44     );
 45 
 46 
 47     i.set_commit(0); // set stream "auto-commit" to OFF.
 48 
 49     char str1[101];
 50     float f1;
 51     char f2[31];
 52     otl_refcur_stream s1, s2; // reference cursor streams for reading rows.
 53 
 54     i << 8 << 4; // assigning :f1 = 8, :f2 = 4
 55     i >> str1; // reading :str1 from the stream
 56     i >> s1; // initializing the refeence cursor stream with the output 
 57 
 58              // reference cursor :cur1
 59     i >> s2;// initializing the refeence cursor stream with the output 
 60             // reference cursor :cur2
 61 
 62     cout << "STR1=" << str1 << endl;
 63     cout << "=====> Reading :cur1..." << endl;
 64     while (!s1.eof()) { // while not end-of-data
 65         s1 >> f1 >> f2;
 66         cout << "f1=" << f1 << ", f2=" << f2 << endl;
 67     }
 68 
 69     cout << "=====> Reading :cur2..." << endl;
 70     while (!s2.eof()) { // while not end-of-data
 71         s2 >> f1 >> f2;
 72         cout << "f1=" << f1 << ", f2=" << f2 << endl;
 73     }
 74 
 75     s1.close(); // closing the reference cursor
 76     s2.close(); // closing the reference cursor
 77 }
 78 
 79 int main()
 80 {
 81     otl_connect::otl_initialize(); // initialize OCI environment
 82     try {
 83 
 84         db.rlogon("scott/tiger"); // connect to Oracle
 85 
 86         otl_cursor::direct_exec
 87         (
 88             db,
 89             "drop table test_tab",
 90             otl_exception::disabled // disable OTL exceptions
 91         ); // drop table
 92 
 93         otl_cursor::direct_exec
 94         (
 95             db,
 96             "create table test_tab(f1 number, f2 varchar2(30))"
 97         );  // create table
 98 
 99             // create a PL/SQL package
100         otl_cursor::direct_exec
101         (db,
102             "CREATE OR REPLACE PACKAGE my_pkg IS "
103             " "
104             "  TYPE my_cursor IS REF CURSOR; "
105             " "
106             "  PROCEDURE my_proc "
107             "    (f1_in IN NUMBER, "
108             "     f2_in IN NUMBER, "
109             "     str1 OUT VARCHAR2, "
110             "     cur1 OUT my_cursor, "
111             "     cur2 OUT my_cursor); "
112             " "
113             "END; "
114         );
115 
116         otl_cursor::direct_exec
117         (db,
118             "CREATE OR REPLACE PACKAGE BODY my_pkg IS "
119             " "
120             "  PROCEDURE my_proc "
121             "    (f1_in IN NUMBER, "
122             "     f2_in IN NUMBER, "
123             "     str1 OUT VARCHAR2, "
124             "     cur1 OUT my_cursor, "
125             "     cur2 OUT my_cursor) "
126             "  IS "
127             "    lv_cur1 my_cursor; "
128             "    lv_cur2 my_cursor; "
129             "  BEGIN "
130             "    OPEN lv_cur1 FOR "
131             "      SELECT * FROM test_tab "
132             "      WHERE f1>=f1_in  "
133             "        AND f1<=f1_in*2; "
134             "    OPEN lv_cur2 FOR "
135             "      SELECT * FROM test_tab "
136             "      WHERE f1>=f2_in  "
137             "        AND f1<=f2_in*2; "
138             "    str1 := 'Hello, world'; "
139             "    cur1 := lv_cur1; "
140             "    cur2 := lv_cur2; "
141             "  END; "
142             "   "
143             "END; "
144         );
145 
146         insert(); // insert records into table
147         select(); // select records from table
148 
149     }
150 
151     catch (otl_exception& p) { // intercept OTL exceptions
152         cerr << p.msg << endl; // print out error message
153         cerr << p.stm_text << endl; // print out SQL that caused the error
154         cerr << p.var_info << endl; // print out the variable that caused the error
155     }
156 
157     db.logoff(); // disconnect from Oracle
158 
159     return 0;
160 
161 }

 

輸出結果:

STR1=Hello, world
=====> Reading :cur1...
f1=8, f2=Name8
f1=9, f2=Name9
f1=10, f2=Name10
f1=11, f2=Name11
f1=12, f2=Name12
f1=13, f2=Name13
f1=14, f2=Name14
f1=15, f2=Name15
f1=16, f2=Name16
=====> Reading :cur2...
f1=4, f2=Name4
f1=5, f2=Name5
f1=6, f2=Name6
f1=7, f2=Name7
f1=8, f2=Name8

13 最佳實踐

13.1流緩沖區大小的設置

OTL流的性能主要被緩沖區大小一個參數控制,在創建使用OTL流時必須通過構造函數或open()函數的第一個參數arr_size指定,合理設置流緩沖區大小對數據庫訪問的性能優化尤其重要。

與INSERT/DELETE/UPDATE語句不同,SELECT語句的執行往往導致若干滿足查詢條件的記錄行返回,OTL的底層實現將這兩類語句的操作執行區分開來,因此SELECT語句相關的流緩沖區在作用和使用方式上與其他語句存在很大的差別。

對於SELECT語句而言,緩沖區大小主要影響OTL底層調用OCI接口OCIStmtFetch()一次性獲取結果記錄的行數。其底層實現如以下代碼片斷所示。

 1 / **
 2 
 3  *OTL底層的OCI接口封裝類otl_cur的fetch()具體實現,其功能主要是
 4 
 5  *在執行SELECT語句后獲取返回的記錄行。
 6 
 7  *參數:iters 輸入參數,從當前位置處一次性提取的記錄數
 8 
 9  *      eof_date 輸入輸出參數,標識是否還有未獲取的記錄行,0為是,1為否
10 
11  */
12 
13 int fetch(const otl_stream_buffer_size_type iters, int& eof_data)
14 
15  {
16 
17         eof_data=0;
18 
19         status=OCIStmtFetch(
20 
21                cda,                              //語句句柄
22 
23                errhp,                             //錯誤句柄
24 
25                OTL_SCAST(ub4,iters),              //從當前位置處開始一次提取的記錄數
26 
27                OTL_SCAST(ub4,OCI_FETCH_NEXT), //提取方向
28 
29                OTL_SCAST(ub4,OCI_DEFAULT)     //模式
30 
31         );
32 
33  
34 
35         eof_status=status;
36 
37         if(status!=OCI_SUCCESS&&
38 
39                status!=OCI_SUCCESS_WITH_INFO&&
40 
41                status!=OCI_NO_DATA)
42 
43                return 0;
44 
45  
46 
47         //如果記錄被提取完或沒有數據則
48 
49         //輸入輸出參數eof_data賦值為1,返回1
50 
51         if(status==OCI_NO_DATA){
52 
53                eof_data=1;
54 
55                return 1;
56 
57         }
58 
59      return 1;
60 
61  }

 

由於緩沖區大小作為第一參數傳遞給fetch()方法進行底層的OCI接口調用,因此緩沖區大小決定了從當前位置處開始一次提取的記錄數。

對於查詢數據量較大的應用,緩沖區大小的合理設置往往是性能優化的關鍵。表13-1為查詢10000條記錄時的緩沖區大小對結果行獲取的影響。數據庫為Oracle10g, 操作系統為HP-UNIX。

13-1 查詢10000條記錄時緩沖區大小不同設置的相應耗時

緩存區大小

耗時(s)

1

0.25

10

0.14

50

0.09

100

0.06

200

0.05

300

0.05

1000

0.05

2000

0.04

備注:(1) 測試環境為HP, Oracle10g

(2) 耗時僅指SQL執行成功后,記錄的獲取時間

由該表可見,對於查詢結果為10000條記錄的SELECT語句而言,緩沖區大小的設置對結果行的獲取時間存在幾十倍的差距。因此在使用OTL流進行數據庫查詢時,請合理設置緩沖區大小。

注意對於SELECT語句來說,緩沖區的大小並不影響其執行時機。當otl_stream以SELECT語句實例化,並設定好緩沖區大小后,一旦所有聲明的綁定變量使用<<操作符被綁定完成,該SELECT語句將被OTL流立刻執行,與設置的緩存區具體大小並沒有關系。

與此相反,對於INSERT/DELETE/UPDATE語句,緩沖區大小的設置將影響其執行時機。當緩沖區被SQL填滿時,OTL流將自動刷新緩沖區,立刻批量執行緩沖區中所有的SQL。因此,當執行語句為INSERT/DELETE/UPDATE時,也應該根據需要合理設置緩沖區大小。

13.2批量操作注意的問題

當使用OTL的SQL變量綁定進行數據的批量操作時,應該注意otl_stream緩沖區大小的合理設置(參見13.1小節)以及OTL默認的語句執行成功后立刻提交事務。

對於批量操作,OTL默認的語句執行成功后立刻提交事務的操作方式往往不實用。例如對於批量更新10000條記錄,緩沖區大小為1000的情況,當緩沖區填滿時更新操作被執行,執行成功后當前事務被立刻提交,如果在此后的處理中出現錯誤,根本沒有辦法通過回滾事務取消之前的更新。防止語句執行后立刻提交事務的方法請參見12.1小節。

OTL流otl_stream 的這種默認操作方式,主要和底層調用OCI接口OCIStmtExecute()的相關實現有關。其底層實現如以下代碼片斷所示。

 1 / **
 2 
 3  *OTL底層的OCI接口封裝類otl_cur的exec ()具體實現,其功能主要是
 4 
 5  *在執行SELECT語句。
 6 
 7  *參數:iters 輸入參數,SQL語句執行次數
 8 
 9  *      rowoff  輸入參數,綁定變量的開始偏移量
10 
11  */
12 
13 int exec(const int iters, const int rowoff)
14 
15  {
16 
17         //如果模式為語句執行成功后立刻提交事務則
18 
19         //設置相應的局部變量mode值
20 
21         ub4 mode;
22 
23         if(commit_on_success)
24 
25               mode=OCI_COMMIT_ON_SUCCESS;
26 
27         else
28 
29               mode=OCI_DEFAULT;
30 
31  
32 
33         //調用OCI接口OCIStmtExecute()執行SQL語句
34 
35         status=OCIStmtExecute(
36 
37               db->svchp,
38 
39        cda,
40 
41               errhp,
42 
43               OTL_SCAST(ub4,iters),
44 
45               OTL_SCAST(ub4,rowoff),
46 
47               0,
48 
49               0,
50 
51               mode
52 
53        );
54 
55  
56 
57        //如果執行SQL語句錯誤則返回0
58 
59        if(status!=OCI_SUCCESS)
60 
61               return 0;
62 
63  
64 
65        return 1;
66 
67  }

 

由於otl_stream默認將語句執行成功立刻提交事務的開關打開,因此在exec()方法中調用OCI接口時,將使用該操作模式。

13.3與開源項目ORAPP的性能對比

ORAPP是一個基於C++語言的封裝了OCI函數,對Oracle數據庫進行專門訪問的類庫。表13-2所示的性能對比結果的測試環境為Linux, Oracle10g, OTL流緩沖區大小為1000, 可以看出當數據量劇增時OTL的性能明顯優越。

表1-2  ORAPP與OTL性能對比

數據量

OTL耗時(s)

ORAPP耗時(s)

1000

0.02

0.02

10000

0.03

0.21

100000

0.19

2.04

300000

0.56

5.77

       備注:(1) 測試環境為Linux, Oracle10g, OTL流緩沖區大小為1000

                   (2) 耗時包括SQL執行時間和記錄獲取時間的總和

究其原因,是由於OTL存在流緩沖區、流緩沖池等性能保證的機制,對於大數據量的查詢OTL可以通過設置緩沖池大小,指定每次底層OCI調用獲取的結果行,幾倍甚至幾十倍的提高性能。ORAPP則將每次獲取的記錄行數硬編碼為1,性能不能保證。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM