天天看點

mooon db wrapper2. 分層結構3. 源碼目錄結構4. DB API5. DB Adapter6. 附錄: 如何編譯common庫?

mooon db wrapper

1. 前言

mooon db wrapper不是一個DB,僅是對現有的DB API的封裝,使得使用更為簡單。項目位址:http://code.google.com/p/mooon,可使用SVN下載下傳最新代碼。開發和交流論壇:http://bbs.hadoopor.com/index.php?gid=67,可了解項目最新動态。

共分三層,最底層為各類資料庫提供的API,在它之上封裝成mooon db api,提供資料庫連接配接池功能。mooon db API本身與具體的資料庫無關,通過不同的實作可支援不同的資料庫,目前已經有MySQL的實作。

DB Adapter是基于DB API的更進階抽象,内置資料庫操作線程和隊列,專門負責與資料庫的讀寫互動,為上層應用提供一個異步回調的資料庫操作,進而可解除應用和DB之間的一個強耦合。

<a href="http://blog.51cto.com/attachment/201206/151923407.png" target="_blank"></a>

資料庫錯誤不能錯誤碼的形式傳回,而是采用異常的方式,可使得代碼結構變得更為簡潔。

class CDBException

{

public:

/***

* 構造一個異常對象

* 請注意不應當顯示調用構造函數

*/

CDBException(const char* sql, const char* error_message, int error_number=0, const char* filename=__FILE__, int line_number=__LINE__);

/** 傳回執行出錯的SQL語句,如果不是執行SQL語句,則僅傳回一個字元串結尾符 */

const char* get_sql() const;

/** 傳回資料庫的出錯資訊 */

const char* get_error_message() const;

/** 傳回資料庫的出錯代碼 */

int get_error_number() const;

/** 傳回執行資料庫操作時出錯的檔案名 */

const char* get_filename() const;

/** 傳回執行資料庫操作時出錯的代碼行 */

int get_line_number() const;

};

提供基礎的資料庫操作功能,而且也資料庫無關,通過不同的實作,可支援不再的資料庫。

* 記錄行接口

class IRecordrow

* 通過字段編号取得字段的值

virtual const char* get_field_value(uint16_t index) const = 0;

* 記錄集接口

class IRecordset

* 得到記錄集的行數

* 對于MySQL,如果query時,參數is_stored為false,則該函數不能傳回正确的值,

* 是以應當隻有在is_stored為true,才使用該函數

virtual size_t get_row_number() const = 0;

* 得到字段個數

virtual uint16_t get_field_number() const = 0;

* 判斷記錄集是否為空

virtual bool is_empty() const = 0;

* 檢索結果集的下一行

* @return: 如果沒有要檢索的行傳回NULL,否則傳回指向記錄行的指針,這時必須調用release_recordrow,否則有記憶體洩漏

virtual IRecordrow* get_next_recordrow() const = 0;

* 釋放get_next_recordrow得到的記錄行

virtual void release_recordrow(IRecordrow* recordrow) = 0;

* 資料庫連接配接接口

class IDBConnection

/** 是否允許自動送出 */

virtual void enable_autocommit(bool enabled) = 0;

* 用來判斷資料庫連接配接是否正建立着

virtual bool is_established() const = 0;

* 資料庫查詢類操作,包括:select, show, describe, explain和check table等

* @is_stored: 是否将所有記錄集拉到本地存儲

* @return: 如成功傳回記錄集的指針,這時必須調用release_recordset,否則有記憶體洩漏

* @exception: 如出錯抛出CDBException異常

virtual IRecordset* query(bool is_stored, const char* format, ...) = 0;

* 釋放query得到的記錄集

virtual void release_recordset(IRecordset* recordset) = 0;

* 資料庫insert和update更新操作

* @return: 如成功傳回受影響的記錄個數

virtual size_t update(const char* format, ...) = 0;

* 資料庫連接配接池接口

class IDBConnectionPool

* 得到全小寫形式的資料庫類型名,如:mysql和postgresql等

virtual const char* get_type_name() const = 0;

* 線程安全函數

* 從資料庫連接配接池中擷取一個連接配接

* @return: 如果目前無可用的連接配接,則傳回NULL,否則傳回指向資料庫連接配接的指針

* @exception: 不會抛出任何異常

virtual IDBConnection* get_connection() = 0;

* 将已經擷取的資料庫連接配接放回到資料庫連接配接池中

virtual void release_connection(IDBConnection* db_connection) = 0;

* 建立連接配接池

* @pool_size: 資料庫連接配接池中的資料庫連接配接個數

* @db_ip: 需要連接配接的資料庫IP位址

* @db_port: 需要連接配接的資料庫服務端口号

* @db_name: 需要連接配接的資料庫池

* @db_user: 連接配接資料庫用的使用者名

* @db_password: 連接配接資料庫用的密碼

virtual void create(uint16_t pool_size, const char* db_ip, uint16_t db_port, const char* db_name, const char* db_user, const char* db_password) = 0;

* 銷毀已經建立的資料庫連接配接池

virtual void destroy() = 0;

* 得到連接配接池中的連接配接個數

virtual uint16_t get_connection_number() const = 0;

強烈建議:

能使用助手類的時候盡可能地使用它,可以帶來不必要的麻煩,而且可以簡化代碼結構。

* DB連接配接助手類,用于自動釋放已經擷取的DB連接配接

class DBConnectionHelper

DBConnectionHelper(IDBConnectionPool* db_connection_pool, IDBConnection*&amp; db_connection);

~DBConnectionHelper();

* 記錄集助手類,用于自動釋放已經擷取的記錄集

class RecordsetHelper

RecordsetHelper(IDBConnection* db_connection, IRecordset* recordset);

~RecordsetHelper();

* 記錄行助手類,用于自動釋放已經擷取的記錄行

class RecordrowHelper

RecordrowHelper(IRecordset* recordset, IRecordrow* recordrow);

~RecordrowHelper();

#include "sys/db.h"

#include "plugin/plugin_mysql/plugin_mysql.h"

using namespace sys;

using namespace plugin;

int main()

std::string sql = "SELECT * FROM test"; // 需要查詢的SQL語句

std::string db_ip = "127.0.0.1";

std::string db_name = "test";

std::string db_user = "root";

std::string db_password = "";

// create_mysql_connection_pool和destroy_mysql_connection_pool兩個全局函數

// 在檔案plugin/plugin_mysql/plugin_mysql.h中聲明

IDBConnectionPool* db_connection_pool = create_mysql_connection_pool();

try

// 建立資料庫連接配接池

db_connection_pool-&gt;create(10, db_ip.c_str(), 3306, db_name.c_str(), db_user.c_str(), db_password.c_str());

}

catch (sys::CDBException&amp; ex)

fprintf(stderr, "Create database connection pool error: %s.\n", ex.get_error_message());

exit(1);

do // 這個循環無實際意義,僅為簡化代碼結構

// 從資料庫連接配接池中取一個連接配接

IDBConnection* db_connection = db_connection_pool-&gt;get_connection();

if (NULL == db_connection)

fprintf(stderr, "Database pool is empty.\n");

break;

// 自動釋放

DBConnectionHelper db_connection_helper(db_connection_pool, db_connection);

size_t row = 0; // 目前行數

// 執行一條查詢語句

IRecordset* recordset = db_connection-&gt;query(false, "%s", sql.c_str());

uint16_t field_number = recordset-&gt;get_field_number();

RecordsetHelper recordset_helper(db_connection, recordset);

for (;;)

// 取下一行記錄

IRecordrow* recordrow = recordset-&gt;get_next_recordrow();

if (NULL == recordrow) break;

RecordrowHelper recordrow_helper(recordset, recordrow);

// 循環列印出所有字段值

fprintf(stdout, "ROW[%04d] ==&gt;\t", row++);

for (uint16_t col=0; col&lt;field_number; ++col)

const char* field_value = recordrow-&gt;get_field_value(col);

fprintf(stdout, "%s\t", field_value);

fprintf(stdout, "\n");

fprintf(stderr, "Query %s error: %s.\n", ex.get_sql(), ex.get_error_message());

} while(false);

// 銷毀資料庫連接配接池

destroy_mysql_connection_pool(db_connection_pool);

進入$mooon/common_library/test/plugin/plugin_mysql目錄(其中$mooon為mooon源代碼所在目錄),運作Make編譯,成功後執行run.sh即可運作測試代碼,如:sh run.sh。

暫未實作。

編譯測試代碼之前,需要先編譯好common庫,common庫包含兩大部分:基礎類庫和插件類庫,它們的編譯是自動進行的,而且會自動探測MySQL的安裝目錄。

要求MySQL安裝在/usr/local或使用者主目錄下,或者由環境變量MYSQL_HOME指定安裝路徑,而且MySQL的目錄結構應當如下:

MySQL安裝目錄

|---------include

|----------lib

在include目錄下要求有mysql.h頭檔案,在lib目錄下要求有libmysqlclient_r.so庫檔案。

當然,如果您不使用mooon db wrapper,可以不用安裝MySQL,因為編譯腳本會自動檢測,如果沒有安裝,不會執行編譯。

言歸正傳,先從SVN上取最新的mooon common-library代碼,上傳到Linux後,進入src目錄,然後依次:

1) 執行first_once.sh,該檔案預設無執行權限,是以可sh first_once.sh方式執行(注:first_once.sh隻有在從SVN上取出代碼時執行一次,這也是取first_once的意思)

2) 接下來,完全和普通的automake操作步驟一樣,就不多說了

3) 編譯好mooon common-library後,就可以進入test目錄,編譯測試代碼了。在測試代碼目錄中,Makefile是用來編譯測試代碼的,是以隻需要執行make即可,而且run.sh是用來運作測試代碼的,每一個CPP檔案都會被編譯成一個可執行的檔案,是以必須保證目錄下所有的CPP檔案都實作了main函數。

    本文轉自eyjian 51CTO部落格,原文連結:http://blog.51cto.com/mooon/909647,如需轉載請自行聯系原作者