在本節中,我將首先介紹MYSQL++中的查詢的幾個簡單例子用法,然后看一下mysqlpp::Query中的幾個與查詢相關的方法原型(重點關注返回值),最后對幾個關鍵類型進行解釋。
1. MYSQL++的查詢實例
下面的兩個例子分別是STORE(所有數據一次性從服務器拉到本地緩存)和USE(將數據一條一條從服務器拉到本地)的使用方式,其中STORE內部實質就是mysql_store( ),而USE實質就是調用了mysql_use( )。
說明,下面的例子雖然使用的都是最為基本的查詢過程(即沒有使用template sql或者SSQLS),但是這個並不影響我們的討論,因為遍歷結果集的過程是一致的。
STORE
mysqlpp::Query query = conn.query("select item from stock"); mysqlpp::StoreQueryResult res = query.store(); mysqlpp::StoreQueryResult::const_iterator it; for (it = res.begin(); it != res.end(); ++it) { mysqlpp::Row row = *it; // 第一列 row[0] // 第二列 row[1] }
USE
mysqlpp::Query query = conn.query("select * from stock"); mysqlpp::UseQueryResult res = query.use(); while (mysqlpp::Row row = res.fetch_row()) { row["item"]; row["num"]; } // Check for error: // can't distinguish "end of results" and // error cases in return from fetch_row() otherwise if (conn.errnum()) { cout << "Error in fetch_row"; }
從上面的兩個例子中我們大致可以看出來,整個結果集的查詢過程就是針對相匹配的Result類型,找到ROW,然后逐行或者跳躍着查找。
2. Result相關類型的使用場合
首先讓我們回顧mysqlpp::Query中提到的幾個方法,以及他們的返回值。以下部分內容直接摘自關於mysqlpp::Query的介紹的內容。
為了講述方便,我們先來看一下在result.h(Declares classes for holding information about SQL query)中所定義的各種類型的關系。這幾個類型后面會仔細說明。
1)
bool mysqlpp::Query::exec(const std::string& str);
這里返回的是bool,表示的是“語句是否執行成功”,我認為適合於那種update, delete,insert,且都不關心有多少rows被touch的情況。
2)
SimpleResult mysqlpp::Query::execute();
這里的SimpleResult正如其名,其中只有如下信息
-
the last value used for an AUTO_INCREMENT field
(ulonglong insert_id() const) -
the number of rows affected by the query
(ulonglong rows( ) const) -
any additional information about the query returned by the server(const char* info( ) const)
3)
UseQueryResult mysqlpp::Query::use();
由於use的語義類似於使用游標,也就是支持一行一行地拉出內容,所以UseQueryResult 也就自然而然地支持一些關於fetch row的功能。
4)
StoreQueryResult mysqlpp::Query::store();
StoreQueryResult它本身就是從vector<Row>繼承而來,所以它就是vector。所以用戶程序可以直接使用下標的形式來獲取所有的ROW。這也就是說在這個store之后,所有的ROW的內容都在了這個vecor<Row>里面了。
3. Result相關類型解析
與Result相關的類型主要集中在result.h和result.cpp。
1) SimpleResult
這個類型非常簡單,是一個比較單純的類型,並沒有父類。主要的成員變量有3個,分別是
// last value used for an AUTO_INCREMENT field ulonglong insert_id_; // the number of rows affected by the query ulonglong rows_; // any additional information about the query returned by the server std::string info_;
成員方法也比較單純,就是對上述幾個字段的GET,構造函數就是對上述幾個字段的SET。
以SimpleResult mysqlpp::Query::execute();為例,讓我們來看一下MYSQL++是如何設置這個SimpleResult。
再來看一下Query::insert_id( ), Query::affected_rows( ), Query::info( ),這幾個方法都直接delegate DBDriver的相關函數,直接返回了結果而已。
2) ResultBase
這個類型是StoreQueryResult和UseQueryResult的父類型,該類型其實沒有特別的含義,只是作為一個common methods的container而已。但是該類型提供的幾乎所有的public methods都是和fields有關的,包括了
- 根據index查找field;
- 根據name查找field;
- 根據index查找到field的名字和屬性類型;
- 獲取當前所在的field(ResultBase內部會有一個mutable的index名為current_field_用於指明當前fetch_field的是哪一個field,在fetch_field中該index會自增)
該類型的核心成員有如下幾個
- Fields(std::vector<Field>) 類型的 fields_
- FieldNames (繼承自std::vector<std:: string>)類型的 names_
- FieldTypes ( 繼承自std::vector<mysql_type_info>)類型的types_
其中FieldNames和FieldTypes都是被RefCountedPointer包裹着,估計這樣做能夠節省空間。關於Field類型在后續進行介紹。至於剛才說的幾個找field的操作,看着這幾個核心成員的類型還是比較好理解的。不就是在vector中不找。
需要額外關注的是以下這個最主要的構造函數
以下幾點需要注意,
- names_和types_在new出來之后是不需要顯式delete的,因為他們都在智能指針RefCountedPointer包裹着。
- 成員初始化列表中的 fields_(Fields:: size_type(…)) 這一句其實調用的是
explicit std::vector::vector (size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type());
- 從以上構造函數可以看到,其實關於Field的信息還是從DBDriver中拿到的,然后關於FieldNames和FieldTypes的信息,則是通過這兩個類型自身的構造函數給實現的(其實就是收到ResultBase*,然后遍歷其中的成員變量 fields_ 再各求所需罷了。
- 為什么要有重新將指示field_當前index的指針重置的過程(dbd->field_seek(res, 0))?這是因為方便這個DBDriver再次循環訪問這些fields。
3) StoreQueryResult
該類型是同時集成了ResultBase和vecor<Row>類型(請注意了,這個Result居然是一個vecor<Row>!)文檔中說,該類型hold results from a SQL query that returns rows: a specialization of std::vector holding Row objects in memory so you get random-access semantics.
說白了,該類型在繼承了ResultBase之后,只有以下這個構造函數是比較要緊的了。
有以下幾點需要說明
- StoreQueryResult繼承自std::vector<Row>
- 在成員初始化列表中,首先調用ResultBase的構造函數
- list_type是std::vector<Row>的typedef,所以在list_type(…)這一行,直接根據DBDriver返回的結果的rows的數量就布置好了vector的長度。
- MYSQL_ROW是MYSQL自帶的結構類型(定義域mysql.h中,typedef char **MYSQL_ROW;)
- dbd->fetch_row(MYSQL_RES*)應該只是包裝了mysql_fetch_row(MYSQL_RES*)這個MySql C API,而dbd->fetch_lengths(MYSQL_RES*)其實包裝的是mysql_fetch_lengths(MYSQL_RES*)這個MYSQL C API,他獲取的是整個row的對應的所有field的長度。
- 這一次Query的結果在全部拷貝完之后就釋放了(dbd->free_result(res);)
如此一來,所有我們需要的信息都通過構造一個mysqlpp::Row對象來實現了。關於mysqlpp::Row的具體說明請看下文。
4) UseQueryResult
該類型拓展於ResultBase類型。根據MYSQL++ user document的說法,UseQueryResule適用於For these large result sets,a “use” query tells the database server to send the results back one row at a time, to be processed linearly.
該類型的方法比StoreQueryResult豐富多了,主要體現在:
- 可以順序讀取行(mysqlpp::Row fetch_row() const;或者MYSQL_ROW fetch_raw_row() const;)
- 可以獲取當前行的各個field的信息(const Field& fetch_field() const; const Field& fetch_field(Fields::size_type i) const)
- 可以獲取當前行的所有fields的長度(const unsigned long* fetch_lengths() const;)
值得注意的是,UseQueryResult有一個成員變量
mutable RefCountedPointer<MYSQL_RES> result_;
需要強調的有兩點
- MYSQL_RES是 MYSQL C API 中的用於做為資源的結構體,獲取一行的函數mysql_fetch_one_row的參數就是MYSQL_RES*
- RefCountedPointer是一個智能指針,但是就在定義這些result類型的result.h中,作者將其針對MYSQL_RES進行了具化,即
很顯然,這個是為了在ref count為0時自動調用析構器而准備的,該Destroyer使用mysql_free_result來銷毀這里的result_,從而代替默認的delete操作。
接下來,我們來具體看幾個函數的實現。
- 構造函數
- 獲取fields的信息
請注意,這個result_是一個RefCountedPointer<MYSQL_RES>,所以調用的raw方法將會返回MYSQL_RES*
- 獲取一行數據
核心的做法就是利用MYSQL_ROW DBDriver::fetch_row(MYSQL_RES* res) const。其實質也就是調用了mysql_fetch_one_row( )這個C API。然后直接生成一個mysqlpp::Row對象,返回。