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

服務器之家:專注于服務器技術及軟件下載分享
分類導航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數據庫技術|

服務器之家 - 數據庫 - Mysql - 用C++封裝MySQL的API的教程

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

2020-06-23 11:05MYSQL教程網 Mysql

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

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

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

    1.簡單

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

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

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

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

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,已經支持了自動重連模式,即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<map<string, MYSQLValue> > &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<map<string, MYSQLValue> > &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的代碼:

?
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的收發包已經很簡潔了,但是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 分割符,默認是用',',也可以用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之后的代碼應該如何:

?
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();
}

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

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

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

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 99欧美精品| 三级黄色片在线观看 | 精品一久久香蕉国产二月 | 日韩经典在线 | 成年人福利 | 手机看片黄色 | 女仆色在线观看 | 小小水蜜桃视频高清在线观看免费 | 欧美高清日韩 | 国产精品不卡高清在线观看 | 95视频免费看片 | 男人午夜禁片在线观看 | 亚洲视频日韩 | 亚洲国产第一区二区三区 | 成人特级毛片69免费观看 | 毛片资源站 | 日本成人免费在线视频 | 精品在线视频一区 | 色五月天天| 草草草视频在线观看 | 色网在线视频 | 国产三级精品播放 | 91制片厂制作传媒破解版免费 | 国产精品最新资源网 | 99视频一区 | 91夜夜人人揉人人捏人人添 | 1769亚洲资源站365在线 | 婷婷婷色 | 4hu永久地域网名入口 | seetube18日本第一次 | 国产人人草 | 四虎在线视频免费观看 | 国产aⅴ一区二区三区 | 国产欧美日韩高清专区ho | 欧美三级免费观看 | 国产精品久久久久毛片真精品 | 色屁屁www | 亚洲男人网 | 国色天香社区视频免费观看3 | 羲义嫁密着中出交尾gvg794 | avtt一区 |