天天看點

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之間的一個強耦合。

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

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

public:    

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

    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*& 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->create(10, db_ip.c_str(), 3306, db_name.c_str(), db_user.c_str(), db_password.c_str());

    }

    catch (sys::CDBException& ex)

    {

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

        exit(1);

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

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

        IDBConnection* db_connection = db_connection_pool->get_connection();

        if (NULL == db_connection)

        {

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

            break;

        }

        // 自動釋放

        DBConnectionHelper db_connection_helper(db_connection_pool, db_connection);

        try

            size_t row = 0; // 目前行數

            // 執行一條查詢語句

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

            uint16_t field_number = recordset->get_field_number();

            // 自動釋放

            RecordsetHelper recordset_helper(db_connection, recordset);

            for (;;)

            {

                // 取下一行記錄

                IRecordrow* recordrow = recordset->get_next_recordrow();

                if (NULL == recordrow) break;

                // 自動釋放

                RecordrowHelper recordrow_helper(recordset, recordrow);

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

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

                for (uint16_t col=0; col                {

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

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

                }

                fprintf(stdout, "\n");

            }

        catch (sys::CDBException& ex)

            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函數。

閱讀(1890) | 評論(0) | 轉發(0) |