一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數(shù)據(jù)庫技術(shù)|

服務(wù)器之家 - 數(shù)據(jù)庫 - Mysql - 用C++封裝MySQL的API的教程

用C++封裝MySQL的API的教程

2020-06-23 11:05MYSQL教程網(wǎng) Mysql

這篇文章主要介紹了用C++封裝MySQL的API的教程,包括對語句拼裝器SQLJoin的介紹,需要的朋友可以參考下

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

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

    1.簡單

    簡單,意味著不為了微小的效率提升,而去把接口搞的復(fù)雜。因為本身數(shù)據(jù)庫存儲效率的瓶頸并不是那一兩次內(nèi)存copy,代碼中隨處可以看到以這個為依據(jù)的設(shè)計。
    2.低學(xué)習(xí)成本

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

    代碼實際包括了兩個模塊,一個是mysql client端的封裝,一個是sql的拼接器,這兩個模塊是完全獨立的,調(diào)用者可以任意組合或者獨立使用。
    4.盡量使用STL以及模板,簡化代碼編寫

    最大的特點就是大量使用了stringstream進(jìn)行類型轉(zhuǎn)化,減少了大量的重復(fù)代碼。

OK,基于以上的簡單介紹,我們先來看一下
一.mysql client端的封裝:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
class CMYSQLWrapper
{
 /**
  * @brief 獲取錯誤信息
  *
  * @return 錯誤信息
  */
 char* GetErrMsg();
 
 /**
  * @brief 連接MYSQL,已經(jīng)支持了自動重連模式,即mysql server關(guān)閉鏈接會自動重連
  *
  * @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 關(guān)閉鏈接并釋放result
  */
 void Close();
 
 /**
  * @brief 執(zhí)行SQL語句
  *
  * @param strSql  執(zhí)行語句
  * @param result  執(zhí)行結(jié)果
  *
  * @return 0   succ
  *   else  fail
  */
 int Query(const char* strSql);
 
 /**
  * @brief 針對Read(select)相關(guān)的的Query,可以支持blob了
  *
  * @param strSql   sql語句
  * @param vecData   rows
  *
  * @return 0    succ
  *   else   fail
  */
 int Query(const char* strSql, vector<map<string, MYSQLValue> > &vecData);
 
 /**
  * @brief 針對Write(insert,update,delete)相關(guān)的Query
  *
  * @param strSql   sql語句
  * @param affectRowsCount 影響的行的個數(shù)
  *
  * @return 0    succ
  *   else   fail
  */
 int Query(const char* strSql, int& affectRowsCount);
 
 
 /**
  * @brief Select時獲取數(shù)據(jù),記得手工析構(gòu),或者用StMYSQLRes
  *
  * @param result  執(zhí)行結(jié)果
  *
  * @return 0   succ
  *   else  fail
  */
 int Result(MYSQL_RES *&result);
 
 /**
  * @brief 返回影響行數(shù)
  *
  * @return >0   succ
  *   0   沒有更新
  *   <0   fail
  */
 int AffectedRows();
 
 /**
  * @brief 主要是將blob轉(zhuǎn)成字符串
  *
  * @param src   blob源
  * @param len   長度
  *
  * @return 轉(zhuǎn)化后的字符串
  */
 string EscStr(const char* src,uint32_t len);
 
 /**
  * @brief 將字符串中的某些字符轉(zhuǎn)化(如')
  *
  * @param src   字符串
  *
  * @return 轉(zhuǎn)化后的字符串
  */
 string EscStr(const char* src);
};
 
class CMYSQLWrapper
{
 /**
  * @brief 獲取錯誤信息
  *
  * @return 錯誤信息
  */
 char* GetErrMsg();
 
 /**
  * @brief 連接MYSQL,已經(jīng)支持了自動重連模式,即mysql server關(guān)閉鏈接會自動重連
  *
  * @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 關(guān)閉鏈接并釋放result
  */
 void Close();
 
 /**
  * @brief 執(zhí)行SQL語句
  *
  * @param strSql  執(zhí)行語句
  * @param result  執(zhí)行結(jié)果
  *
  * @return 0   succ
  *   else  fail
  */
 int Query(const char* strSql);
 
 /**
  * @brief 針對Read(select)相關(guān)的的Query,可以支持blob了
  *
  * @param strSql   sql語句
  * @param vecData   rows
  *
  * @return 0    succ
  *   else   fail
  */
 int Query(const char* strSql, vector<map<string, MYSQLValue> > &vecData);
 
 /**
  * @brief 針對Write(insert,update,delete)相關(guān)的Query
  *
  * @param strSql   sql語句
  * @param affectRowsCount 影響的行的個數(shù)
  *
  * @return 0    succ
  *   else   fail
  */
 int Query(const char* strSql, int& affectRowsCount);
 
 
 /**
  * @brief Select時獲取數(shù)據(jù),記得手工析構(gòu),或者用StMYSQLRes
  *
  * @param result  執(zhí)行結(jié)果
  *
  * @return 0   succ
  *   else  fail
  */
 int Result(MYSQL_RES *&result);
 
 /**
  * @brief 返回影響行數(shù)
  *
  * @return >0   succ
  *   0   沒有更新
  *   <0   fail
  */
 int AffectedRows();
 
 /**
  * @brief 主要是將blob轉(zhuǎn)成字符串
  *
  * @param src   blob源
  * @param len   長度
  *
  * @return 轉(zhuǎn)化后的字符串
  */
 string EscStr(const char* src,uint32_t len);
 
 /**
  * @brief 將字符串中的某些字符轉(zhuǎn)化(如')
  *
  * @param src   字符串
  *
  * @return 轉(zhuǎn)化后的字符串
  */
 string EscStr(const char* src);
};

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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<map<string,MYSQLValue> > 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<uint32_t>();
   }
   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<map<string,MYSQLValue> > 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<uint32_t>();
   }
   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的收發(fā)包已經(jīng)很簡潔了,但是sql語句的拼裝卻顯得十分臃腫。所以這個時候sql語句拼裝器-SQLJoin閃亮登場!
二.sql語句拼裝器-SQLJoin

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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 分割符,默認(rèn)是用',',也可以用and、or之類
  *
  * @return 所有列名-列值
  */
 string pairs(const string& split_str = ",");
 
 /**
  * @brief 清空所有數(shù)據(jù)
  */
 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 分割符,默認(rèn)是用',',也可以用and、or之類
  *
  * @return 所有列名-列值
  */
 string pairs(const string& split_str = ",");
 
 /**
  * @brief 清空所有數(shù)據(jù)
  */
 void clear();
};

看看我們用了SQLJoin之后的代碼應(yīng)該如何:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
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();
}

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

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

明天這份代碼就會作為數(shù)據(jù)庫訪問層正式進(jìn)入生產(chǎn)環(huán)境的代碼中,因此有什么bug我也會及時在這里更新。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 久久精品AV一区二区无码 | 亚洲国产精品免费在线观看 | 国产精品亚洲w码日韩中文 国产精品香蕉在线观看不卡 | 国产精品第 | 亚洲国产综合久久久无码色伦 | 日韩福利网站 | 希望影院高清免费观看视频 | 99re8在这里只有精品23 | 99精彩免费观看 | 黄色大片免费网站 | 交换朋友夫妇3中文字幕 | 第一次不是你高清在线观看 | 亚洲天堂男人网 | 国产这里有精品 | 9420高清视频在线观看网百度 | 欧美美女被艹 | 青青青国产在线 | ai换脸造梦jennie | 91精品手机国产露脸 | 好大好硬好紧太深了受不了 | 天天av天天翘天天综合网 | 糖心vlog麻豆精东影业传媒 | 精品欧美一区二区三区四区 | 欧美成人香蕉在线观看 | 日韩亚洲一区中文字幕在线 | 好 舒服 好 粗 好硬免费视频 | 国产一卡2卡3卡四卡精品网站 | japonensis中国东北老人 | 亚洲小视频 | 日本免费在线 | 日本漫画被黄漫免费动 | 特黄未满14周岁毛片 | 窝窝午夜精品一区二区 | 国内自拍2020 | 午夜视频一区二区三区 | 日本一区二区三区四区无限 | 亚洲成A人片在线观看中文L | 91精品免费观看老司机 | jizz漫画| 成人高清视频在线观看 | 小辣椒精品福利视频导航 |