使用 C 语言连接 MySQL 客户端(重点) - 实践

使用 C 语言连接 MySQL 客户端(重点) - 实践

为了方便后续重复编译源文件,可以在项目目录下创建一个Makefile,Makefile当中的内容如下:

# 目标文件:依赖文件

mysql_connect: mysql_connect.cc

g++ -o $@ $^ -std=c++11 -I ./include -L ./lib -l mysqlclient

.PHONY:clean

clean:

rm -f mysql_connect

编译时,使用 g++ 命令,指定库的包含路径 (-I) 和库文件路径 (-L),并链接 MySQL 客户端库 (-lmysqlclient):

说明一下:

-I:用于指明头文件的搜索路径。

-L:用于指明库文件的搜索路径。

-l:用于指明需要连接库文件路径下的哪一个库。比如:mysqlclient:要链接的库名,链接器会查找 libmysqlclient.so 或 libmysqlclient.a(掐头去尾,中间剩下的就是要链接的库)

Makefile编写完毕后,直接通过make命令即可编译代码生成可执行程序。如下:

但此时生成的可执行程序还不能直接运行,通过ldd命令可以看到,该可执行程序所依赖的mysqlclient库找不到。如下:

原因如下:

GCC/G++编译器默认采用动态链接方式,编译时会自动链接动态库,因此生成的可执行文件运行时需要加载对应的动态库。然而,我们使用的mysqlclient库并不在系统默认的搜索路径中。

需要注意的是,Makefile中的-I、-L和-l选项仅在编译阶段起作用(编译时只告诉在哪找这些文件进行编译,主要是用来编译操作,但是链接时这些文件编译器找不到,因为这些选项仅在编译阶段起作用),用于指明头文件和库文件的位置。一旦生成可执行文件后,这些选项就不再相关。

解决方法通常有三种(详细可回顾Linux系统的动静态库制作那部分,反正方法有很多种):

将库文件复制到系统默认的库文件搜索路径/lib64下

将库文件所在目录添加到LD_LIBRARY_PATH环境变量中,该变量用于指定额外的动态库搜索路径

将库文件目录路径写入.conf配置文件,将该文件放入/etc/ld.so.conf.d/目录,然后执行ldconfig命令更新配置。该目录下的所有配置文件路径都会被纳入动态库搜索范围

这里我们采用第三种方式进行解决(永久方案)。如下:

# 1. 创建库路径配置文件

echo /home/hmz/thirdPath/mysql_connector/lib > mysql_connect.conf

# 2. 将配置文件复制到系统配置目录

sudo cp mysql_connect.conf /etc/ld.so.conf.d/

# 3. 验证配置文件是否正确安装

ls /etc/ld.so.conf.d/

# 4. 更新系统库缓存

sudo ldconfig

命令1:创建库路径配置文件

echo /home/hmz/thirdPath/mysql_connector/lib > mysql_connect.conf

作用解析:

echo:输出文本内容到标准输出

/home/hmz/thirdPath/mysql_connector/lib:MySQL连接器库文件的实际路径

>:重定向操作符,将输出内容写入文件(覆盖方式)

mysql_connect.conf:创建的配置文件名

执行结果:

创建一个名为 mysql_connect.conf 的文件

文件内容只有一行:/home/hmz/thirdPath/mysql_connector/lib

这个路径告诉系统在哪里可以找到 MySQL 客户端库文件

conf文件内容示例:

/home/hmz/thirdPath/mysql_connector/lib

命令2:安装配置文件到系统目录

sudo cp mysql_connect.conf /etc/ld.so.conf.d/

作用解析:

sudo:以管理员权限执行命令(需要输入密码)

cp:复制文件命令

mysql_connect.conf:源文件(上一步创建的文件)

/etc/ld.so.conf.d/:目标目录,系统动态链接器配置目录

目录说明:

/etc/ld.so.conf.d/ 是 Linux 系统专门用于存放库路径配置的目录

系统启动时会自动读取该目录下所有 .conf 文件中的路径

这样配置可以让所有用户都能找到这些库文件

命令3:验证配置文件安装

ls /etc/ld.so.conf.d/

作用解析:

ls:列出目录内容

/etc/ld.so.conf.d/:要查看的目录路径

执行目的:

确认 mysql_connect.conf 文件已成功复制到系统配置目录

查看目录中已有的配置文件

验证操作是否成功

典型输出示例:

命令4:更新动态链接器缓存

sudo ldconfig

作用解析:

sudo:管理员权限

ldconfig:Linux 动态链接器配置工具

具体作用:

扫描配置路径:读取 /etc/ld.so.conf 和 /etc/ld.so.conf.d/ 中的所有配置

更新缓存:重建 /etc/ld.so.cache 文件(库文件索引缓存)

创建符号链接:为库文件创建必要的版本链接

内存映射:让系统运行时能快速找到所需的动态库

此时该可执行程序在链接阶段所依赖的mysqlclient库就能够被找到了。如下:

运行可执行程序后,可以看到客户端的版本为6.1.11,也就是刚才下载的库文件的版本。如下:

至此引入库的工作已经做完,接下来就是熟悉接口啦!!!想在C语言程序中连接MySQL数据库,并查询相关函数和接口的用法。就得会使用以下C语言连接MySQL(MySQL C API)的官方和实用资源:

MySQL C API 官方文档这是最权威的参考,包含了所有C语言连接MySQL所需的函数和数据结构:MySQL 8.0 C API 参考:https://dev.mysql.com/doc/c-api/8.0/en/

这个文档详细介绍了:

mysql_init(), mysql_real_connect() - 连接数据库

mysql_query(), mysql_store_result() - 执行查询和处理结果

mysql_fetch_row(), mysql_fetch_field() - 获取数据和元数据

mysql_error(), mysql_errno() - 错误处理

mysql_close() - 关闭连接

四、MySQL对象初始化

1、创建MySQL对象 在建立数据库连接前,需要先初始化MySQL对象(也就是我们平时在服务器上使用的MySQL客户端,它就是MySQL对象,集成了各种成员方法和成员变量可以被我们使用),使用以下函数:

MYSQL* mysql_init(MYSQL *mysql);

如: MYSQL *mfp = mysql_init(NULL);

2、函数说明该函数用于分配或初始化一个MySQL对象(MySQL客户端),用于连接MySQL服务器:

当参数为NULL时,函数会自动分配并返回一个新的MySQL对象(MySQL客户端)

当参数为有效地址时,函数会在该地址处完成对象初始化

3、MYSQL对象结构 MYSQL对象(MySQL客户端)包含多种连接相关信息,MYSQL对象中的methods变量是一个结构体变量,该变量里面保存着很多函数指针,这些函数指针将会在数据库连接成功以后的各种数据操作中被调用。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 数据库

函数 mysql_real_connect():创建完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);

1、参数说明mysql: 表示在连接数据库前,调用mysql_init函数创建的MySQL对象。

host: 数据库服务器地址(通常是 localhost 或 IP 地址)。表示需要连接的MySQL服务器的IP地址,"127.0.0.1"表示连接本地MySQL服务器。

user: 表示连接MySQL服务器时,所使用用户的用户名。

passwd: 表示连接MySQL服务器时,所使用用户的密码

db: 数据库名称。表示连接MySQL服务器后,需要使用的数据库。

port: 数据库端口(默认为 3306)。表示连接的MySQL服务器,所对应的端口号。

unix_socket: 用于 Unix 套接字连接的路径。表示连接时应该使用的套接字或命名管道,通常设置为NULL。

clientflag:客户端标志。可以设置为多个标志位的组合,表示允许特定的功能,通常设置为0。

2、返回值说明如果连接数据库成功,则返回一个MySQL对象,该对象与第一个参数的值相同。

如果连接数据库失败,则返回NULL。

六、关闭数据库连接

在完成数据库操作后,应当及时关闭数据库连接。关闭连接的函数声明如下:

void mysql_close(MYSQL *sock);

1、重要说明该函数参数应为通过mysql_init创建的MySQL连接对象

若MySQL对象由mysql_init自动创建,调用此函数将同时释放该对象内存

在学习上面的连接数据库和关闭数据库连接后,我们可以进行连接示例操作:初始化 MySQL 客户端后,我们需要连接到 MySQL 数据库。可以使用 mysql_real_connect() 函数建立连接,以下是一个简单的连接示例:比如使用如下代码连接我的MySQL服务器(此时是在我云服务器上用代码连接MySQL客户端,并不是在本地电脑上,这点要清楚!!!):(重点!!!)

#include

#include

#include

using namespace std;

const string host = "113.45.79.2";

const string user = "hmz";

const string passwd = "HMZhmz123!!!";

const string db = "test";

const int port = 3306;

int main()

{

//1、创建MySQL对象

MYSQL* ms = mysql_init(nullptr);

//2、连接数据库

if(mysql_real_connect(ms, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)

{

cerr << "数据库连接失败: " << mysql_error(ms) << endl; // 添加具体错误信息

return 1;

}

cout<<"数据库连接成功!"<

c_str()方法详解

const char* c_str() const noexcept;

作用:返回指向C风格字符串的指针

返回值:const char*类型,指向std::string内部数据的只读指针

特性:不分配新内存,直接返回内部缓冲区指针

2、版本差异导致的SSL兼容性问题(超重点!!!)如果我们此时使用make命令生成可执行程序,运行后并不是得到连接成功的结果,而是:

SSL连接是一种安全的网络通信协议,全称是Secure Sockets Layer(安全套接层),现在普遍使用其升级版TLS(Transport Layer Security)。SSL连接 = 加密通信!!!这个SSL连接错误 SSL connection error: unknown error number 是一个比较常见的MySQL SSL连接问题。

首先我们回顾上面,我们知道使用的MySQL客户端是6.1.11的,然而当前我使用的MySQL服务端却是8.0+版本的!!!这个是版本差异导致的SSL兼容性问题!!!

MySQL 8.0+ 服务端特性:默认启用并强制SSL加密连接、SSL配置更加严格、默认要求安全传输

MySQL 6.1.11 客户端特性:较老的客户端版本、可能使用旧的SSL/TLS协议、与MySQL 8.0+的SSL要求不兼容

在 MySQL 8.0+ 的配置文件 /etc/mysql/mysql.conf.d/mysqld.cnf 中添加 ssl=0 和 skip_ssl,然后重新启动MySQL服务端,如下:

vim /etc/mysql/mysql.conf.d/mysqld.cnf

#重新启动MySQL服务端

systemctl restart mysql

ssl=0

skip_ssl

3、作用说明ssl=0:显式禁用 SSL/TLS 加密连接、等同于 ssl=OFF 或 ssl=DISABLED

skip_ssl:跳过 SSL 功能初始化、完全禁用 SSL 相关功能

4、实际效果禁用加密连接:客户端与服务器之间的通信将不使用 SSL/TLS 加密

性能提升:避免了 SSL 握手和加密解密的开销

安全性降低:数据传输以明文方式进行,可能被窃听

5、注意事项(⚠️ 安全警告)在生产环境中禁用 SSL 可能存在安全风险

建议仅在测试环境或内部安全网络中使用!!!

如果必须禁用,确保网络环境可信

然后我们现在使用make命令再生成可执行程序,运行后即可看到连接数据库成功、关闭数据库成功!!!如下:

然后我们此时如果再想在本地MySQL客户端登录云服务器上的MySQL的话,这时会登录不了!!!报错结果如下,这个跟SSL是有关系的!!!:

有些提前了解过的大佬,可以会想到如果进行下面的SQL修改操作后再尝试本地登录会不会成功?为什么会认为这个方案会成功呢?原因有下面几点:

绕过 SSL 要求:mysql_native_password 插件不要求强制 SSL 连接

兼容禁用 SSL 的环境:与之前配置的 ssl=0 skip_ssl 兼容

客户端支持:所有 MySQL 客户端都支持这个传统的认证插件

ALTER USER 'hmz'@'%' IDENTIFIED WITH mysql_native_password BY '你的密码';

FLUSH PRIVILEGES;

但在我的本地电脑得出的结果如下,其主要原因是:这个错误表明本地的MySQL 服务器端缺少 mysql_native_password 插件。MySQL 8.0+ 可能没有编译或加载传统的 mysql_native_password 插件。原理上使用上面的直接修改为 mysql_native_password 插件是可以的!!!

第一个错误:caching_sha2_password

ERROR 2061: Authentication plugin 'caching_sha2_password' reported error

原因:本地客户端首选尝试使用 caching_sha2_password 插件

但服务端禁用了SSL,而该插件要求安全连接

结果:认证失败

第二个错误:mysql_native_password

ERROR 2059: Authentication plugin 'mysql_native_password' cannot be loaded

原因:第一个插件失败后,本地客户端回退尝试 mysql_native_password

但本地机器的客户端的 mysql_native_password 插件可能:文件缺失或损坏、版本不兼容、路径配置错误

所以上面的例子是我们学习过程中需要解决的问题,这里我学习用不到(后面使用到再解决,毕竟我也是菜鸡。。。),就自行解决了,详细大家可以上网查找解决方案!!!

mysql_native_password 是MySQL传统的认证插件,兼容性好但不要求SSL加密;而 caching_sha2_password 是MySQL 8.0默认的新插件,安全性更高但默认要求SSL安全连接。第一个问题正是本地客户端尝试使用需要SSL的新插件去连接已禁用SSL的服务端导致的冲突!!!然而第二个问题使用第二个插件时又可能因为本地机器的文件缺失或损坏、版本不兼容、路径配置错误不能导致连接!!!

七、设置字符集

MySQL 默认使用 latin1 字符集,如果你需要处理中文或其他多字节字符,可以使用 mysql_set_character_set() 设置字符集为 utf8:

1、函数定义

int mysql_set_character_set(MYSQL *mysql, const char *csname);

参数1: MYSQL *mysql数据库连接对象指针

必须是有效的、已建立的连接

参数2: const char *csname字符集名称字符串

常用值:

"utf8" 或 "utf8mb3" - UTF-8编码(3字节)

"utf8mb4" - UTF-8编码(4字节,支持emoji)

"gbk" - 中文编码

"latin1" - 西欧编码

"binary" - 二进制数据

返回值成功: 返回 0

失败: 返回非 0 值

2、函数作用mysql_set_character_set() 函数用于设置当前数据库连接的字符集。这个函数确保客户端、连接和结果集使用指定的字符集,避免乱码问题。

3、基本用法

#include

#include

using namespace std;

int main()

{

MYSQL *conn = mysql_init(nullptr);

// 连接数据库

if (mysql_real_connect(conn, "113.45.79.2", "hmz", "HMZhmz123!!!", "test", 3306, nullptr, 0))

{

// 设置字符集为UTF-8

if (mysql_set_character_set(conn, "utf8") == 0)

{

cout << "字符集设置成功: utf8" << endl;

}

else

{

cerr << "字符集设置失败: " << mysql_error(conn) << endl;

}

mysql_close(conn);

}

return 0;

}

重要结论:字符集设置确实生效了,但只对当前连接有效!字符集设置是连接级别的,每个新连接都会恢复为服务器默认设置!!!如下的查询可以证明和说明这一点:

SELECT

@@character_set_client as client,

@@character_set_connection as connection,

@@character_set_results as results;

4、为什么需要设置字符集?1. 字符集不匹配导致的乱码问题示例场景:

// 如果不设置字符集,可能出现以下情况:

// 插入中文数据

const char* sql = "INSERT INTO users (name) VALUES ('张三')";

mysql_query(conn, sql);

// 查询时显示乱码:�Ź� 或者 ???

2. 默认字符集问题MySQL 8.0 默认字符集是 utf8mb4

旧版本MySQL默认可能是 latin1

客户端默认字符集可能不匹配

八、下发SQL请求

函数说明:mysql_query在与数据库建立连接后,即可通过 mysql_query 函数向 MySQL 服务器发送 SQL 请求。该函数的定义如下:

int mysql_query(MYSQL *mysql, const char *q);

参数说明mysql:指向一个已初始化的 MYSQL 结构体指针,通常通过 mysql_init 函数创建,并已成功建立数据库连接。

q:以 C 风格字符串形式表示的 SQL 语句。注意,SQL 语句末尾可以不带分号。

返回值说明返回值为 0 表示 SQL 语句执行成功。

返回值非零表示执行失败,可通过相关错误处理函数进一步获取错误信息。

使用示例MySQL 数据库测试说明:本次测试使用的数据库名为 connect_demon,其中包含一个 user 表,表内有 3 条测试记录。具体如下:

向数据库插入数据:通过调用mysql_query函数,向MySQL服务器发送INSERT SQL语句。示例如下:

#include

#include

#include

using namespace std;

const string host = "113.45.79.2";

const string user = "hmz";

const string passwd = "HMZhmz123!!!";

const string db = "connect_demon";

const int port = 3306;

int main()

{

//1、创建MySQL对象

MYSQL* ms = mysql_init(nullptr);

//2、连接数据库

if(mysql_real_connect(ms, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)

{

cerr<<"数据库连接失败!"<

通过make命令编译生成可执行程序后运行,程序执行结果会直接反映在MySQL数据库中,可以看到数据已成功插入。具体操作如下:

从数据库中删除数据:通过调用mysql_query函数向MySQL服务器发送delete语句。示例如下:

#include

#include

#include

using namespace std;

const string host = "113.45.79.2";

const string user = "hmz";

const string passwd = "HMZhmz123!!!";

const string db = "connect_demon";

const int port = 3306;

int main()

{

// 1、创建MySQL对象

MYSQL *ms = mysql_init(nullptr);

// 2、连接数据库

if (mysql_real_connect(ms, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)

{

cerr << "数据库连接失败!" << endl;

return 1;

}

cout << "数据库连接成功!" << endl;

mysql_set_character_set(ms, "utf8"); // 设置编码格式为utf8

// 3、删除数据库表中的记录

std:

string sql = "delete from user where id=4";

if (mysql_query(ms, sql.c_str()) != 0)

{

cout << "删除数据失败!" << endl;

return 2;

}

cout << "删除数据成功!" << endl;

// 4、关闭数据库

mysql_close(ms);

cout << "数据库关闭成功!" << endl;

return 0;

}

通过make命令编译生成可执行程序,运行后在MySQL数据库中即可观察到目标数据被成功删除。操作示例如下:

修改数据库数据:通过调用mysql_query函数向MySQL服务器发送update SQL语句。示例如下:

#include

#include

#include

using namespace std;

const string host = "113.45.79.2";

const string user = "hmz";

const string passwd = "HMZhmz123!!!";

const string db = "connect_demon";

const int port = 3306;

int main()

{

//1、创建MySQL对象

MYSQL* ms = mysql_init(nullptr);

//2、连接数据库

if(mysql_real_connect(ms, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)

{

cerr<<"数据库连接失败!"<

执行make命令编译生成程序后,运行该程序即可在MySQL数据库中观察到相应的数据变更。具体操作如下:

九、获取查询结果思考:我们想想下面代码的输出结果是什么?下面执行的是select的查询操作:

#include

#include

#include

using namespace std;

const string host = "113.45.79.2";

const string user = "hmz";

const string passwd = "HMZhmz123!!!";

const string db = "connect_demon";

const int port = 3306;

int main()

{

//1、创建MySQL对象

MYSQL* ms = mysql_init(nullptr);

//2、连接数据库

if(mysql_real_connect(ms, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)

{

cerr<<"数据库连接失败!"<

这段代码没有显示查询结果的原因是:它只执行了查询,但没有获取和处理查询结果。

1、问题分析1. 缺少结果集处理代码在 mysql_query() 执行成功后就直接关闭连接了,没有:获取结果集、遍历结果行、提取字段数据、显示结果

2. 代码执行流程:

缺少了关键的 结果处理 步骤。

需要后面会学到的添加的结果处理代码在 cout<<"查找数据成功!"<

// 获取结果集

MYSQL_RES* result = mysql_store_result(ms);

if (result == nullptr) {

cout << "获取结果集失败!" << endl;

return 3;

}

// 获取字段数量

int num_fields = mysql_num_fields(result);

// 遍历每一行数据

MYSQL_ROW row;

while ((row = mysql_fetch_row(result))) {

// 遍历当前行的每个字段

for (int i = 0; i < num_fields; i++) {

cout << (row[i] ? row[i] : "NULL") << "\t";

}

cout << endl;

}

// 释放结果集

mysql_free_result(result);

加了处理结果集的当前输出结果:

查询确实执行成功了,但因为没有处理结果集,所以你看不到具体的数据内容。经过上面的问题引入,我们应该明白为什么要学习下面的内容了,主要是用来获取查询结果。对数据库执行增、删、改操作时,仅需调用 mysql_query 下发对应 SQL 请求即可;而执行查询操作时,除下发查询语句外,还需额外获取查询结果集。

2、获取查询结果集

MYSQL_RES *mysql_store_result(MYSQL *mysql);

说明:

该函数通过 MySQL 对象内部函数指针获取查询结果,并将完整结果集保存至 MYSQL_RES 结构体中返回。

注意:MYSQL_RES 对象使用动态分配的内存,使用完毕后必须手动释放,否则将导致内存泄漏。

MYSQL_RES 结构定义(摘要)

typedef struct st_mysql_res {

my_ulonglong row_count; // 结果集行数

MYSQL_FIELD *fields; // 列属性数组

MYSQL_ROW current_row; // 当前行数据

unsigned int field_count; // 列数

// ... 其他内部字段

} MYSQL_RES;

3、获取结果集行数

my_ulonglong mysql_num_rows(MYSQL_RES *res);

说明:返回指定 MYSQL_RES 对象中结果集的总行数。

4、获取结果集列数

unsigned int mysql_num_fields(MYSQL_RES *res);

说明:返回指定 MYSQL_RES 对象中结果集的列数。

5、获取列属性信息

MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);

说明:

返回一个 MYSQL_FIELD 结构体数组,包含结果集中每一列的详细属性信息(如列名、类型、长度等)。

数组长度为结果集的列数,可通过 mysql_num_fields 获取。

MYSQL_FIELD 结构定义(摘要):

typedef struct st_mysql_field {

char *name; // 列名

char *table; // 所属表名

unsigned long length; // 列定义长度

unsigned long max_length; // 结果集中最大长度

enum enum_field_types type; // 列数据类型

unsigned int flags; // 列标志(如是否为主键)

// ... 其他属性字段

} MYSQL_FIELD;

6、逐行获取结果数据

MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);

说明:

从结果集中提取下一行数据,返回 MYSQL_ROW 对象。

MYSQL_ROW 实质为 char ** 类型,即字符串数组,每个元素对应一行中的某一列值。

typedef char **MYSQL_ROW; /* return data as array of strings */ 返回值说明:

成功:返回当前行数据的指针

失败或无更多数据:返回 NULL

内部游标机制(重要!!!)MYSQL_RES* res 结果集对象内部维护着一个游标指针

初始时,这个游标指向结果集的第一行之前

每次调用 mysql_fetch_row(res) 时,游标会自动移动到下一行

7、完整查询示例比如查询user表中的数据并进行打印输出。如下:

#include

#include

#include

using namespace std;

const string host = "113.45.79.2";

const string user = "hmz";

const string passwd = "HMZhmz123!!!";

const string db = "connect_demon";

const int port = 3306;

int main()

{

//1、获取MySQL实例(相当于给我们创建了一个MySQL句柄)

MYSQL* ms = mysql_init(nullptr);

//2、连接数据库

if(mysql_real_connect(ms, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)

{

cerr<<"数据库连接失败!"<

在两层for循环那里,我们可以将其看作为一个二维数组,二维数组每个元素对应着一个字符串,如下直观图:(忘记了的话就回看C语言指针!!!)

执行make命令生成可执行程序,运行后即可查看数据查询结果。示例如下:

十、事务控制MySQL C API 提供了完整的事务操作支持,以下是常用的事务相关函数:

1、主要函数1. mysql_autocommit() :开启或关闭自动提交模式

功能:控制MySQL的自动提交模式

参数:

int mysql_autocommit(MYSQL *mysql, int mode)

mysql: MySQL连接对象

mode:

1 或 非0: 开启自动提交

0: 关闭自动提交

工作模式:

自动提交模式开启时 (mode=1)

mysql_autocommit(conn, 1); // 开启自动提交

每个SQL语句都会立即提交到数据库

相当于每条语句都是一个独立的事务

MySQL默认模式

自动提交模式关闭时 (mode=0)

mysql_autocommit(conn, 0); // 关闭自动提交

SQL语句不会立即提交

需要手动调用 mysql_commit() 提交或 mysql_rollback() 回滚

开启事务操作的必备步骤

使用场景:

// 开始事务操作

mysql_autocommit(conn, 0); // 必须先关闭自动提交

// 执行多个相关操作

mysql_query(conn, "UPDATE ...");

mysql_query(conn, "INSERT ...");

// 手动提交

mysql_commit(conn);

// 恢复自动提交(可选)

mysql_autocommit(conn, 1);

简单理解:就像游戏的"保存模式" - 自动提交是"自动保存",关闭后是"手动保存"。

2. mysql_commit() :提交当前事务

功能:提交当前事务,将所有未提交的更改永久保存到数据库

参数:

int mysql_commit(MYSQL *mysql)

mysql: MySQL连接对象

返回值:成功返回0,失败返回非0

使用前提:

必须先关闭自动提交才有效:

mysql_autocommit(conn, 0); // 必须先关闭自动提交

工作流程:

没有mysql_commit():

mysql_autocommit(conn, 0);

mysql_query(conn, "UPDATE user SET age=25 WHERE id=1");

// 更改不会保存到数据库!

有mysql_commit():

mysql_autocommit(conn, 0);

mysql_query(conn, "UPDATE user SET age=25 WHERE id=1");

mysql_commit(conn); // 现在更改才真正保存

完整事务流程

mysql_autocommit(conn, 0); // 1. 开始事务

mysql_query(conn, "UPDATE ..."); // 2. 执行操作

mysql_query(conn, "INSERT ...");

if(一切正常) {

mysql_commit(conn); // 3. 提交确认

} else {

mysql_rollback(conn); // 3. 回滚取消

}

简单理解:就像网购的确认订单:

mysql_autocommit(0) = 把商品加入购物车

各种操作 = 修改商品数量、地址等

mysql_commit() = 点击"确认支付",交易完成

mysql_rollback() = 取消订单

关键点:不调用 mysql_commit(),所有操作都不会真正生效!

3. mysql_rollback() :回滚当前事务

功能:回滚当前事务,取消所有未提交的更改

参数:

int mysql_rollback(MYSQL *mysql)

mysql: MySQL连接对象

返回值:成功返回0,失败返回非0

使用前提:

必须先关闭自动提交才有效:

mysql_autocommit(conn, 0); // 必须先关闭自动提交

工作流程:

正常提交:

mysql_autocommit(conn, 0);

mysql_query(conn, "UPDATE user SET age=25 WHERE id=1");

mysql_commit(conn); // 确认更改

异常回滚:

mysql_autocommit(conn, 0);

mysql_query(conn, "UPDATE user SET age=25 WHERE id=1");

mysql_rollback(conn); // 取消所有更改!

典型使用场景

mysql_autocommit(conn, 0); // 开始事务

// 执行多个操作

if (mysql_query(conn, "UPDATE accounts SET balance=balance-100 WHERE user='A'")) {

mysql_rollback(conn); // 失败就回滚

return;

}

if (mysql_query(conn, "UPDATE accounts SET balance=balance+100 WHERE user='B'")) {

mysql_rollback(conn); // 失败就回滚

return;

}

mysql_commit(conn); // 都成功才提交

简单理解:就像游戏的撤销操作:

mysql_autocommit(0) = 开始录制操作

各种SQL操作 = 在游戏中移动、建造、购买

mysql_commit() = 保存游戏

mysql_rollback() = 读取存档,回到开始状态

重要特性

原子性保证:要么全部成功,要么全部取消

数据一致性:确保相关操作要么都执行,要么都不执行

错误恢复:遇到任何错误时保护数据完整性

关键点:回滚后,所有未提交的操作就像从未发生过!

2、使用说明在MySQL中,默认启用自动提交模式,每个SQL语句都会立即生效。要使用事务控制,需要先关闭自动提交,然后手动管理事务的提交与回滚。

3、简单示例代码

// 关闭自动提交模式,开始事务控制

mysql_autocommit(conn, 0);

// 执行一系列SQL操作

// 例如:

// mysql_query(conn, "INSERT INTO table1 ...");

// mysql_query(conn, "UPDATE table2 ...");

// mysql_query(conn, "DELETE FROM table3 ...");

// 如果所有操作都成功,提交事务

mysql_commit(conn);

// 如果在操作过程中发生错误,可以回滚事务

// mysql_rollback(conn);

// 恢复自动提交模式(如果需要)

// mysql_autocommit(conn, 1);

4、注意事项关闭自动提交后,必须显式调用 mysql_commit() 或 mysql_rollback() 来结束事务

事务操作失败时,应及时回滚以避免数据不一致

建议在事务操作期间进行错误检查,确保每一步操作都成功执行

长时间的事务可能会占用数据库资源,应尽快完成并提交或回滚

5、完整代码示例

#include

#include

#include

using namespace std;

const string host = "113.45.79.2";

const string user = "hmz";

const string passwd = "HMZhmz123!!!";

const string db = "connect_demon";

const int port = 3306;

int main()

{

//1、创建MySQL对象

MYSQL* ms = mysql_init(nullptr);

//2、连接数据库

if(mysql_real_connect(ms, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0) == nullptr)

{

cerr<<"数据库连接失败!"<

回顾知识点:这些返回值表示程序退出的不同状态码,用于标识不同的错误类型!!!这些返回值是用户自定义的,不是系统定义的。return值主要是给其他程序用的,不是给操作系统用的!!!(重要!!!)

返回值含义错误位置return 0程序完全成功正常结束return 1数据库连接失败连接数据库时return 2关闭自动提交失败开始事务时return 3第一个更新失败更新张三年龄时return 4第二个更新失败更新李四年龄时return 5事务提交失败提交事务时return 6查询数据失败验证结果时return 7获取结果集失败处理查询结果时十一、总结 本文介绍了如何在 C 语言中使用 MySQL Connector/C 库,建立与 MySQL 数据库的连接,并执行简单的 SQL 查询。通过 mysql_init() 初始化库,mysql_real_connect() 连接数据库,mysql_query() 执行查询,mysql_fetch_row() 获取结果,最后使用 mysql_close() 关闭连接。你还可以使用事务控制来保证数据的一致性和完整性。

更多创意

克隆赛选5个蔡文姬忍了,选5个盾山也能忍,选她6分钟不投太心累
大清亡后爱新觉罗家族全部改姓,后人都以姓氏为荣,定期祭拜祖先
成宫宽贵宣布退出演艺圈 成宫宽贵为什么退出娱乐圈原因真相揭秘