OTL之Oracle開發總結---轉
關 于OTL,網上介紹的也不少,但看來看去也隻是官方的那些文檔。OTL很好用,結合官方提供的一些例子,多多嘗試才能領悟。經過一個月左右的項目開發,對 OTL也有些了解,在這裡總結一下,希望對剛接觸OTL的新手有所幫助。其中有些地方,比如對網絡異常的處理,可是費了我兩天的時間才解決的,我那個汗 哪。廢話少說,開始正題:
一、開始前的準備工作
在使用OTL進行程式設計之前,要首先确定使用的Oralce版本以及所選用的字元集。OTL支援目前幾乎所有的主流資料庫,可以通過宏啟用otlv4.h中對應的資料庫操作接口。
如:使用oracle 11g R2、字元集選擇UTF8,則可在包含otlv4.h之前聲明以下兩個宏:
#define OTL_ORA11G_R2
#define OTL_ORA_UTF8
#include "otlv4.h"
....
二、常用類及其常用成員
1. otl_connect類
static int otl_initialize(const int threaded_mode=0):用于初始化OTL環境的靜态函數,參數指定是用于多線程還是單線程。它不保證線程安全,也就是說,如果多個線程共享使 用一個otl_connect對象,需要加鎖進行控制。有個同僚因為在多線程環境下使用了預設的參數0,就導緻了程式異常。但是單線程環境下,參數設1是 沒有問題的。是以,可以考慮将此參數直接設為1。
void rlogon(...):這個函數有多個版本,請注意參考官方文檔中與相應資料庫版本對應的函數聲明。11g中用到的參數說明如下:
const char *connect_str:連接配接字元串,格式為:"使用者名/密碼@資料庫服務名"
const int aauto_commit:自動送出模式。若此參數設為0(預設),則通過此連接配接對象執行的事務不會自動送出。如使用direct_exec執行删除記錄 的操作時,需要手動調用commit()成員函數送出事務;若設為1則通過此otl_connect對象開啟的事務會自動送出。
long direct_exec(...):
const char *sqlstm: 指定所要執行的“靜态SQL語句”,即不産生輸入或輸出的SQL語句。如delete from book where name='c++‘ 。但是不能執行如select sysdate from dual或select * from book亦或delete from book where name=:f1<char[20]>之類的語句,因為它們會帶有輸入或輸出,此類SQL語句可以通過otl_stream實作,下面會有介 紹。
int ignore_error:是否忽略異常。可以指定otl_exception::disable禁用異常,否則程式需要使用try...catch(otl_exception &e)...捕獲并處理異常。
int connected:此成員變量辨別了連接配接對象是否連接配接成功,一旦連接配接成功其值即為1。即便後來網絡斷掉了,此值仍舊保持不變,logoff()後此值變為0。是以此變量隻能用于檢查rlogon是否連接配接成功,而不能判斷目前與資料庫的連接配接是否正常。
2. otl_stream類
void open(...):為流對象關聯一個SQL語句,可以是帶輸入或輸出的SQL語句或PL/SQL塊。
const int arr_size:指定流緩沖區的大小。作為輸出流使用時,若輸出緩沖區中的記錄數達到此值即緩沖區滿時,會自動重新整理緩沖區,若已設設定了自動送出則一并送出資料(預設);
const char *sqlstm:SQL語句,可以指定綁定變量,如:delete from book where id = :f1<int> and price = :f2<int>;
otl_connect &db:流所使用的資料庫連接配接對象。
void set_commit(int auto_commit=0):設定流被重新整理時否自動送出事務。兩種條件下流會被重新整理:a.緩沖區滿 b.手動調用flush成員函數
void flush(...):執行和流關聯的SQL語句。如執行:
otl_stream delStream;
delStream.open(100, delete from book where id = :f1<int> and price = :f2<int>, dbConnect); //正常情況下向流中加入100條記錄時才執行删除
delStream << 1;
delSteam << 'C++';
delStream.flush(); //立即執行删除操作,盡管目前流中隻有一條記錄
long get_rpc():擷取流中SQL語句執行後所影響到的記錄數,如插入100條記錄,則調用此函數傳回的即為100
int good():判斷流對象是否已正常打開,已打開傳回1。注意:若複用一個流對象時,必須先調用close函數将其關閉,然後再調用open重新打開。
int get_dirty_buf_len():擷取目前流對象緩沖區中的記錄數,其最大值為緩沖區size-1。每當緩沖區滿時會自動重新整理,重新整理後再調用此函數時傳回0
3.otl_exception類
該類的幾個成員用于表示異常的資訊,如:
char stm_text[2048]:出錯的SQL語句;
char var_info[256]:若在流中使用了與實際類型不符的綁定變量,此數組的值為綁定變量的資訊;
unsigned char msg[1000]:這個我比較喜歡用,此數組顯示出具體的異常資訊(包括oracle傳回的錯誤碼),如連接配接逾時等等。
二、對于網絡異常的處理
現在項目對于程式的異常處理能力要求越來越高,比如網絡中斷或資料庫出現異常等,要求在故障恢複後,程式能正常與資料庫保持連接配接,使業務盡可能的少受影響。可以通過以下方法解決此種情況。這也是折磨了我兩天的一個問題:(
首先,程式要在送出資料的地方使用try...catch捕獲otl_exception異常,當送出失敗時,otl會抛出此異常并攜帶異常資訊;
其次,要在捕獲到異常之後,關閉之前的連接配接對象(如果有流使用此連接配接對象,則一定要先關閉流對象,然後再斷開otl_connect對象);
最後,重新連接配接資料庫并再次初始化流對象。
如:
void ReConnect(otl_connect &otlConnect, const char *pConnStr, int iAutoCommit);
//聲明otl對象并初始化對象
otl_connect dbConn;
otl_stream outStream;
void Init(void)
{
try
{
otl_connect::otl_initialize(1);
dbConn.rlogon("");
outStream.open(100, "insert into book values(:f1<int>, :f2<char[50]>, :f3<int>, :f4<char[20]>)", dbConn);
outStream.set_commit(1);
}
catch (otl_exception &e)
//處理異常
}
//送出資料的函數
void Submit(void)
for (int i = 0; i < 5000; i++)
{
outStream << i;
outStream << "abc";
outStream << "null";
}
catch(otl_exception &e)
//送出資料異常,重新連接配接資料庫并重新初始化流對象
outStream.close(); //必須先關閉流對象。若先斷開連接配接會出現關閉流對象時報OCIHandleFree異常導緻流對象無法正常關閉引起記憶體洩漏
ReConnect(dbConn, "", 0);
//重新連接配接連接配接資料庫函數
void ReConnect(otl_connect &otlConnect, const char *pConnStr, int iAutoCommit)
if (1 == otlConnect.connected)
otlConnect.logoff();
Retry:
otlConnect.rlogon(pConnStr, iAutoCommit);
Sleep(1000);
goto Retry;
這樣,調用Submit函數送出資料時,就有了網絡異常處理功能,若送出失敗則會一直嘗試重新連接配接,直到連接配接成功為止。
本文版權歸作者 kanego 和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.