C/C++ 连接访问 MySQL数据库
前面我们已经讲述了MySQL的基础使用,现在我们来看一下如何使用语言来操作数据库。在实际开发中,语言连接MySQL是为了能够在编程语言中与MySQL数据库进行交互和操作。大部分情况我们都是通过语言连接MySQL,建立与MySQL数据库的连接,可以向MySQL数据库发送更新请求,如插入、更新或删除数据。本篇文章主要讲解使用C语言连接MySQL数据库,当然在C++中也能够使用,因为C++兼容C语言的。
文章目录
🙋♂️ 作者:@Ggggggtm 🙋♂️
👀 专栏:MySQL 👀
💥 标题:C/C++ 连接访问 MySQL💥
❣️ 寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景 ❣️
一、连接MySQL
1、1 MySQL官网下载库
要使用C语言连接mysql,需要使用mysql官网提供的库。MySQL官网:MySQL。如下图:
然后点击DOWNLOADS,如下图:
往下寻找MySQL Community Downloads,如下图:
因为我们使用C语言连接MySQL,所以查找C API,如下图:
然后点击如下图:
我个人所用的是Linux,所以选择Linux的安装包。可根据自己的环境进行选择,如下图:
再次选择OS合适的版本,如下图:
当我们下载后,采用rz -E的方法可将压缩包传输到我们的Linux上。然后再使用 tar -xzf进行解压即可。解压后我们再看其中的文件,如下图:
其中对我们有用的就是头文件和库函数了。具体怎么进行使用该头文件和库函数,可以参照文章:动静态库的原理与制作详解。这里就不再过多解释。
但是我们也不建议使用以上方法,因为其中可能会产生兼容性等问题。我们接着往下看。
1、2 安装对应开发库
当我们再Linux安装了mysql的开发环境时,就会自动给我们安装对应的语言连接mysql 的头文件与库函数。我们可以进行查看。具体查看方法如下:
- 查看头文件:
- 查看对应的库:
如果上述都能够查看到,证明就已经安装了对应的开发库。一般情况下都是有的,如果没有的话,可以进行自己安装,如下图:
我这里已经安装过了,所以最后显示的是Nothing to do。下面我们来验证一下看是否安装成功。代码如下:
#include <iostream> #include <mysql/mysql.h> int main() { printf("mysql client Version: %s\n", mysql_get_client_info()); return 0; }
运行结果如下图:
到这里就证明已经安装成功对应的开发库了,且可以正常使用了。这里再说明一下:
- 我们再包含头文件时,只需要包含mysql.h即可。但是需要添加上对应的mysql/mysql.h。因为g++编译器只会去include路径下寻找,而我们的头文件在include路径下的mysql文件夹中。
- mysql_get_client_info()函数就是过去自身版本信息的一个函数。
- 我们在使用g++进行编译时,还需要指明对应的库文件的搜索路径和所使用的库名称。g++编译器在编译时,只会寻找C++相关的库和系统自带的库。而我们自己安装的需要指明库的搜索路径和所用到的库名称。简单理解,就是告诉g++,我们所用到的库的路径。代码如下:
g++ -o mytest mytest.cc -std=c++11 -L /lib64/mysql -lmysqlclient
二、MySQL接口介绍
2、1 创建和释放 MYSQL 对象
函数原型:
MYSQL *mysql_init(MYSQL *mysql)
该函数接受一个MYSQL类型的指针作为参数,一般我们都设置为NULL。并返回一个MYSQL类型的指针,这个指针指向初始化后的MySQL连接句柄。
具体来说,mysql_init()函数完成以下几个任务:
分配内存空间:它会为MYSQL结构体类型的变量分配足够的内存空间。MYSQL结构体包含了与MySQL服务器通信所需的各种信息和状态。
初始化结构体成员:它会将MYSQL结构体中的各个成员初始化为默认值。这些成员包括连接属性、错误信息等。
返回连接句柄:它会返回指向初始化后的MYSQL结构体的指针,我们可以将这个指针保存下来,在后续的MySQL操作中使用。
MYSQL结构体中包含了众多属性信息,具体如下:
typedef struct st_mysql { NET net; /* Communication parameters */ unsigned char *connector_fd; /* ConnectorFd for SSL */ char *host,*user,*passwd,*unix_socket,*server_version,*host_info; char *info, *db; struct charset_info_st *charset; MYSQL_FIELD *fields; MEM_ROOT field_alloc; my_ulonglong affected_rows; my_ulonglong insert_id; /* id if insert on table with NEXTNR */ my_ulonglong extra_info; /* Not used */ unsigned long thread_id; /* Id for connection in server */ unsigned long packet_length; unsigned int port; unsigned long client_flag,server_capabilities; unsigned int protocol_version; unsigned int field_count; unsigned int server_status; unsigned int server_language; unsigned int warning_count; struct st_mysql_options options; enum mysql_status status; my_bool free_me; /* If free in mysql_close */ my_bool reconnect; /* set to 1 if automatic reconnect */ /* session-wide random string */ char scramble[SCRAMBLE_LENGTH+1]; my_bool unused1; void *unused2, *unused3, *unused4, *unused5; LIST *stmts; /* list of all statements */ const struct st_mysql_methods *methods; void *thd; /* Points to boolean flag in MYSQL_RES or MYSQL_STMT. We set this flag from mysql_stmt_close if close had to cancel result set of this object. */ my_bool *unbuffered_fetch_owner; /* needed for embedded server - no net buffer to store the 'info' */ char *info_buffer; void *extension; } MYSQL;
需要注意的是,mysql_init()函数不会创建实际的连接;只是初始化了一个连接句柄,我们还需要通过其他函数(如mysql_real_connect())来建立与MySQL服务器的实际连接。后续会详细解释。
在连接使用完成后,我们还需要通过mysql_close()函数关闭连接并释放内存。函数原型如下:
void mysql_close(MYSQL *connection);
参数:
- connection:指向一个已经打开的
MYSQL
对象的指针。该对象代表与MySQL服务器的连接。该函数没有返回值。注意事项:
- 在调用mysql_close之前,确保已经完成了与数据库的所有操作,包括查询和事务等。
- 关闭连接后,将无法再使用该连接对象执行任何操作,除非重新使用mysql_real_connect建立新的连接。
- 对于多个线程同时共享一个连接对象的情况,应该确保在多个线程之间正确地协调和同步连接的打开和关闭操作,以避免出现竞态条件和意外错误。
2、2 连接mysql
初始化完毕之后,必须先链接数据库,在进行后续操作。(mysql网络部分是基于TCP/IP的)
函数原型:
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服务器的连接相关信息。host
:MySQL服务器的主机名或IP地址。user
:连接MySQL服务器所使用的用户名。passwd
:连接MySQL服务器所使用的密码。db
:连接成功后要使用的默认数据库名。port
:MySQL服务器的端口号。unix_socket
:Unix域套接字路径,用于本地连接Unix系统上的MySQL服务器,一般情况都属设置为nullptr。client_flag
:客户端标志位,用于指定连接选项。返回值:
- 连接成功时,返回一个新的MYSQL对象指针,该对象与第一个参数的值相同,用于后续的MySQL操作。
- 连接失败时,返回NULL,并通过调用mysql_errno()和mysql_error()函数获取错误代码和错误信息。
使用mysql_real_connect函数,可以实现以下功能:
- 与MySQL服务器建立起连接。
- 指定要连接的数据库。
接下来我们就来看一个实际的连接mysql的例子,代码如下:
#include <iostream> #include <string> #include <cstdio> #include <cstring> #include <mysql/mysql.h> using namespace std; const string Host = "127.0.0.1"; const string User = "gtm"; const string Passwd = "123654"; const string Db = "test"; unsigned int Port = 3306; int main() { MYSQL* my = mysql_init(NULL); if(my == nullptr) { cerr << "mysql init error!" << endl; exit(1); } cout << "mysql init success!" << endl; if(mysql_real_connect(my, Host.c_str(), User.c_str(), Passwd.c_str(), Db.c_str(), Port, nullptr, 0) ==nullptr) { cerr << "mysql connect error!" << endl; exit(2); } cout << "mysql connect success!" << endl; mysql_close(my); cout << "mysql close success!" << endl; return 0; }
我们来看一下运行结果,如下图:
2、3 发送sql语句
前面都是准备工作,现在我们可以向mysql发送sql指令了。函数原型:
int mysql_query(MYSQL *mysql, const char *query)
该函数接受两个参数:一个是MYSQL结构体指针,代表与MySQL服务器的连接;另一个是一个字符串指针,代表要执行的SQL查询语句。
函数返回一个整数值,用于表示查询执行的结果。如果执行成功,返回0;如果执行失败,返回非零值,表示出错的类型。在执行过程中,可以通过调用mysql_error()函数获取错误信息。
稍微总结一下,使用mysql_query()函数执行SQL查询的一般步骤如下:
建立与MySQL服务器的连接,可以使用mysql_init()函数初始化一个MYSQL结构体,并通过mysql_real_connect()函数建立连接。
构造要执行的SQL语句,以字符串形式传递给mysql_query()函数。
调用mysql_query()函数执行SQL语句。
根据返回值判断查询执行结果,如果返回0,则表示执行成功;如果返回非零值,则表示执行失败。
我们现在查看一下gtm用户下test数据库中都有哪些表,如下图:
test库正式我们所连接的库。我们现在通过mysql_query函数向t1表中插入数据,我们先来看一下t1表中现有的数据,如下图:
我们再来插入数据,代码如下:
#include <iostream> #include <cstdlib> #include <string> #include <cstdio> #include <cstring> #include <mysql/mysql.h> using namespace std; const string Host = "127.0.0.1"; const string User = "gtm"; const string Passwd = "gao520918"; const string Db = "test"; unsigned int Port = 3306; int main() { MYSQL* my = mysql_init(NULL); if(my == nullptr) { cerr << "mysql init error!" << endl; exit(1); } cout << "mysql init success!" << endl; if(mysql_real_connect(my, Host.c_str(), User.c_str(), Passwd.c_str(), Db.c_str(), Port, nullptr, 0) ==nullptr) { cerr << "mysql connect error!" << endl; exit(2); } cout << "mysql connect success!" << endl; const char* sql = "insert into t1 values(18,'xiaogao','男')"; int n = mysql_query(my, sql); if(n != 0) { cout << "mysql query error!" << endl; exit(3); } cout << "mysql query success!" << endl; mysql_close(my); cout << "mysql close success!" << endl; return 0; }
我们再来看插入的结果,如下图:
通过上图可以看到,连接成功了但是下达指令失败了。我们检查了语法并没有错误,那是什么原因呢?注意:我们使用的语言插入数据时,还需要设置客户端与服务器之间的字符集。设置字符集函数原型如下:
int mysql_set_character_set(MYSQL *mysql, const char *charset)
参数说明:
mysql
:已初始化的MYSQL结构体指针,表示与MySQL服务器的连接。charset
:一个字符串,表示要设置的字符集名称。返回值:
- 设置成功返回0,否则返回非零值。
我们知道,远端的mysql的字符集时utf8,那么在来接成功后设置一下字符集即可。代码如下:
mysql_set_character_set(my, "utf8");
我们再次进行插入,如下图:
我们看到,现在就可以插入成功了。再看一下数据是否插入到对应的表中,如下图:
其实删除和更新的sql语句都与插入语句相同,直接以字符串形式传递给mysql_query()函数。但是我们想查询呢?同时还需要获取查询结果该怎么办呢?
mysql_store_result 函数用于将查询结果保存在一个客户端可访问的缓冲区中。它用于处理从数据库中检索的结果集。函数原型如下:
MYSQL_RES* mysql_store_result(MYSQL *mysql);
参数说明:
mysql
:一个指向MYSQL结构体的指针,表示与MySQL服务器的连接。返回值:
- 成功时,返回一个指向MYSQL_RES结构体的指针,表示查询结果集。
- 失败时,返回NULL。
使用mysql_store_result函数的具体步骤如下:
- 在执行查询语句后,调用mysql_store_result函数来获取查询结果集。
- 判断返回值是否为NULL,如果为NULL,则表示查询出错。
- 通过返回的MYSQL_RES指针,可以使用其他函数来获取和处理结果集的数据。
MYSQL_RES变量中保存了查询得到的各种信息,其类型定义如下:
typedef struct st_mysql_res { my_ulonglong row_count; MYSQL_FIELD *fields; MYSQL_DATA *data; MYSQL_ROWS *data_cursor; unsigned long *lengths; /* column lengths of current row */ MYSQL *handle; /* for unbuffered reads */ const struct st_mysql_methods *methods; MYSQL_ROW row; /* If unbuffered read */ MYSQL_ROW current_row; /* buffer to current row */ MEM_ROOT field_alloc; unsigned int field_count, current_field; my_bool eof; /* Used by mysql_fetch_row */ /* mysql_stmt_close() had to cancel this result */ my_bool unbuffered_fetch_cancelled; void *extension; } MYSQL_RES;
我们知道查询的结果会保存在MYSQL_RES变量中,但是怎么提取其中的数据呢?我们知道在MySQL中查询的结果是一张二维表,如下图:
那么我们首先要做的就是获取表中数据的行数和列数。其函数原型如下:
//获取查询结果的行数 my_ulonglong mysql_num_rows(MYSQL_RES *res); //获取查询结果的列数 unsigned int mysql_num_fields(MYSQL_RES *res);
- mysql_num_fields函数是一个用于获取结果集中字段(列)的数量的MySQL函数。它的作用是返回一个结果集中字段的数量,可以用于确定查询结果集中有多少列。
- mysql_num_fields函数的返回值是一个整数,表示结果集中的字段数量。如果发生错误或结果集为空,则返回0。
- mysql_num_rows函数是一个用于获取结果集中行的数量的MySQL函数。它的作用是返回一个结果集中行的数量,可以用于确定查询结果集中有多少行数据。
- mysql_num_rows函数的返回值是一个整数,表示结果集中的行数量。如果发生错误或结果集为空,则返回0。
这两个函数的参数均是mysql_store_result 函数的返回值。也就是我们所获取的查询结果集。只有行数和列数不够,我们还需要能够获取其中的内容,我们再来看一个函数,函数原型如下:
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
参数解释:
result
:MYSQL_RES
结构体指针,表示查询结果集。返回值:
MYSQL_ROW
:结果集中下一行的数据,以字符串数组的形式返回。如果已经读取完所有行,则返回NULL。实际上MYSQL_ROW就是一个char**的类型,其类型定义如下:
typedef char **MYSQL_ROW; /* return data as array of strings */
mysql_fetch_row函数也很像迭代器,当我们查找一行数据后,mysql_fetch_row会自动的向后移动一行,并不需要使用者自己去操作关心。接下来我们看一个查询的实例,具体代码如下:
#include <iostream> #include <cstdlib> #include <string> #include <cstdio> #include <cstring> #include <mysql/mysql.h> using namespace std; const string Host = "127.0.0.1"; const string User = "gtm"; const string Passwd = "gao520918"; const string Db = "test"; unsigned int Port = 3306; int main() { MYSQL* my = mysql_init(NULL); if(my == nullptr) { cerr << "mysql init error!" << endl; exit(1); } cout << "mysql init success!" << endl; if(mysql_real_connect(my, Host.c_str(), User.c_str(), Passwd.c_str(), Db.c_str(), Port, nullptr, 0) ==nullptr) { cerr << "mysql connect error!" << endl; exit(2); } cout << "mysql connect success!" << endl; mysql_set_character_set(my, "utf8"); const char* sql = "select * from t1"; int n = mysql_query(my, sql); int n = mysql_query(my, sql); if(n != 0) { cout << "mysql query error!" << endl; exit(3); } cout << "mysql query success!" << endl; MYSQL_RES* res = mysql_store_result(my); int row = mysql_num_rows(res); int col = mysql_num_fields(res); for(int i = 0; i < row; i++) { MYSQL_ROW line = mysql_fetch_row(res); for(int j = 0; j < col; j++) { cout << line[j] << "\t"; } cout << endl; } mysql_free_result(res); mysql_close(my); cout << "mysql close success!" << endl; return 0; }
运行结果如下图:
但是上述获取行信息中,并没有获取到列的名字。看起来并不完整,我们在看如下函数:
MYSQL_FIELD* mysql_fetch_fields(MYSQL_RES *res);
参数:
res
:一个指向MYSQL_RES类型的结果集指针。该结果集通常通过调用mysql_store_result()
或mysql_use_result()
函数从MySQL服务器获得。返回值:
- 如果成功,返回一个指向MYSQL_FIELD结构体数组的指针,其中每个元素代表结果集的一个字段。
- 如果失败,返回NULL。
mysql_fetch_fields函数返回一个指向MYSQL_FIELD结构体数组的指针。MYSQL_FIELD结构体包含每个列相关字段的详细信息,如字段名、字段类型、字段长度等。结构体具体内容如下:
typedef struct st_mysql_field { char *name; /* Name of column */ char *org_name; /* Original column name, if an alias */ char *table; /* Table of column if column was a field */ char *org_table; /* Org table name, if table was an alias */ char *db; /* Database for table */ char *catalog; /* Catalog for table */ char *def; /* Default value (set by mysql_list_fields) */ unsigned long length; /* Width of column (create length) */ unsigned long max_length; /* Max width for selected set */ unsigned int name_length; unsigned int org_name_length; unsigned int table_length; unsigned int org_table_length; unsigned int db_length; unsigned int catalog_length; unsigned int def_length; unsigned int flags; /* Div flags */ unsigned int decimals; /* Number of decimals in field */ unsigned int charsetnr; /* Character set */ enum enum_field_types type; /* Type of field. See mysql_com.h for types */ void *extension; } MYSQL_FIELD;
其用法也相当简单,我们获取到了列数,那么就可以直接打印即可,用法如下:
MYSQL_FIELD* fields = mysql_fetch_fields(res); for(int i = 0; i < col; i++) { cout << fields[i].name << "\t\t"; } cout << endl;
我们再来看一下运行结果,如下图;