天天看點

OTL之Oracle開發總結《轉》

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 和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.