python調用c++接口,python封裝c++接口_用C++封裝MySQL的API的教程

 2023-10-08 阅读 26 评论 0

摘要:其實相信每個和mysql打過交道的程序員都應該會嘗試去封裝一套mysql的接口,這一次的封裝已經記不清是我第幾次了,但是每一次我希望都能做的比上次更好,更容易使用。python調用c++接口。先來說一下這次的封裝,遵守了幾個原則,其中部分思想是

其實相信每個和mysql打過交道的程序員都應該會嘗試去封裝一套mysql的接口,這一次的封裝已經記不清是我第幾次了,但是每一次我希望都能做的比上次更好,更容易使用。

python調用c++接口。先來說一下這次的封裝,遵守了幾個原則,其中部分思想是從python借鑒過來的:

1.簡單

簡單,意味著不為了微小的效率提升,而去把接口搞的復雜。因為本身數據庫存儲效率的瓶頸并不是那一兩次內存copy,代碼中隨處可以看到以這個為依據的設計。

2.低學習成本

使用一套新庫通常意味著投入學習成本,而這次的封裝并沒有像django那樣實現一套完整的模型系統,也沒有做soci那樣的語法分析器,我選擇最簡單易懂的方式:做sql語句拼接器,所以對習慣了使用原生mysql api的朋友,學習成本很低

3.模塊化

代碼實際包括了兩個模塊,一個是mysql client端的封裝,一個是sql的拼接器,這兩個模塊是完全獨立的,調用者可以任意組合或者獨立使用。

4.盡量使用STL以及模板,簡化代碼編寫

最大的特點就是大量使用了stringstream進行類型轉化,減少了大量的重復代碼。

OK,基于以上的簡單介紹,我們先來看一下

一.mysql client端的封裝:

class CMYSQLWrapper

{

/**

* @brief 獲取錯誤信息

*

* @return 錯誤信息

*/

char* GetErrMsg();

/**

* @brief 連接MYSQL,已經支持了自動重連模式,即mysql server關閉鏈接會自動重連

*

* @param ip IP

* @param user 用戶名

* @param pwd 密碼(沒有則傳NULL)

* @param db 庫(沒有則傳NULL)

*

* @return 0 succ

* else fail

*/

int Open(const char* ip,const char* user,const char* pwd,const char* strDb);

/**

* @brief 關閉鏈接并釋放result

*/

void Close();

/**

* @brief 執行SQL語句

*

* @param strSql 執行語句

* @param result 執行結果

*

* @return 0 succ

* else fail

*/

int Query(const char* strSql);

/**

* @brief 針對Read(select)相關的的Query,可以支持blob了

*

* @param strSql sql語句

* @param vecData rows

*

* @return 0 succ

* else fail

*/

int Query(const char* strSql, vector > &vecData);

/**

* @brief 針對Write(insert,update,delete)相關的Query

*

* @param strSql sql語句

* @param affectRowsCount 影響的行的個數

*

* @return 0 succ

* else fail

*/

int Query(const char* strSql, int& affectRowsCount);

/**

* @brief Select時獲取數據,記得手工析構,或者用StMYSQLRes

*

* @param result 執行結果

*

* @return 0 succ

* else fail

*/

int Result(MYSQL_RES *&result);

/**

* @brief 返回影響行數

*

* @return >0 succ

* 0 沒有更新

* <0 fail

*/

int AffectedRows();

/**

* @brief 主要是將blob轉成字符串

*

* @param src blob源

* @param len 長度

*

* @return 轉化后的字符串

*/

string EscStr(const char* src,uint32_t len);

/**

* @brief 將字符串中的某些字符轉化(如')

*

* @param src 字符串

*

* @return 轉化后的字符串

*/

string EscStr(const char* src);

};

class CMYSQLWrapper

{

/**

* @brief 獲取錯誤信息

*

* @return 錯誤信息

*/

char* GetErrMsg();

/**

* @brief 連接MYSQL,已經支持了自動重連模式,即mysql server關閉鏈接會自動重連

*

* @param ip IP

* @param user 用戶名

* @param pwd 密碼(沒有則傳NULL)

* @param db 庫(沒有則傳NULL)

*

* @return 0 succ

* else fail

*/

int Open(const char* ip,const char* user,const char* pwd,const char* strDb);

/**

* @brief 關閉鏈接并釋放result

*/

void Close();

/**

* @brief 執行SQL語句

*

* @param strSql 執行語句

* @param result 執行結果

*

* @return 0 succ

* else fail

*/

int Query(const char* strSql);

/**

* @brief 針對Read(select)相關的的Query,可以支持blob了

*

* @param strSql sql語句

* @param vecData rows

*

* @return 0 succ

* else fail

*/

int Query(const char* strSql, vector > &vecData);

/**

* @brief 針對Write(insert,update,delete)相關的Query

*

* @param strSql sql語句

* @param affectRowsCount 影響的行的個數

*

* @return 0 succ

* else fail

*/

int Query(const char* strSql, int& affectRowsCount);

/**

* @brief Select時獲取數據,記得手工析構,或者用StMYSQLRes

*

* @param result 執行結果

*

* @return 0 succ

* else fail

*/

int Result(MYSQL_RES *&result);

/**

* @brief 返回影響行數

*

* @return >0 succ

* 0 沒有更新

* <0 fail

*/

int AffectedRows();

/**

* @brief 主要是將blob轉成字符串

*

* @param src blob源

* @param len 長度

*

* @return 轉化后的字符串

*/

string EscStr(const char* src,uint32_t len);

/**

* @brief 將字符串中的某些字符轉化(如')

*

* @param src 字符串

*

* @return 轉化后的字符串

*/

string EscStr(const char* src);

};

代碼中的注釋已經描述的很清楚了,語言描述不清楚,我們直接來看一下gtest的代碼:

select:

string g_name = "good";

int g_sex = 1;

string g_name_up = "update";

int g_sex_up = 2;

TEST(mysql_wrapper_easy, select)

{

vector > vecData;

string sql = "select * from tb_test where name = '"+g_name_up+"'";

int ret = g_client.Query(sql.c_str(),vecData);

ASSERT_EQ(ret, 0) << g_client.GetErrMsg();

foreach(vecData, it_vec)

{

foreach(*it_vec, it_map)

{

cout << it_map->first << ",";

if (it_map->first == "sex")

{

cout << it_map->second.as();

}

else

{

cout << it_map->second.data();

}

cout << "," << it_map->second.size() << endl;

}

}

}

int main(int argc, char **argv)

{

int ret = g_client.Open("localhost","dantezhu",NULL,"soci");

//int ret = g_client.Open("127.0.0.1","dantezhu",NULL,"soci");

if (ret)

{

cout << ret << "," << g_client.GetErrMsg() << endl;

return -1;

}

::testing::InitGoogleTest(&argc, argv);

return RUN_ALL_TESTS();

}

string g_name = "good";

int g_sex = 1;

string g_name_up = "update";

int g_sex_up = 2;

TEST(mysql_wrapper_easy, select)

{

vector > vecData;

string sql = "select * from tb_test where name = '"+g_name_up+"'";

int ret = g_client.Query(sql.c_str(),vecData);

ASSERT_EQ(ret, 0) << g_client.GetErrMsg();

foreach(vecData, it_vec)

{

foreach(*it_vec, it_map)

{

cout << it_map->first << ",";

if (it_map->first == "sex")

{

cout << it_map->second.as();

}

else

{

cout << it_map->second.data();

}

cout << "," << it_map->second.size() << endl;

}

}

}

int main(int argc, char **argv)

{

int ret = g_client.Open("localhost","dantezhu",NULL,"soci");

//int ret = g_client.Open("127.0.0.1","dantezhu",NULL,"soci");

if (ret)

{

cout << ret << "," << g_client.GetErrMsg() << endl;

return -1;

}

::testing::InitGoogleTest(&argc, argv);

return RUN_ALL_TESTS();

}

insert:

TEST(mysql_wrapper_easy, insert)

{

clear_data();

stringstream ss;

ss

<< "insert into tb_test(name,sex) values('"

<< g_client.EscStr(g_name.c_str())

<< "',"

<< g_sex

<< ");";

int affectRowsNum;

int ret = g_client.Query(ss.str().c_str(), affectRowsNum);

ASSERT_EQ(ret, 0) << g_client.GetErrMsg();

EXPECT_GE(affectRowsNum,0) << g_client.GetErrMsg();

}

TEST(mysql_wrapper_easy, insert)

{

clear_data();

stringstream ss;

ss

<< "insert into tb_test(name,sex) values('"

<< g_client.EscStr(g_name.c_str())

<< "',"

<< g_sex

<< ");";

int affectRowsNum;

int ret = g_client.Query(ss.str().c_str(), affectRowsNum);

ASSERT_EQ(ret, 0) << g_client.GetErrMsg();

EXPECT_GE(affectRowsNum,0) << g_client.GetErrMsg();

}

可以看出,對于mysql的收發包已經很簡潔了,但是sql語句的拼裝卻顯得十分臃腫。所以這個時候sql語句拼裝器-SQLJoin閃亮登場!

二.sql語句拼裝器-SQLJoin

class SQLJoin

{

public:

/**

* @brief 用流處理的方式,添加一個列名

*

* @param key 列名

*

* @return 0

*/

SQLJoin& operator << (const string& key);

/**

* @brief 用流處理的方式,添加一個SQLPair對象

*

* @param pair_data SQLPair對象

*

* @return 0

*/

SQLJoin& operator << (const SQLPair& pair_data);

/**

* @brief 輸出所有列名(如name, sex, age)

*

* @return 所有列名

*/

string keys();

/**

* @brief 輸出所有列值(如'dante', 1, 25)

*

* @return 所有列值

*/

string values();

/**

* @brief 輸入所有列名-列值,并用指定分隔符分割(如name='dante', sex=1, age=25)

*

* @param split_str 分割符,默認是用',',也可以用and、or之類

*

* @return 所有列名-列值

*/

string pairs(const string& split_str = ",");

/**

* @brief 清空所有數據

*/

void clear();

};

class SQLJoin

{

public:

/**

* @brief 用流處理的方式,添加一個列名

*

* @param key 列名

*

* @return 0

*/

SQLJoin& operator << (const string& key);

/**

* @brief 用流處理的方式,添加一個SQLPair對象

*

* @param pair_data SQLPair對象

*

* @return 0

*/

SQLJoin& operator << (const SQLPair& pair_data);

/**

* @brief 輸出所有列名(如name, sex, age)

*

* @return 所有列名

*/

string keys();

/**

* @brief 輸出所有列值(如'dante', 1, 25)

*

* @return 所有列值

*/

string values();

/**

* @brief 輸入所有列名-列值,并用指定分隔符分割(如name='dante', sex=1, age=25)

*

* @param split_str 分割符,默認是用',',也可以用and、or之類

*

* @return 所有列名-列值

*/

string pairs(const string& split_str = ",");

/**

* @brief 清空所有數據

*/

void clear();

};

看看我們用了SQLJoin之后的代碼應該如何:

TEST(mysql_wrapper_join, insert)

{

clear_data();

SQLJoin sql_join;

sql_join

<< SQLPair("name", g_client.EscapeRealString(g_name.c_str()))

<< SQLPair("sex", g_sex);

stringstream ss;

ss

<< "insert into tb_test("

<< sql_join.keys()

<< ") values("

<< sql_join.values()

<< ")";

int affectRowsNum;

int ret = g_client.ExecuteWrite(ss.str().c_str(), affectRowsNum);

ASSERT_EQ(ret, 0) << g_client.GetErrMsg();

EXPECT_GE(affectRowsNum,0) << g_client.GetErrMsg();

}

TEST(mysql_wrapper_join, update)

{

SQLJoin sql_join;

sql_join

<< SQLPair("name", g_name_up)

<< SQLPair("sex", g_sex_up);

stringstream ss;

ss

<< "update tb_test set "

<< sql_join.pairs()

<< " where name='"

<< g_name

<

int affectRowsNum;

int ret = g_client.ExecuteWrite(ss.str().c_str(),affectRowsNum);

ASSERT_EQ(ret, 0) << g_client.GetErrMsg();

EXPECT_GE(affectRowsNum,0) << g_client.GetErrMsg();

}

TEST(mysql_wrapper_join, insert)

{

clear_data();

SQLJoin sql_join;

sql_join

<< SQLPair("name", g_client.EscapeRealString(g_name.c_str()))

<< SQLPair("sex", g_sex);

stringstream ss;

ss

<< "insert into tb_test("

<< sql_join.keys()

<< ") values("

<< sql_join.values()

<< ")";

int affectRowsNum;

int ret = g_client.ExecuteWrite(ss.str().c_str(), affectRowsNum);

ASSERT_EQ(ret, 0) << g_client.GetErrMsg();

EXPECT_GE(affectRowsNum,0) << g_client.GetErrMsg();

}

TEST(mysql_wrapper_join, update)

{

SQLJoin sql_join;

sql_join

<< SQLPair("name", g_name_up)

<< SQLPair("sex", g_sex_up);

stringstream ss;

ss

<< "update tb_test set "

<< sql_join.pairs()

<< " where name='"

<< g_name

<

int affectRowsNum;

int ret = g_client.ExecuteWrite(ss.str().c_str(),affectRowsNum);

ASSERT_EQ(ret, 0) << g_client.GetErrMsg();

EXPECT_GE(affectRowsNum,0) << g_client.GetErrMsg();

}

從上面的代碼可以看出,代碼的可維護性和健壯性得到了很大的提升。

OK,簡單的介紹就是這樣,說的比較簡略,大家有興趣可以直接看代碼,也歡迎給我提意見和建議。代碼下載路徑如下:

mysql_wrapper

明天這份代碼就會作為數據庫訪問層正式進入生產環境的代碼中,因此有什么bug我也會及時在這里更新。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/5/129268.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息