mysql訪問
- 1.引入MySQL 客戶端庫
- 2.C/C++ 進行增刪改
- 3.查詢的處理細節
- 4.圖形化界面訪問數據庫
- 4.1下載MYSQL Workbench
- 4.2MYSQL Workbench遠程連接數據庫
點贊👍👍收藏🌟🌟關注💖💖
你的支持是對我最大的鼓勵,我們一起努力吧!😃😃
1.引入MySQL 客戶端庫
從開始到選擇我們用的都是命令行式的mysql訪問mysqld,向mysqld下達我們的指令,其實在數據庫層面上,連接數據庫的客戶端除了現在命令行式的客戶端,還有圖形化界面、網頁版的,當然也包括語言級別的庫或者包幫我們去訪問數據庫。
下面我們就用C/C++訪問數據庫。關于訪問數據庫我們要做兩個準備工作,一是創建一個遠端或者本地訪問的請求也就是創建一個專門用來進行用C/C++訪問客戶端的賬號。
reate user 'connector'@'localhost' identified by 'xxx';
然后創建一個數據庫,賦予這個賬號對數據庫中表的所有權限。
二是安裝C/C++要能訪問的庫,這個庫有兩種準備,第一種是從官網下載庫,
下面這個是用不同語言連接MYSQL,官方提供的庫。
下載推薦的8.0
我們用的是linux,所以選擇linux系統x86 64位
下載好,上傳到linux,然后解壓一下,mysql-connector是解壓后重命名的
這是解壓后的文件,最重要的就是include 頭文件,還有一個lib64 庫
未來頭文件給我們提供連接mysql的方法,庫 提供連接mysql的庫函數
我們就可以根據頭文件,直接調用頭文件的的方法,在編譯連接的時候把庫連入進來。如果忘記怎么引入,在Linux哪里基礎IO動靜態庫就有對應的方法!
但是這種方式不推薦了!之間不是下載mysql,用yum源下載嗎。yum源它會給我們找到合適的服務器,客戶端,甚至是開發包。所以我們直接在yum哪里去找進行了。
沒下載過mysql 可以下一下如果你安裝過yum對應msyql源的話,如果不會下請移步于
【MySQL】MySQL在 Linux下環境安裝
yum install -y mysql-community-server
如果你下載過,你就可以看到這里有對應的頭文件,未來我們主要用的就是mysql.h
如果你下載過但是找不到頭文件,執行下面的命令,就可以看到了
install -y mysql-devel
mysql庫在系統默認安裝路徑下
開發環境我們有已經準備好了,接下來我們學習具體的接口。不過我們通過 mysql_get_client_info() 函數,來驗證我們的引入是否成功。
#include <iostream>
#include <mysql/mysql.h>using namespace std;int main()
{//獲取當前客戶端版本信息cout<<"mysql client Version: "<<mysql_get_client_info()<<endl;return 0;
}
如果直接編譯肯定會報錯,說的是未定義的引用,也就是這個mysql_get_client_info()找不到。
之前基礎IO就說過,你要保證這個外來庫能被正確連接,gcc/g++默認會選擇C和C++的庫,雖然你的mysql在系統中,系統也會默認會去/lib64路徑下去找,
但是現在的問題是我怎么知道我需要連接那個庫,盡管能找到這個庫,但是我怎么知道我們該連那個庫呢?所以我們需要需要在編譯的時候就指明需要連那個庫,
g++ -o mytest test.cc -L/lib64/mysql -lmysqlclient
然后就可以打出來所有mysql客戶端的版本,說明這個庫就被重新引入了
以前引入外來庫不是還帶上面-I選項,今天這里咋沒有帶。這是因為g++默認會去include里去找。然后拿著代碼里的
去找,所有不會報頭文件找不到的問題。如果你非得帶-I,就把前面去掉
g++ -o mytest test.cc -I/usr/include/mysql -L/lib64/mysql -lmysqlclient
接下來我們我們通過這個庫它內部給我提供對應的方法來讓我們連接mysqld服務端,然后進行訪問。
2.C/C++ 進行增刪改
接下來我們先認識mysql常用接口。msyql接口介紹
這里還有mysql庫為了方便操作mysql,然后提前在庫中給我創建很多的數據結構
msyql的是一套網絡服務,就注定了實際在進行mysql操作之前比如下達sql,一定在之前要連接mysql,而在連接mysql之前,我們還要先來創建一下mysql基礎數據結構。要使用庫,必須先進行初始化。mysql_init()幫我們創建一個MYSQL對象
MYSQL *mysql_init(MYSQL *mysql);
成功返回MYSQL對象,失敗返回nullptr,用的時候必須要調用mysql_close()對應的鏈接。
MYSQL是 C api中一個非常重要的變量(mysql_init的返回值),里面內存非常豐富,有port,dbname,charset等連接基本參數。它也包含了一個叫 st_mysql_methods的結構體變量,該變量里面保存著很多函數指針,這些函數指針將會在數據庫連接成功以后的各種數據操作中被調用。
MYSQL實際上就是一個結構體里面包含連接mysql相關保存好的一些屬性。這相當于FILE*,用來標識mysql客戶端一些資源,我們把這些東西可以稱之為句柄。
#include <iostream>
#include <mysql/mysql.h>using namespace std;int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL* my=mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
把數據庫對應的MYSQL結構體對象創建初始化好,最終不用它了把它釋放。可是要訪問mysql之前要先連接數據庫,連接數據庫的函數。
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 clientflag);
第一個參數就是剛才創建的MYSQL對象。連接成功后把連接成功套接字信息保存到MYSQL對象中。然后要連接的mysql的主機在哪里,用戶是誰,密碼是多少,連接那個數據庫,數據庫的端口號是多少,剩下設置為nullptr,0就可以了。
成功時把我們傳入的MYSQL給我們返回,失敗返回nullptr。
最開始創建的只允許在本地主機連接數據庫的用戶就用上了。
#include <iostream>
#include <mysql/mysql.h>
#include <string>using namespace std;const string host="localhost"; //host="127.0.0.1" 也是可以的
const string user="connector";
const string passwd="xxx";
const string db="conn";
unsigned int port=xxx; //自己的端口號不要暴露int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL* my=mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if(mysql_real_connect(my,host.c_str(),user.c_str(),passwd.c_str(),db.c_str(),port,nullptr,0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}cout<<"connect success"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
自此我們完成了連接mysql的第工作
- 初始化mysql,構建mysql對象,得到mysql訪問句柄
- 進行mysql 的connect連接
- 用完mysql之后close
然后就可以對mysql下達sql指令了。這個下達sql指令的函數是mysql_query對數據庫下達指令。第一個參數就是剛才的MYSQL對象,第二個參數就是下達的sql指令。以前我們在命令行寫單sql后面會帶;,但是在這里并不需要帶;or \g,成功時返回0,失敗時返回非0。
在此之前可以自己先創建一個表
create table user(id bigint primary key auto_increment,name varchar(32) not null,age int not null,telphone varchar(32) unique);
之前我們學過一個sql指令,查看正在連接數據庫的用戶
show processlist;
可以看到我們C/C++庫確實連接到數據庫了
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;string sql;while (true){cout << " mysql> ";if (!getline(cin, sql) || sql == "quit"){cout << "Bye" << endl;break;}int n = mysql_query(my, sql.c_str());if (n == 0){cout << sql << " success" << endl;}else{cerr << sql << " error" << endl;return 3;}}// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
這里名字我們先輸入英文,具體原因等會說。下達的這個sql執行可帶;或者不帶;。然后看到我們確實把信息插入到數據庫了。我們其實也可以自己搞一個mysql的客戶端。但是實際上我們也不會這樣做,別人自己有客戶端。我們直接向數據庫下達sql指令就行了。
更新
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}string sql="update user set name='jim' where id=2";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
執行一下,看到id=2的名字確實更新為jim了。
所以未來我們所寫的任何的關于數據庫方面的程序,你想使用數據庫,你可以提前建好數據庫賬號然后賦權,并且建表。然后編寫對應C/C++代碼。然后就可以為你的上層進行數據層面上的支撐了。
插入
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}//string sql="update user set name='jim' where id=2";string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
刪除
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}//string sql="update user set name='jim' where id=2";//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";string sql="delete from user where id=1";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
單sql本身就是事務,mysql默認事務提交是自動的,所以今天你可以用一個連接訪問數據庫,也可以創建十個連接訪問數據庫,你可以用命令行方式訪問數據庫,也可以用C/C++訪問數據庫。所以數據庫可以存在多個訪問,因為有事務,事務有ACID能保證原子性等等。所以我們在應用層就不用擔心業務代碼執行時出現問題。
mysql_query這個函數現在可以增刪改,那查行不行呢?
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}//string sql="update user set name='jim' where id=2";//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";//string sql="delete from user where id=1";string sql="select * from user";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
別人告訴我成功了啊,然后呢?
在我們數據庫語句中,增刪改是最簡單的,因為增刪改只需要保證成功即可,那么增刪改操作一定能完成。而select是最不好處理的,因為當成功執行select語句,只是執行完sql第一步,那select后的數據在那呢?你是不是要把數據給顯示處理或者說讓用戶獲取到,所以還需要在做后面的工作。所以select還有后序獲取數據包括把數據在交給它的調用層。而不像增刪改執行完就完了。
還有一個問題,剛剛在插入的時候,name插入都是英文,那我們插入中文會怎么樣呢?
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}//string sql="update user set name='jim' where id=2";//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";string sql="insert into user (name,age,telphone) values ('張三',18,11187654321)";//string sql="delete from user where id=1";//string sql="select * from user";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
我們插入中文時亂碼了!
亂碼問題怎么解決?我們的數據庫編碼格式都配過是utf8的。出現亂碼問題,一定是因為客戶端和服務端對編碼問題沒有形成一致。比如說服務端用的是utf8,客戶端用的是latin1。因為我們數據庫編碼都是utf8的,所以一定是客戶端出現了問題。
建立好鏈接之后,獲取英文沒有問題,如果獲取中文是亂碼:設置鏈接的默認字符集是utf8,原始默認是latin1
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// 設置編碼格式mysql_set_character_set(my, "utf8");//string sql="update user set name='jim' where id=2";//string sql="insert into user (name,age,telphone) values ('peter',19,19187654321)";string sql="insert into user (name,age,telphone) values ('李四',18,12187654321)";//string sql="delete from user where id=1";//string sql="select * from user";int n=mysql_query(my,sql.c_str());if(n == 0) cout<<sql<<" success"<<endl;else cout<<sql<<" error"<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
設置好編碼,在插入中文就沒問題了。
自此我們就已經能夠進行mysql對象初始化,mysql的連接,mysql的關閉,以及通過mysql_query向數據庫下達對應的指令了。下面我們就解決拿到select的數據問題。
3.查詢的處理細節
查sql本身能夠被正確執行,但是執行之后怎么把查的數據交給上層返回給客戶或者把它的數據顯示處理呢?那在執行select的時候要把select的結果通過mysql_query查完之后把結果應該讓我們怎么獲取到呢?實際上mysql_query在執行select的時候會把結果保存到MYSQL這個句柄中。這個句柄是一個結構體它本身里面會包含對應的保存數據的緩存區區域。但是這部分數據依舊要被我們提取出來的。我們通過mysql_store_result這個函數來將結果進行轉儲。這個函數提取并且轉儲到結果集中。會把結果按照條目放置好。
是從MYSQL這個句柄中把結果轉儲到MYSQL_RES這個結構中。
把查詢結果按照行為單位,全部都放進結果集中。
成功返回MYSQL_RES對象,失敗返回nullptr。也可以采用mysql_errno和mysql_error來進行判斷出錯原因。
下面我們看看怎么用這個函數
首先先將結果轉儲到MYSQL_RES結構體中。
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設置編碼格式mysql_set_character_set(my, "utf8");string sql="select * from user";int n=mysql_query(my,sql.c_str());if(n == 0) {cout<<sql<<" success"<<endl;}else{cout<<sql<<" error"<<endl;return 3;} //轉儲MYSQL_RES* res=mysql_store_result(my);if(res == nullptr){cerr<<"mysql_store_result error"<<endl;return 4;}// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
那MYSQL_RES這個結構體應該如何理解呢?
首先我們要知道我們查出來的是一種表結構,表結構中行列關系是我們打印出來的,尤其是這些分割符,這些東西我們自己打印出來的,并不是在mysql在表中真實存在的。
在mysql表中其實存在兩類信息,一類是存儲表結構中的列屬性或者列名稱,還有一類是存儲表中的內容。
我們先看內容,這些內容以一定格式先放到MYSQL結構體對象內部,然后在轉儲到MYSQL_RES結構中。轉儲是為了更好的便于做二次處理。
在表中查出來的數據,就一定是在MYSQL內部在內存中保存這部分內容的。mysql將所有數據,讀取出來的時候,全部都當作字符串。 雖然以前它們什么int,char等,但是一旦把數據讀到自定義緩存區中的時候,那么它一定是把所有數據都當作字符串來看待。以前約束類型只是在插入的時候要處理,讀取出來當當作字符串處理。
然后MYSQL_RES要把結果歸置一下,我可以把MYSQL_RES想象成一個存著char**的指針數組。數組大小代表篩選出來的數據記錄有多少行。一條記錄里面有多列,每一列都是字符串。
然后這個數組中每一個char** 指針都指向一個存訪char類型的指針數組,指向的是這個數組中首個元素的地址。char** 的指針數組大小代表的是數據記錄有多少行,而每一行char 的指針數組里的char* 依次指向一條記錄的每一列。
所以未來想提取一整行,想提取一整行中的列,我可以按照行列去提取!
我們可以把MYSQL_RES當作一個char**xx[]數組去理解,或者當作一個char*[][]數組去理解都可以,怎么方便怎么來。縱向去遍歷每一行,橫向去遍歷一行中每一列。就可以找所有查詢結果都找到。
未來MYSQL_RES就以這樣的方式把查詢的結果保存起來。
那結果最終怎么遍歷呢?首先我們要知道這個轉儲的結果有多少行,有多少列!然后按照行列去遍歷。
獲取結果行數mysql_num_rows
參數是MYSQL_RES,從MYSQL_RES結果集中拿結果有多少行。
my_ulonglong mysql_num_rows(MYSQL_RES *res);
獲取結果列數mysql_num_fields
unsigned int mysql_num_fields(MYSQL_RES *res);
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設置編碼格式mysql_set_character_set(my, "utf8");string sql="select * from user";int n=mysql_query(my,sql.c_str());if(n == 0) {cout<<sql<<" success"<<endl;}else{cout<<sql<<" error"<<endl;return 3;} MYSQL_RES* res=mysql_store_result(my);if(res == nullptr){cerr<<"mysql_store_result error"<<endl;return 4;}my_ulonglong row=mysql_num_rows(res);my_ulonglong col=mysql_num_fields(res);cout << "行: " << rows << endl;cout << "列: " << cols << endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
目前這個表不就是3行4列嗎
那結果怎么拿?遍歷每一行每一列。一行一列的把結果都拿到。
mysql為了很好的支持遍歷,它內部還提供一種數據結構叫做MYSQL_ROW,每次拿到一行。
獲取結果內容mysql_fetch_row
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
這個函數就向以前學過的迭代器,當我們第一次調用mysql_fetch_row它內部會為我們維護當前遍歷的第一行,把第一行的結果集以MYSQL_ROW形式返回。當遍歷第二行的時候,只需要在調用一次mysql_fetch_row,不用自己維護行下標,它會自動指向下一行。就如迭代器一樣,只需要按照特定的次數進行遍歷調用就會依次把元素遍歷一遍,特別像迭代器。
可以看到MYSQL_ROW其實就是一個char指針,因為只有char才能指向第一行第二行等等。
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設置編碼格式mysql_set_character_set(my, "utf8");string sql = "select * from user";int n = mysql_query(my, sql.c_str());if (n == 0){cout << sql << " success" << endl;}else{cout << sql << " error" << endl;return 3;}MYSQL_RES *res = mysql_store_result(my);if (res == nullptr){cerr << "mysql_store_result error" << endl;return 4;}//下面都是和結果集有關的, MYSQL_RESmy_ulonglong rows = mysql_num_rows(res);my_ulonglong cols = mysql_num_fields(res);cout << "行: " << rows << endl;cout << "列: " << cols << endl;//拿結果 for(int i=0;i<rows;++i){MYSQL_ROW line=mysql_fetch_row(res);for(int j=0;j<cols;++j){cout<<line[j]<<"\t"; }cout<<endl;}// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
現在我們就可以把select的結果都拿到了!
剛才說了在獲取msyql表內容的時候,我們除了獲取表格中內容,它的列屬性我們也想獲取。想知道它是那一列的,或者想知道當前查的是那張表,那個數據庫。怎么辦呢?我們有另一個接口。
獲取列名mysql_fetch_fields
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);
與mysql_fetch_fields相對的還有一個mysql_fetch_field接口,mysql_fetch_fields以數組形式返回的是所有的列屬性對應的結構體。每一列屬性都是一個結構體。
mysql_fetch_field可以讓你依次遍歷所有列,以列為單位,第一次是第一列,第二次是第二列,也就像迭代器。
這里我們就不麻煩了,直接一次獲得所有列屬性。然后可以遍歷所有列把列名稱打印出來。
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設置編碼格式mysql_set_character_set(my, "utf8");string sql = "select * from user";int n = mysql_query(my, sql.c_str());if (n == 0){cout << sql << " success" << endl;}else{cout << sql << " error" << endl;return 3;}MYSQL_RES *res = mysql_store_result(my);if (res == nullptr){cerr << "mysql_store_result error" << endl;return 4;}//下面都是和結果集有關的, MYSQL_RESmy_ulonglong rows = mysql_num_rows(res);my_ulonglong cols = mysql_num_fields(res);cout << "行: " << rows << endl;cout << "列: " << cols << endl;//屬性MYSQL_FIELD* fields=mysql_fetch_fields(res);for(int i=0;i<cols;++i){cout<<fields[i].name<<"\t";}cout<<endl;//內容for(int i=0;i<rows;++i){MYSQL_ROW line=mysql_fetch_row(res);for(int j=0;j<cols;++j){cout<<line[j]<<"\t"; }cout<<endl;}// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
自此列名稱我們就拿到了。
如果你將來想獲取什么屬性就直接去獲取。
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設置編碼格式mysql_set_character_set(my, "utf8");string sql = "select * from user";int n = mysql_query(my, sql.c_str());if (n == 0){cout << sql << " success" << endl;}else{cout << sql << " error" << endl;return 3;}MYSQL_RES *res = mysql_store_result(my);if (res == nullptr){cerr << "mysql_store_result error" << endl;return 4;}//下面都是和結果集有關的, MYSQL_RESmy_ulonglong rows = mysql_num_rows(res);my_ulonglong cols = mysql_num_fields(res);cout << "行: " << rows << endl;cout << "列: " << cols << endl;//屬性MYSQL_FIELD* fields=mysql_fetch_fields(res);for(int i=0;i<cols;++i){cout<<fields[i].name<<"\t";}cout<<endl;//內容for(int i=0;i<rows;++i){MYSQL_ROW line=mysql_fetch_row(res);for(int j=0;j<cols;++j){cout<<line[j]<<"\t"; }cout<<endl;}cout<<fields[0].db<<" "<<fields[0].table<<endl;// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
最終我們就把mysql的查詢結果獲取到,然后轉儲到結果集,然后從結果集中提取行列,還有其他屬性和內容。現在我們對增刪查改就有一個完整的認識了。
但是還有一個細節mysql_store_result會把查詢結果由MYSQL轉儲到MYSQL_RES里,MYSQL_RES一定要為我們malloc一大堆空間,所以我們一定要記的 free(result),不然是肯定會造成內存泄漏的。我們不能直接用C的free或者C++的delete,而用的是mysql庫提供的這個函數,把結果集中空間釋放掉。
int main()
{// 1. 初始化mysql,構建mysql對象,得到mysql訪問句柄MYSQL *my = mysql_init(nullptr);if (my == nullptr){cerr << "init MySQL error" << endl;return 1;}// 2.連接數據庫if (mysql_real_connect(my, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr){cerr << "connect MySQL error" << endl;return 2;}// cout<<"connect success"<<endl;// 設置編碼格式mysql_set_character_set(my, "utf8");string sql = "select * from user";int n = mysql_query(my, sql.c_str());if (n == 0){cout << sql << " success" << endl;}else{cout << sql << " error" << endl;return 3;}MYSQL_RES *res = mysql_store_result(my);if (res == nullptr){cerr << "mysql_store_result error" << endl;return 4;}//下面都是和結果集有關的, MYSQL_RESmy_ulonglong rows = mysql_num_rows(res);my_ulonglong cols = mysql_num_fields(res);cout << "行: " << rows << endl;cout << "列: " << cols << endl;//屬性MYSQL_FIELD* fields=mysql_fetch_fields(res);for(int i=0;i<cols;++i){cout<<fields[i].name<<"\t";}cout<<endl;//內容for(int i=0;i<rows;++i){MYSQL_ROW line=mysql_fetch_row(res);for(int j=0;j<cols;++j){cout<<line[j]<<"\t"; }cout<<endl;}cout<<fields[0].db<<" "<<fields[0].table<<endl;//釋放結果集mysql_free_result(res);// 3.用完之后close,釋放資源mysql_close(my);return 0;
}
可以看到對于讀取的處理其實是挺麻煩的。
我們常用的接口就這些,對于一般數據庫讀取沒有任何問題。
最后,mysql C api還支持事務等常用操作,大家下來自行了解:
my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
my_bool STDCALL mysql_commit(MYSQL * mysql);
my_bool STDCALL mysql_rollback(MYSQL * mysql)
目前為止所有連接數據庫的操作,我們已經有第二種方案。以前就是mysql命令行式的、剛剛就是mysql的C/C++的連接方案,我們還可以通過圖形化界面連接數據庫。
4.圖形化界面訪問數據庫
這里推薦幾個好用的圖形化界面。
Nacicat是一個桌面版的mysql數據庫管理和開發工具,以圖形化界面顯示數據庫的操作。雖然這個工具非常好用,但遺憾的是,這個軟件是收費的。可以自己在網上找找它的破解版。
SQLyog 也是一個易于使用的、快速而簡潔的圖形化管理MYSQL數據庫的工具,它能夠在任何地點有效地管理你的數據庫。但同樣的,雖然它用起來很舒服,但它也是收費的
MYSQL Workbench是mysql官方提供的數據庫管理和開發工具,支持圖形化界面。雖然它在使用上并沒有上面的Navicat和SQLyog好用,但優點就是它是免費的。這里我們重點就用它。
4.1下載MYSQL Workbench
我們可以直接去mysql官網上去找到它,然后下載。
往下翻找到這個然后進去
找到MySQL Workbench然后進去
選擇對應的操作系統,直接下載就行了
下好之后運行就是這個樣子
4.2MYSQL Workbench遠程連接數據庫
MYSQL Workbench也是一個客戶端,說白了它和我們自己寫的C/C++客戶端和曾經mysql對應的命令行客戶端是沒有任何差別的,它也要進行網絡請求,也要進行登錄。所以mysql一定要允許這個用戶進行遠程登錄。
因此我們在創建一個允許遠程登錄的賬號。
創建一個允許用戶從任意地點登錄。這個任意登錄這件事情,雖然我們這樣寫了,一般在公司內允許那一臺機器訪問數據庫可以把這個IP地址確定下來不用填%,今天我們用的云服務器,我們內網經過NAT路由器轉發IP地址會發生改變。本地IP配上去沒有任何意義,所以這里只能用%。
create user 'workbench'@'%' identified by 'xxx';
然后在給新用戶賦權。把conn數據庫所有表的權限都給這個用戶。
grant all on conn.* to 'workbench'@'%';
然后才能遠程連接,點擊這里+,配置一下相關信息
我們主要填的就是IP地址,端口號,和用戶。還可以給這個連接起個名字。
配置好,然后點擊下面的,代表測試一下連接,它會要求輸入對應的密碼,直接把這個用戶的密碼輸入就行了。
如果成功連接在點擊ok就是下面這樣,如果連接不上可能是你端口號沒有放出來。自己放一下。如果想登錄的話,點擊一下這個出來的東西,然后就登錄進去了。
進去之后我們可以看到對應表結構、視圖,存儲過程和函數這些。我們現在只用關系表結構就可以了。表結構里面對應的列、索引、外鍵、觸發器這些。
列里面可以看到對應列的屬性,如id列的主機和自增長
下面我們可以在框里寫一些sql指令了。寫完之后選中或者光標放在你寫的sql這里然后點擊這個閃電執行它。然后下面就可以看到對應的信息。
雖然光標放在后面點擊閃電也可以執行,但是它是從上到下開始執行的,如果只需要執行某一個sql指令,還是要選擇它然后在去執行。
接下來我們可以查一下這個表,可以看到這里擔心數據太多,默認只把0-1000行篩選出來。
然后可以直接在這里對數據進行插入,然后再點擊Apply執行一下
Apply會把剛剛在圖形化界面這里做的動作變成sql的語句,然后在點Apply執行。
然后在點Finsh,這個插入就算真正執行完成了
然后我們查一下,確實看到是插入了
接下里我們想把張三這條記錄刪除一下,可以直接在圖形化界面刪除,然后還是和上面一樣的流程Apply。
然后這看到確實可以刪除。
更新也是一樣在圖形化界面改,然后Apply執行一下。我們把李四年齡改成90歲。
看到確實也是改了
換言之,我們就不用自己寫sql了,拿到表結構對它進行增刪查改,直接在里面手改就行了。這就是圖形化界面的好處。
如果想把自己寫的一大堆sql指令保存一下,可以ctrl+s保存。形成一個.sql文件。