C/C++ 连接访问 MySQL数据库

前面我们已经讲述了MySQL的基础使用,现在我们来看一下如何使用语言来操作数据库。在实际开发中,语言连接MySQL是为了能够在编程语言中与MySQL数据库进行交互和操作。大部分情况我们都是通过语言连接MySQL,建立与MySQL数据库的连接,可以向MySQL数据库发送更新请求,如插入、更新或删除数据。本篇文章主要讲解使用C语言连接MySQL数据库,当然在C++中也能够使用,因为C++兼容C语言的
文章目录

一、连接MySQL

[1、1 MySQL官网下载库](#1、1 MySQL官网下载库)

[1、2 安装对应开发库](#1、2 安装对应开发库)

二、MySQL接口介绍

[2、1 创建和释放 MYSQL 对象](#2、1 创建和释放 MYSQL 对象)

[2、2 连接mysql](#2、2 连接mysql)

[2、3 发送sql语句](#2、3 发送sql语句)


🙋‍♂️ 作者:@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 的头文件与库函数。我们可以进行查看。具体查看方法如下:

  1. 查看头文件:
  2. 查看对应的库:

如果上述都能够查看到,证明就已经安装了对应的开发库。一般情况下都是有的,如果没有的话,可以进行自己安装,如下图:

我这里已经安装过了,所以最后显示的是Nothing to do。下面我们来验证一下看是否安装成功。代码如下:

cpp 复制代码
#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++,我们所用到的库的路径。代码如下:

    bash 复制代码
    g++ -o mytest mytest.cc -std=c++11 -L /lib64/mysql -lmysqlclient

二、MySQL接口介绍

2、1 创建和释放 MYSQL 对象

函数原型:

cpp 复制代码
MYSQL *mysql_init(MYSQL *mysql)

该函数接受一个MYSQL类型的指针作为参数,一般我们都设置为NULL并返回一个MYSQL类型的指针,这个指针指向初始化后的MySQL连接句柄

具体来说,mysql_init()函数完成以下几个任务:

  1. 分配内存空间:它会为MYSQL结构体类型的变量分配足够的内存空间。MYSQL结构体包含了与MySQL服务器通信所需的各种信息和状态。

  2. 初始化结构体成员:它会将MYSQL结构体中的各个成员初始化为默认值。这些成员包括连接属性、错误信息等。

  3. 返回连接句柄:它会返回指向初始化后的MYSQL结构体的指针,我们可以将这个指针保存下来,在后续的MySQL操作中使用。

MYSQL结构体中包含了众多属性信息,具体如下:

cpp 复制代码
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()函数关闭连接并释放内存。函数原型如下:

cpp 复制代码
void mysql_close(MYSQL *connection);

参数:

  • connection:指向一个已经打开的MYSQL对象的指针。该对象代表与MySQL服务器的连接。

该函数没有返回值。注意事项:

  1. 在调用mysql_close之前,确保已经完成了与数据库的所有操作,包括查询和事务等。
  2. 关闭连接后,将无法再使用该连接对象执行任何操作,除非重新使用mysql_real_connect建立新的连接。
  3. 对于多个线程同时共享一个连接对象的情况,应该确保在多个线程之间正确地协调和同步连接的打开和关闭操作,以避免出现竞态条件和意外错误。

2、2 连接mysql

初始化完毕之后,必须先链接数据库,在进行后续操作。(mysql网络部分是基于TCP/IP的)

函数原型:

cpp 复制代码
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函数,可以实现以下功能:

  1. 与MySQL服务器建立起连接。
  2. 指定要连接的数据库。
    接下来我们就来看一个实际的连接mysql的例子,代码如下:
cpp 复制代码
#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指令了。函数原型:

cpp 复制代码
int mysql_query(MYSQL *mysql, const char *query)

该函数接受两个参数:一个是MYSQL结构体指针,代表与MySQL服务器的连接;另一个是一个字符串指针,代表要执行的SQL查询语句。

函数返回一个整数值,用于表示查询执行的结果。如果执行成功,返回0;如果执行失败,返回非零值,表示出错的类型。在执行过程中,可以通过调用mysql_error()函数获取错误信息。

稍微总结一下,使用mysql_query()函数执行SQL查询的一般步骤如下:

  1. 建立与MySQL服务器的连接,可以使用mysql_init()函数初始化一个MYSQL结构体,并通过mysql_real_connect()函数建立连接。

  2. 构造要执行的SQL语句,以字符串形式传递给mysql_query()函数。

  3. 调用mysql_query()函数执行SQL语句。

  4. 根据返回值判断查询执行结果,如果返回0,则表示执行成功;如果返回非零值,则表示执行失败。

我们现在查看一下gtm用户下test数据库中都有哪些表,如下图:

test库正式我们所连接的库。我们现在通过mysql_query函数向t1表中插入数据,我们先来看一下t1表中现有的数据,如下图:

我们再来插入数据,代码如下:

cpp 复制代码
#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;
}

我们再来看插入的结果,如下图:

通过上图可以看到,连接成功了但是下达指令失败了。我们检查了语法并没有错误,那是什么原因呢?注意:我们使用的语言插入数据时,还需要设置客户端与服务器之间的字符集。设置字符集函数原型如下:

cpp 复制代码
int mysql_set_character_set(MYSQL *mysql, const char *charset)

参数说明:

  • mysql:已初始化的MYSQL结构体指针,表示与MySQL服务器的连接。
  • charset:一个字符串,表示要设置的字符集名称。

返回值:

  • 设置成功返回0,否则返回非零值。

我们知道,远端的mysql的字符集时utf8,那么在来接成功后设置一下字符集即可。代码如下:

cpp 复制代码
mysql_set_character_set(my, "utf8");

我们再次进行插入,如下图:

我们看到,现在就可以插入成功了。再看一下数据是否插入到对应的表中,如下图:

其实删除和更新的sql语句都与插入语句相同,直接以字符串形式传递给mysql_query()函数。但是我们想查询呢?同时还需要获取查询结果该怎么办呢?
mysql_store_result 函数用于将查询结果保存在一个客户端可访问的缓冲区中。它用于处理从数据库中检索的结果集。函数原型如下:

cpp 复制代码
MYSQL_RES* mysql_store_result(MYSQL *mysql);

参数说明:

  • mysql:一个指向MYSQL结构体的指针,表示与MySQL服务器的连接。

返回值:

  • 成功时,返回一个指向MYSQL_RES结构体的指针,表示查询结果集。
  • 失败时,返回NULL。

使用mysql_store_result函数的具体步骤如下:

  1. 在执行查询语句后,调用mysql_store_result函数来获取查询结果集。
  2. 判断返回值是否为NULL,如果为NULL,则表示查询出错。
  3. 通过返回的MYSQL_RES指针,可以使用其他函数来获取和处理结果集的数据。

MYSQL_RES变量中保存了查询得到的各种信息,其类型定义如下:

cpp 复制代码
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中查询的结果是一张二维表,如下图:

那么我们首先要做的就是获取表中数据的行数和列数。其函数原型如下:

cpp 复制代码
//获取查询结果的行数
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 函数的返回值。也就是我们所获取的查询结果集。只有行数和列数不够,我们还需要能够获取其中的内容,我们再来看一个函数,函数原型如下:

cpp 复制代码
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);

参数解释:

  • resultMYSQL_RES 结构体指针,表示查询结果集。

返回值:

  • MYSQL_ROW:结果集中下一行的数据,以字符串数组的形式返回。如果已经读取完所有行,则返回NULL。

实际上MYSQL_ROW就是一个char**的类型,其类型定义如下:

cpp 复制代码
typedef char **MYSQL_ROW;		/* return data as array of strings */

mysql_fetch_row函数也很像迭代器,当我们查找一行数据后,mysql_fetch_row会自动的向后移动一行,并不需要使用者自己去操作关心。接下来我们看一个查询的实例,具体代码如下:

cpp 复制代码
#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;
}

运行结果如下图:

但是上述获取行信息中,并没有获取到列的名字。看起来并不完整,我们在看如下函数:

cpp 复制代码
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结构体包含每个列相关字段的详细信息,如字段名、字段类型、字段长度等。结构体具体内容如下:

cpp 复制代码
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;

其用法也相当简单,我们获取到了列数,那么就可以直接打印即可,用法如下:

cpp 复制代码
    MYSQL_FIELD* fields = mysql_fetch_fields(res);
    for(int i = 0; i < col; i++)
    {
        cout << fields[i].name << "\t\t";
    }
    cout << endl;

我们再来看一下运行结果,如下图;

相关推荐
做梦敲代码25 分钟前
达梦数据库-读写分离集群部署
数据库·达梦数据库
苹果醋31 小时前
2020重新出发,MySql基础,MySql表数据操作
java·运维·spring boot·mysql·nginx
小蜗牛慢慢爬行1 小时前
如何在 Spring Boot 微服务中设置和管理多个数据库
java·数据库·spring boot·后端·微服务·架构·hibernate
hanbarger1 小时前
nosql,Redis,minio,elasticsearch
数据库·redis·nosql
微服务 spring cloud2 小时前
配置PostgreSQL用于集成测试的步骤
数据库·postgresql·集成测试
先睡2 小时前
MySQL的架构设计和设计模式
数据库·mysql·设计模式
弗罗里达老大爷2 小时前
Redis
数据库·redis·缓存
仰望大佬0072 小时前
Avalonia实例实战五:Carousel自动轮播图
数据库·microsoft·c#
学不透java不改名2 小时前
sqlalchemy连接dm8 get_columns BIGINT VARCHAR字段不显示
数据库