目录
mysql connect
介绍
开发环境
编译链接问题
编译
链接
接口介绍
初始化和销毁
mysql_init()
句柄
mysql_close()
链接数据库
mysql_real_connect()
参数
返回值
show processlist
给mysql下达命令
mysql_query()
参数
返回值
查询结果的获取
引入
mysql_store_result()
参数
返回值
MYSQL_RES
读取结果集中的元数据
行数/列数
mysql_fetch_fields() -- 列信息
type
获取结果集数据
访问行数据
mysql_fetch_row()
MYSQL_ROW
访问列数据
mysql_fetch_field()
设置编码格式
mysql_set_character_set()
测试
连接
命令行输入
代码
运行结果
输出查询结果
代码
运行结果
mysql connect
介绍
无论是使用mysql命令行式客户端,还是图形化界面,还是使用c/c++语言连接数据库
- 本质上没有差别,都是客户端的一种实现形式
- 都是要和mysql服务器建立连接并登录
我们下面介绍用C api来连接数据库的方式
- 因为好理解(c++ api在C api的基础上进行了封装)
- 虽然是C api,但因为c++兼容c,所以我们依然可以使用c++语言来编写代码
开发环境
其实在下载mysql服务时,看似只下载了mysql-community-server,实际上把服务器,客户端,开发包什么的都下载好了
- 所以我们这里可以直接使用
开发包在哪呢?
- ls /usr/include/mysql 头文件
- /usr/lib64/mysql 或者/lib64/mysql 库文件
- (我这里不知道为啥两个路径下都有)
如果没有,就单独安装mysql-devel
编译链接问题
编译
编译时需要指明我们使用了mysql第三方库
因为我们要使用mysql.h中的函数,如果头文件中不写mysql/mysql.h,只写mysql.h,编译器会找不到头文件在哪,就需要添加-I选项
- 因为系统路径只包括/usr/include的部分,而mysql.h在其下子目录中,所以需要带上上级目录名
链接
虽然文件放在了编译器可以查找的路径下,但编译器无法自主寻找,并且也不知道应该链接哪个库
- 所以,要添加-L/lib64/mysql -lmysqlclient
- (哪个路径下有那些库文件,-L就带上哪个路径)
如果运行时报错,就将缺少的动态库路径添加进系统配置文件/环境变量中
- 比如这里的/etc/ld.so.conf.d/,它用于存放动态链接库的配置文件
- 因为我这里有,所以就不添加了:
接口介绍
在mysql官网中可以查看接口手册
初始化和销毁
mysql_init()
初始化一个MYSQL结构体,以便在后续操作中使用
- 参数一般写成NULL即可
- 返回值其实是一个句柄,和打开文件后返回的FILE类型的指针一样
- 如果返回NULL,表示初始化失败
句柄
表示对系统资源(如文件、窗口、数据库连接等)的引用
- 句柄本质上是一个标识符,通常是一个整数或指针
- 它允许程序在不直接操作底层资源的情况下,进行资源的管理和操作
mysql_close()
关闭与数据库的连接,并释放与该连接相关的资源
链接数据库
连接mysql服务器的前提是,要先有一个用户和一个数据库
mysql_real_connect()
MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *password, const char *dbname, unsigned int port, const char *unix_socket, unsigned long client_flag);
参数
返回值
- 如果成功,会将传入的那个指针返回 -- 类似于c接口中做字符串截取/拷贝时,会返回原始子串
- 失败返回NULL
show processlist
是 MySQL 中的一个 SQL 命令
- 用于显示当前数据库服务器中所有正在执行的线程信息
- 可以通过在c/c++程序中调用sleep(),让我们的程序保持和服务器的连接状态,然后在mysql中查看连接情况
给mysql下达命令
mysql_query()
用于执行 SQL 查询
参数
传入MYSQL结构的指针 和 要执行的 SQL 查询字符串
- 这里传入的参数中,sql语句不需要加分号或者\G
返回值
查询结果的获取
引入
因为mysql有事务的存在,即使有多个客户端同时操作表中数据,也不会出问题
- 所以,只要我们提供正确的sql语句,就能完成增删改的操作
但查询不一样
- 当我们传入select操作,函数返回值是0,代表操作成功执行
- 但是我们并没有拿到结果
如何获取结果呢?
- 当mysql服务器执行查询操作后,会将满足条件的数据存放在服务器端的内存中,并形成结果集
- 通过客户端调用特定接口,可以获取到结果集,并存储到特定结构
mysql_store_result()
用于获取查询结果的函数
参数
该函数会调用MYSQL变量中的st_mysql_methods中的 read_rows函数指针来获取查询的结果
返回值
- 同时,该函数malloc了一片内存空间来存储查询结果数据
- 所以我们一定要释放掉这块空间,不然是肯定会造成内存泄漏的 -- mysql内部提供了mysql_free_result()来帮助我们释放掉这块空间
MYSQL_RES
将结果集保存在MYSQL_RES结构中,是为了方便我们进行二次处理
如何进行二次处理?
- 插入的时候mysql分了很多类型,但将数据读出来的时候,全都当做字符串来处理
实际上,可以把MYSQL_RES看作是以下面这种方式放置数据的(二维数组):
- 按行遍历就是拿出char**,按列遍历就是拿出每行中的char*
- 这样对数据做分析,就变成了对这个结构做分析
读取结果集中的元数据
行数/列数
mysql_fetch_fields() -- 列信息
返回一个MYSQL_FIELD类型的指针
- 也就是一个MYSQL_FIELD类型的数组
每一列的信息以结构体的方式保存起来,一个数组里面就包含了该表所有列
- org -- 表示原生(因为可能会给列起别名)
type
这个枚举类型定义了mysql中的数据类型
因为mysql中把数据都当做字符串
- 当我们提取出来之后,就可以根据它们的原有类型进行类型转换,即可恢复类型
获取结果集数据
访问行数据
mysql_fetch_row()
用于从结果集中获取下一行数据,返回一个指向该行的指针
- 类似于迭代器的作用(调用一次就返回当前行,并自动指向下一行)
- 只是需要我们自行控制遍历次数(根据行数)
MYSQL_ROW
为了更好地支持遍历,mysql提供了MYSQL_ROW这个结构
- 表示查询结果集中的一行数据
- 而MYSQL_ROW=char**,其实就是像上面图中画的一样往下遍历
访问列数据
当我们成功拿到一行后,就可以像对待字符串数组一样,用数组下标拿到每一列
- 列数就是元素个数
while ((row = mysql_fetch_row(res)) != NULL) { // 访问第一列 printf("First column: %s\n", row[0]); }
mysql_fetch_field()
获取结果集中当前列的元数据
- 和迭代器类似,每次可以获取一列信息
while ((field = mysql_fetch_field(res)) != NULL) { printf("Column name: %s, Type: %d\n", field->name, field->type); }
设置编码格式
当我们插入中文字符时,mysql内部存入的是乱码
- 出现乱码的原因一定是双方对编码格式没有达成一致
- 而mysql我们已经配置过,使用的就是utf8的格式,那就只能是我们代码这边编码格式有问题
在链接mysql时,需要设置字符集 -- mysql_set_character_set()
- 字符集和编码格式紧密相关,设置字符集通常意味着也设置了相应的编码格式
- 原始默认字符集是latin1
mysql_set_character_set()
测试
连接
我们先在mysql创建一张表
-
然后测试我们是否能通过cpp程序控制mysql
#include <iostream> #include <mysql/mysql.h> int main() { MYSQL *mysql = mysql_init(nullptr); mysql = mysql_real_connect(mysql, "ip地址", "mufeng", "599348181", "conn", 3306, nullptr, 0); if (nullptr == mysql) { std::cout << "connect failed\n"; exit(1); } std::cout<<"connect success\n"; mysql_close(mysql); return 0; }
可以看到我们连接成功:
- 注意这里,我应该是在本机上连接的(vscode和xshell上都是远程连接同一个云服务器),但用户如果设置localhost,依然没法连接成功,不知道为啥
- 总之如果不行的话,用户还是设置成允许所有主机登录吧
命令行输入
我们可以设置以命令行输入的形式,将输入内容作为sql语句让mysql去执行,并且模拟mysql的行为
- 当然,我们实际进行开发的时候,直接调用接口就行,不用整什么命令行
- 因为本身mysql就有客户端,没必要我们也弄一个
以及要注意设置编码格式
代码
#include <iostream> #include <string> #include <mysql/mysql.h> int main() { MYSQL *mysql = mysql_init(nullptr); mysql = mysql_real_connect(mysql, "ip地址", "mufeng", "599348181", "conn", 3306, nullptr, 0); if (nullptr == mysql) { std::cout << "connect failed\n"; exit(1); } std::cout << "connect success\n"; mysql_set_character_set(mysql, "utf8"); std::string sql; std::cout << "mysql>"; std::cout.flush(); while (std::getline(std::cin, sql)) { if (sql == "quit") { std::cout << "bye\n"; break; } int ret = mysql_query(mysql, sql.c_str()); if (ret == 0) { std::cout << sql << " success\n"; } else { std::cout << mysql_error(mysql) << std::endl; } std::cout << "mysql>"; std::cout.flush(); } mysql_close(mysql); return 0; }
运行结果
可以看见,我们通过自己编写的客户端向表中插入数据,在mysql下是可以看到更改的
插入中文也可以:
输出查询结果
如果我们不进行特殊处理,是无法看见查询结果的:
-
代码
#include <iostream> #include <string> #include <mysql/mysql.h> void client(MYSQL *mysql) { std::string sql; std::cout << "mysql>"; std::cout.flush(); while (std::getline(std::cin, sql)) { if (sql == "quit") { std::cout << "bye\n"; break; } int ret = mysql_query(mysql, sql.c_str()); if (ret == 0) { std::cout << sql << " success\n"; } else { std::cout << mysql_error(mysql) << std::endl; } std::cout << "mysql>"; std::cout.flush(); } } void select_test(MYSQL *mysql) { std::string sql; sql = "select * from test"; int ret = mysql_query(mysql, sql.c_str()); if (ret == 0) { MYSQL_RES *res = mysql_store_result(mysql); if (res == nullptr) { std::cout << "mysql_store_result failed\n"; } else { int row_num = mysql_num_rows(res); int field_num = mysql_num_fields(res); // 打印列名 MYSQL_FIELD *field; while ((field = mysql_fetch_field(res)) != NULL) { std::cout << field->name << " "; } std::cout << std::endl; // 打印表数据 for (int i = 0; i < row_num; ++i) { MYSQL_ROW row = mysql_fetch_row(res); for (int j = 0; j < field_num; j++) { std::cout << row[j] << " "; } std::cout << std::endl; } } } else { std::cout << mysql_error(mysql) << std::endl; } } int main() { MYSQL *mysql = mysql_init(nullptr); mysql = mysql_real_connect(mysql, "ip地址", "mufeng", "599348181", "conn", 3306, nullptr, 0); if (nullptr == mysql) { std::cout << "connect failed\n"; exit(1); } std::cout << "connect success\n"; mysql_set_character_set(mysql, "utf8"); // client(mysql); select_test(mysql); mysql_close(mysql); return 0; }
运行结果
可以看到,我们成功模拟出mysql中打印查询结果的样式,只是少了表格结构: