MYSQL訪問之如何使用C/C++連接數(shù)據(jù)庫
1. 前言
我們已經(jīng)學(xué)習(xí)了在 MySQL 客戶端中直接執(zhí)行 SQL 語句來操作數(shù)據(jù),但真正在應(yīng)用開發(fā)中,我們是通過編程語言來訪問數(shù)據(jù)庫的。比如用C/C++編寫的后端服務(wù),需要在自己的代碼里連接MySQL、執(zhí)行SQL、處理結(jié)果。那么,如何使用C/C++來訪問數(shù)據(jù)庫呢?這就要用到MySQL官方提供的C API庫。
在開始編程之前,我們需要做一些準(zhǔn)備:
創(chuàng)建一個專門用于連接的 MySQL 用戶賬號。為了安全和管理,我們不會在代碼里直接使用 root 賬號。
- 創(chuàng)建一個測試庫 conn。
- 創(chuàng)建一個賬號 xay,并賦予它關(guān)于 conn 庫的所有權(quán)限,且只允許本地登錄,執(zhí)行命令:grant all on conn.* to 'xay'@'localhost';
安裝 C/C++ 訪問 MYSQL 所需的開發(fā)庫。例如:在 CentOS 上,可以執(zhí)行 yum install mysql-devel,這會安裝 MYSQL 客戶端的頭文件和動態(tài)鏈接庫。頭文件通暢位于 /usr/include/mysql,庫文件位于 /user/lib64/mysql。
2.核心API接口與應(yīng)用
MySQL的C API設(shè)計得很直觀,整個使用流程就像文件操作一樣:打開文件 -> 操作 -> 關(guān)閉文件。這里的“文件”就是數(shù)據(jù)庫連接,對應(yīng)的“句柄”類型是 MYSQL。
2.1 初始化和連接
mysql_init()
函數(shù)原型:MYSQL *mysql_init(MYSQL *mysql)
功能:初始化一個MYSQL句柄。在使用任何其他MySQL API之前,必須先調(diào)用它來創(chuàng)建和初始化一個連接句柄。
MYSQL *my = mysql_init(nullptr); 這里傳nullptr,它會為你分配并返回一個新的MYSQL對象。
mysql_real_connect()
初始化完畢之后,必須用這個函數(shù)來建立到數(shù)據(jù)庫服務(wù)器的實際連接。
函數(shù)原型:MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db,
unsigned int port, const char *unix_socket, unsigned long client_flag)
功能:鏈接數(shù)據(jù)庫 初始化完畢之后,必須先鏈接數(shù)據(jù)庫,在進行后續(xù)操作
參數(shù) mysql:函數(shù)mysql_init的返回值
參數(shù) host:連接的主機的IP/域名
參數(shù) user:登錄的用戶名
參數(shù) password:登錄密碼
參數(shù) db:要連接的數(shù)據(jù)庫名
參數(shù) port:MySQL服務(wù)端口,通常是3306
參數(shù) unix_socket:域間套接字,本地連接時可指定socket文件路徑,否則傳nullptr
參數(shù) client_flag:一般設(shè)置成0
建立好鏈接之后,獲取英文沒有問題,但如果數(shù)據(jù)庫中有中文,查詢結(jié)果可能會是亂碼。這是因為連接的默認(rèn)字符集是latin1。我們可以在連接成功后,立即設(shè)置字符集為 utf8mb4 來解決這個問題。
2.2 執(zhí)行SQL和獲取結(jié)果
mysql_query()
函數(shù)原型:int mysql_query(MYSQL *mysql, const char *q);
功能:下發(fā)并執(zhí)行一條mysql命令
參數(shù) mysql:mysql_init的返回值
參數(shù) q:要執(zhí)行的sql語句,如“select * from table”
返回值:如果執(zhí)行成功,返回0;如果出錯,返回非0
獲取結(jié)果集:區(qū)分場景
SQL 執(zhí)行完后,如果是增、刪、改語句,我們主要關(guān)心操作是否成功,也就是看mysql_query的返回值和受影響的行數(shù)。但如果是查詢語句,我們還需要讀取返回的數(shù)據(jù)表,這就要用到結(jié)果集相關(guān)的函數(shù)。
如何獲取查詢結(jié)果?如果 mysql_query 執(zhí)行查詢語句并返回成功,我們就通過 mysql_store_result來把結(jié)果集從服務(wù)器取到客戶端內(nèi)存中。
函數(shù)原型:MYSQL_RES *mysql_store_result(MYSQL *mysql);
功能:將服務(wù)器返回的整個查詢結(jié)果集存儲到本地。它返回一個 MYSQL_RES 類型的指針,我們可以通過它來解析表格數(shù)據(jù)
這個函數(shù)在內(nèi)部malloc了一片內(nèi)存空間來存儲查詢結(jié)果,我們必須在用完結(jié)果集后,調(diào)用 mysql_free_result(result) 來釋放,否則會造成內(nèi)存泄漏!
解析結(jié)果集
拿到 MYSQL_RES * 后,我們就可以像操作一個二維表一樣來讀取數(shù)據(jù)了。
獲取行數(shù):mysql_num_rows
函數(shù)原型:my_ulonglong mysql_num_rows(MYSQL_RES *res)
功能:返回結(jié)果集的總行數(shù)。
獲取列數(shù):mysql_num_fields
函數(shù)原型:unsigned int mysql_num_fields(MYSQL_RES *res);
功能:返回結(jié)果集的總列數(shù)。
獲取列名(字段頭):mysql_fetch_fields
函數(shù)原型:MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);
功能:返回一個包含所有字段信息的數(shù)組,數(shù)組長度就是列數(shù)。我們可以通過 MYSQL_FIELD 結(jié)構(gòu)體中的 name 成員來獲取每一列的列名。
MYSQL_FIELD 常用成員屬性:
| 成員名 | 類型 | 說明 |
|---|---|---|
name | char * | 列名(字段名),如 "id", "username",這是我們最常用的 |
table | char * | 該列所屬的表名(如果查詢涉及多表,這個很有用) |
db | char * | 該列所屬的數(shù)據(jù)庫名 |
catalog | char * | 目錄名,通??偸?nbsp;"def" |
def | char * | 該列的默認(rèn)值(如果沒有默認(rèn)值則為NULL) |
length | unsigned long | 該列定義的最大長度(不是實際數(shù)據(jù)的長度),比如 varchar(100) 就是 100 |
type | enum enum_field_types | 該列的MySQL數(shù)據(jù)類型,例如 MYSQL_TYPE_LONG(整數(shù))、MYSQL_TYPE_VAR_STRING(變長字符串)等,可以用于判斷某列是數(shù)字還是字符串 |
flags | unsigned int | 列的標(biāo)志位,一個位掩碼,可以組合多種標(biāo)志,常見的有: • NOT_NULL_FLAG - 字段不能為空• PRI_KEY_FLAG - 是主鍵的一部分• UNIQUE_KEY_FLAG - 是唯一鍵的一部分• AUTO_INCREMENT_FLAG - 是自增字段• NUM_FLAG - 是數(shù)字類型等等 |
charsetnr | unsigned int | 該列的字符集編號 |
獲取每一行數(shù)據(jù):mysql_fetch_row
函數(shù)原型:MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
功能:逐行讀取結(jié)果集。每次調(diào)用,它會返回一個指向當(dāng)前行數(shù)據(jù)的 MYSQL_ROW,并把內(nèi)部指針移到下一行。當(dāng)沒有更多行時,返回NULL。
MYSQL_ROW 本質(zhì)上是 char **,你可以把它當(dāng)成一個字符串?dāng)?shù)組,通過下標(biāo) row[0]、row[1]...來訪問該行每一列的值。
2.3 接口的使用
上述的部分接口使用方式如下所示:
// 打印表的字段
MYSQL_FIELD *fields_array = mysql_fetch_fields(res);
for(my_ulonglong i = 0; i < fields; i++)
std::cout << fields_array[i].name << '\t';
std::cout << std::endl;
my_ulonglong rows = mysql_num_rows(res);
my_ulonglong fields = mysql_num_fields(res);
std::cout << "行: " << rows << std::endl;
std::cout << "列: " << fields << std::endl;
for(my_ulonglong i = 0; i < rows; i++)
{
MYSQL_ROW data = mysql_fetch_row(res);
for(my_ulonglong j = 0; j < fields; j++)
{
std::cout << data[j] << "\t";
}
std::cout << std::endl;
}運行結(jié)果:
mysql_real_connect() success
mysql_query success
行: 5
列: 3
id name blance
1 張三 100.00
2 李四 200.00
3 王五 300.00
4 趙六 400.00
6 南宮 600.00
上面的例子是單個連接的簡單使用。但在真實的Web服務(wù)器中,如果每個用戶請求都去新建和銷毀一個數(shù)據(jù)庫連接,開銷會非常大,且無法控制并發(fā)連接數(shù)。因此,實際應(yīng)用中通常使用 連接池。
連接池 本質(zhì)上是一個管理數(shù)據(jù)庫連接的容器,內(nèi)部包含一個線程池和一組預(yù)先創(chuàng)建好的MySQL連接對象。當(dāng)業(yè)務(wù)線程需要訪問數(shù)據(jù)庫時,就從池中“借用”一個空閑連接,用完后再“歸還”給池,而不是直接關(guān)閉。這樣大大提高了性能和資源利用率。
3. 結(jié)尾
一個用戶邏輯是如何執(zhí)行的?(以注冊和登錄為例)
結(jié)合我們上面講到的C/C++訪問數(shù)據(jù)庫技術(shù),我們來梳理一下用戶注冊和登錄的后臺流程。注意,網(wǎng)站服務(wù)器并不是直接裸露地與數(shù)據(jù)庫交互,它們之間通常有一層用編程語言實現(xiàn)的“中間件”或后端服務(wù)。
注冊流程:
- 用戶向網(wǎng)站A發(fā)起注冊請求。
- 網(wǎng)站A的 Web 服務(wù)器收到請求,并返回一個注冊頁面。
- 用戶填寫用戶名、密碼等信息并點擊提交。
- 網(wǎng)站 A 的后端服務(wù)(可能由 C/C++/Java/Go 等編寫)接收到用戶提交的數(shù)據(jù),提取出賬號、密碼等參數(shù)。
- 后端服務(wù)對密碼進行加密處理(出于安全考慮,絕不能明文存儲)。
- 后端服務(wù)將注冊邏輯構(gòu)建成一條SQL語句,如 insert into user ...。
- 這條SQL被下發(fā)給“中間件”(負(fù)責(zé)負(fù)載均衡、分庫分表等)。中間件決定將數(shù)據(jù)寫入到哪一個具體的數(shù)據(jù)庫實例。
- 在這一步,中間件或最終執(zhí)行SQL的后臺線程,會使用我們上面講的 mysql_init、mysql_real_connect 等API,從連接池中獲取連接,執(zhí)行insert語句。
- 數(shù)據(jù)庫操作成功后,結(jié)果一層層返回,最終網(wǎng)站A告訴用戶“注冊成功”。同時,多個數(shù)據(jù)庫節(jié)點間會進行數(shù)據(jù)同步,保證數(shù)據(jù)一致性。
登錄流程:
- 用戶向網(wǎng)站A發(fā)起登錄請求,獲得登錄頁面。
- 用戶輸入賬號、密碼并提交。
- 網(wǎng)站A后端服務(wù)接收到請求,提取參數(shù)。
- 后端服務(wù)構(gòu)建一條查詢SQL,如 select user_id, password from user where username='...'。
- 通過中間件路由,SQL最終到達具體的數(shù)據(jù)庫。
- 在這一步,后臺程序再次通過C/C++等語言的API,從連接池獲取連接,執(zhí)行select語句,并通過mysql_store_result和mysql_fetch_row獲取查詢結(jié)果。
- 后臺程序?qū)⒉榈降募用苊艽a與用戶輸入密碼的加密結(jié)果進行比對。
- 如果匹配,則為該用戶生成一個會話憑證(Session),并返回登錄成功;否則,返回賬號或密碼錯誤。
通過C/C++的API,我們就打通了應(yīng)用程序與數(shù)據(jù)存儲之間的橋梁,讓死的數(shù)據(jù)能被靈活地用于活的應(yīng)用邏輯中。
到此這篇關(guān)于MYSQL訪問之如何使用C/C++連接數(shù)據(jù)庫的文章就介紹到這了,更多相關(guān)mysql使用C/C++連接數(shù)據(jù)庫內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- C++連接數(shù)據(jù)庫SqlServer、MySql、Oracle、Access、SQLite、PostgreSQL、MongoDB、Redis
- C/C++連接MySQL數(shù)據(jù)庫詳細(xì)圖文教程
- C/C++代碼操作MySQL數(shù)據(jù)庫詳細(xì)步驟
- C++連接mysql數(shù)據(jù)庫的兩種方法小結(jié)
- C/C++ 連接MySql數(shù)據(jù)庫的方法
- Linux下實現(xiàn)C++操作Mysql數(shù)據(jù)庫
- C++利用MySQL API連接和操作數(shù)據(jù)庫實例詳解
- C++用mysql自帶的頭文件連接數(shù)據(jù)庫
- c++連接mysql數(shù)據(jù)庫的兩種方法(ADO連接和mysql api連接)
相關(guān)文章
Ubuntu配置sublime text 3的c編譯環(huán)境的具體步驟
下面小編就為大家?guī)硪黄猆buntu配置sublime text 3的c編譯環(huán)境的具體步驟。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-03-03
C語言關(guān)于include順序不同導(dǎo)致編譯結(jié)果不同的問題
這篇文章主要介紹了在日常調(diào)試C語言中include的順序不同從而影響最后編譯結(jié)果不同的問題,究其原因是寫代碼的習(xí)慣所導(dǎo)致,下面跟小編一起來看看吧2022-04-04
Matlab控制電腦攝像實現(xiàn)實時人臉檢測和識別詳解
人臉識別過程主要由四個階段組成:人臉檢測、圖像預(yù)處理、面部特征提取和特征識別。這篇文章主要介紹了如何使用MATLAB控制筆記本電腦的攝像頭,并進行實時人臉檢測和識別,需要的可以參考一下2022-10-10
C++使用MySQL-Connector/C++連接MySQL出現(xiàn)LNK2019錯誤的解決方法
這篇文章主要介紹了C++使用MySQL-Connector/C++連接MySQL出現(xiàn)LNK2019錯誤的解決方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03
C++ using namespace std 用法深入解析
以下是對C++中using namespace std的用法進行了詳細(xì)的分析介紹,需要的朋友可以過來參考下2013-07-07

