初识mysql数据库之引入mysql客户端库
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
目录
一、下载第三方库
在以前的文章中我们使用的都是命令行式的mysql客户端。但是还有其他形式的客户端的比如图形化界面、网页版等等。当然 也包括语言级别的库或包能帮我们去直接访问数据库。
因此在这里就为大家介绍一下如何使用语言级别的库来访问mysql。在这之前可以先在mysql中创建一个用户方便后续的测试。
1. 准备工作
创建如下一个用户
然后在root用户下再创建如下一个数据库并将这个数据库的所有权限交给该用户
在后续的所有关于mysql的测试都是使用的这个数据库。
然后再在这个数据库中建立一个如下的user表
1. 使用mysql官网提供的库
要使用语言级别的库这里提供两种方法。第一种方法就是直接到mysql官网上下载对应的库。
首先搜索“mysql.com”打开mysql的官网
大家打开后的界面可能会这里的不太一样因为mysql的官网样式可能会变化。
进入官网后选择“DOWNLOADS”在里面可以看到如下内容
点击进去后就可以看到如下界面
里面可以选择下载你需要的各种类型的库。其中就包括封装好的语言级别的库。因为使用的语言是C++大家可能就会想下载C++语言级别的库。但在这里比较推荐使用 C API库因为这个库最简单。
点击后就会出现如上界面。在图中圈出来的部分显示mysql官网推荐下载的版本。直接点击下载即可。
点击后就可以看到如下内容
在这里选择你要将这个库安装在哪个平台下然后选择对应的版本即可。因为我们使用的是linux64位系统所以选择对应版本即可
选择好后下载即可。
当你下载好后它就是一个压缩包将它通过“rz”命令放到你的linux机器下然后解压安装即可。但是在这里 不太推荐这种方法。因为大家在安装的过程中可能会出现一些问题。
2. yum源安装
如果大家看过我之前的文章“mysql数据库安装”的话里面就介绍了如何安装mysql的yum源这里不再赘述。如果大家是使用的yum源安装那么在大家安装好mysql时其实就已经把mysql的安装包下载好了。
大家可以输入“ls /lib64/mysql/”查看linux中是否有如下内容
然后在输入“ls /usr/include/mysql”命令查看是否存在该头文件。
如果这两个内容都存在那么就说明此时你的linux下已经安装好了需要的东西。但如果没有就输入“yum install -y mysql-devel”命令安装一下即可。
二、测试第三方库是否可用
当做好上面的准备工作后就可以打开vscode在上文中创建的test_db目录下创建一个test.cpp文件。然后引入linux中的mysql库。
注意这个mysql目录会在安装好mysql后自动被放到/usr/include/路径下。这个路径是已经配置好了的默认路径不用我们写。但是mysql.h在mysql目录下而mysql并没有被配置到默认路径所以需要自己写剩下的路径。
头文件包好后此时就可以使用“mysql_get_client_info()”函数了该函数就是mysql.h中携带的它的作用是获取当前使用的mysql的客户端版本。在这里就用这个函数来测试一下是否这个头文件可用。
写出如下测试信息
保存好后就可以进行编译生成对应的文件了
但是当编译后可以发现此时会出现报错。报错的原因很简单其实是因为在这里使用的库是一个第三方库编译器并不知道这个库的位置也就无法使用库中的内容。因此在编译时需要带上使用的第三方库的路径。
那这个路径在哪里呢其实就在"/lib64/mysql/"下
在这里要使用的就是上图中圈出来的静态库。因此在编译时带上库的路径和库名称
如果大家不知道为什么这里要带-L和-l以及为什么编译时的静态库名称少了lib和.a可以到我以前的文章“动态库链接”中查看。这里不再赘述。
可以发现当链接了库后就可以编译成功了。执行该程序
执行成功这就说明此时已经能够使用第三方库的内容了。
如果大家在测试时发现还是无法运行就可以输入“ldd 文件名”查看该可执行文件链接的库
如果大家发现上图中圈出来的库的动态库链接对象为空就说明这个库没有能够链接到指定的库上。此时大家可以把这个库后面指向的库添加到系统的配置文件或环境变量中。至于如何添加这里不再多说大家可以自行网上搜索。
三、mysql常用接口介绍
1. 查看官方文档
在介绍mysql的常用接口之前大家可以先到mysql的官网“mysql.com”中选择下图中的内容
点击后往下翻里面全是mysql库的文档。找到C API库点击5.7
在进入的页面的左边点击下图内容
里面存放的就是关于mysql的接口的介绍。
这里只截取了其中一部分。
大家可以将这个网页保存一下当你需要查看某些函数的作用和怎么使用时就可以查看这个文档。
2. 初始化
首先大家要知道mysqld是一个网络服务这就意味着在实际进行mysql操作之前一定需要连接上mysql。而在连接mysql之前还需要对mysql进行初始化。
要初始化化就需要使用“mysql_init(MYSQL *mysql)”接口
该接口会返回一个MYSQL结构体里面包含了创建mysql需要的一些数据。如果初始化失败它就会返回一个空指针。在使用时该接口的参数直接填nullptr即可。
3. 关闭mysql
当不需要使用mysql后就需要手动关闭。此时就需要调用“mysql_close()”接口
它的参数就是mysql_init()的返回值。
4. 连接mysql
上文说了 mysqld是一个网络服务所以要使用mysql我们必须要先连接。当然在连接之前还需要初始化初始化的函数上文中已经介绍了。
要连接mysql就需要使用"mysql_real_connect()"接口
介绍一下里面的参数。
第一个参数mysql就是mysql_init()接口的返回值
第二个参数host就是要连接的主机
第三个参数user就是要用哪个用户登录mysql
第三个参数password就是这个用户的密码
第五个参数db就是要使用的数据库
第六个参数port就是要连接的mysqld的端口号第五个参数unix_socket是域间套接字大家可以看成就是使用管道但这里我们是要使用网络所以不用管这个参数填为nullptr即可
最后一个参数client_flag是用户的一些选项这里填为0即可。
5. 下达sql指令
要向mysqld下达sql指令需要使用“mysql_query()”接口
该接口的第一个参数就是mysql_init()的返回值第二个参数就是我们要下达的sql指令。以字符串的方式传递给该接口。在这个字符串中的sql指令可以带也可以不带。
当这个接口执行成功后它会返回0失败则会返回非0的错误码。
四、一个简单的C++客户端库连接mysql程序
有了上面的几个接口其实就已经可以初步的使用C++客户端库了。为了方便大家看到实际的使用过程这里利用C++客户端库写一个简单的可以控制mysql内的数据库的程序。
1. 头文件
首先准备如下头文件
里面的内容大家应该都很清楚不再多说。
2. 初始化与退出
调用mysql_init()接口初始化获得一个MYSQL结构体指针
然后再将退出mysql写好
3. 连接mysql
调用mysql_real_connect()接口连接mysql
有了上面的内容其实我们就已经可以开始对特定的数据库做操作了。在这之前先编译生成可执行文件看看该程序是否可以正常连接
可以正常运行此时就可以着手操作数据库了。
4. 下达sql指令
为了方便测试在这里就采取从简单读取sql指令的方式
5. 测试
准备好如上代码后我们就可以开始测试了测试用的数据库和表在上文中已经说过了这里就不再多说。
首先为了能看到数据库中表的数据的变化我们先用该程序中的用户登录mysql
登录成功后进入conn库然后查看user表的数据
可以看到此时user表内没有任何数据。然后启动写好的程序
5.1 退出
首先来测试一下能否退出
退出没有问题进行下一个测试。
5.2 插入数据
重新启动程序然后输入insert语句
此时就提示该指令执行成功。那到底是不是真的成功了呢查看一下user表的数据
可以看到user表中确实多出了刚刚在mytest程序中要插入的数据。注意在上面的mysql语句中是带了“;”的其实在mysql_query()接口中也是可以不带“;”的。再插入一个数据
这条sql语句中就没有带“;”。查看user表的数据
依然插入成功了。
要知道在linux中我们使用mysql的时候不就是使用的mysql的客户端进行操作么因此如果大家愿意其实也可以使用C++客户端库自己写一个客户端来与mysqld交互。
5.3 修改程序
通过上面的测试其实就可以发现我们是可以用C++客户端库自己写一份客户端的。但没有这个必要毕竟有现成的何必自己去写呢
同时大家可以发现在linux下以命令行的方式去提交sql指令有点麻烦。因此修改一下程序直接从程序中提供sql语句
此时就可以直接在sql字符串内写好要执行的sql语句然后重新编译执行即可。测试起来就比在命令行中写方便。
5.4 更新数据
修改好程序后在sql字符串中写好update语句
重新编译并执行。然后查看user表内的数据
修改成功。
5.5 删除数据
再来测试一下delete语句
重新编译并执行。查看user表内的数据
删除成功。
5.6 查询数据
再来测试一下select语句
重新编译并执行
可以看到执行成功了。然后呢数据库的数据呢在这个场景下我们自己写的程序就是一个上层应用该程序将指定的sql语句发送给数据库后这些sql语句就被会看成事务。因此在以前的文章中讲的事务执行失败、事务需要回滚等等操作都由数据库自行处理无需上层应用去考虑。
同时在insert、delete、update这些sql语句中都是对数据库做操作。因此使用这些数据后上层应用只需要知道这些sql语句是否执行成功无需看到执行成功后的数据。但是select语句不一样在上层应用中调用该语句不就是想查看特定数据然后用这些数据去执行一些特定的操作么。但是在这里虽然执行select语句成功了但是上层应用中依然仅仅知道执行成功而无法看到查询结果。
因此 在select语句之后还需要调用其他接口来让我们看到查询出来的结果。
5.7 当前插入存在的问题
如果大家仔细观察了user表的数据就会发现在这个表中插入的用户名字都是英文的。那如果插入中文呢测试一下
重新编译并执行
程序执行成功没有问题。我们再来看一下user表内的数据
可以发现虽然插入成功了但是user表中本应存储“张三”这个名字的位置却是乱码。这其实就是编码格式的问题。
在mysql数据库中已经配置过了在该数据库下默认使用utf8的编码集。但是这仅仅是服务端下的编码格式。当前使用的客户端即该程序下它使用的编码集并不是utf8。而是默认的latin1。因此在此时我们的客户端在发送数据时是将数据按Latin1的编码格式进行编码的但是当mysqld服务端接收到数据后确实以utf8的格式进行解码的。编码与解码使用的编码格式不同也就导致了服务端中出现乱码。
由此在客户端中我们还需要设置编码格式。此时就需要使用“msql_set_character_set()”接口
第一个参数mysql就是mysql_init()的返回值第二个参数csname就是要使用的编码集的名字。这个接口需要在连接成功即调用mysql_real_connect()接口后使用。
重新执行一次insert语句
重新编译并执行。查看user表的数据
没有出现乱码。
此时大家可能有个疑问在上面没有设置编码格式时为什么在编码格式不同的情况下中文会乱码英文和数字却不会乱码呢其实很简单英文字母一共也就26个个位数字也是只有10个。因为它们的数量很少所以不同的编码集对这些内容的适配都很好。但中文不同中文字符有上万个常用的中文字符也有数千个大量的字符就导致了不同的编码集可能采用不同的编码方式进行处理也就可能出现乱码了。
6. 获取select后的数据
6.1 提取数据
在实际上当用mysql_query()接口执行select语句后它会将数据库中查询到的结果保存到该接口传入的参数MSYQL结构体中。在这个结构体中是有专门的缓冲区来保存这些数据的。
虽然这些数据是保存在MYSQL结构体提供的缓冲区内但依然需要将这些数据从它的缓冲区中提取出来。
要从MYSQL中提取出数据就需要使用“mysql_store_result()”接口
这个接口的参数就是mysql_init()的返回值。成功时返回对应的结构体指针失败则返回null。
我们可以在官网文档中查看关于该结构的说明
根据文档说明可以知道这个结构会将查询结果按行为单位放置在“结果集”即该结构当中。
由此就可以使用对应接口提取了
当提取出来对应的数据后如何显示呢在了解如何显示之前还需要了解一下MYSQL_RES是如何存储数据的。
6.2 理解MYSQL_RES结构
假设现在有如下一张表
对于这张表可以将其组成部分看做两个分别是表结构和表数据。表结构就是列属性表数据就是每列中的数据。而组成表的这些符号在数据库中实际并未存储而是在显示时打印出来供用户区分表内的各个部分的。
首先大家要知道当数据库中查询出来的数据被转储到MYSQL_RES结构中时它们必然已经是被放到了内存里这也就说明此时这些函数是有对应的地址的。
当上层应用调用select语句查询表时它就是将表结构和表数据添加到MYSQL结构体内准备好的缓冲区中
当调用mysql_store_result()接口时就是将MYSQL结构体里面的缓冲区中保存的数据按行转储到MYSQL_RES结构当中
那MYSQL_RES内如何保存这些数据呢为了方便大家理解这里用只以表数据来举例。MYSQL_RES可以将其看成两层数组。第一层数组里面保存的是char**的二级指针每个二级指针指向一个数组这个数组里面保存的是char*的一级指针。每个指针都指向对应行数据
当然实际的MYSQL_RES中还存有很多余这张表相关的其他数据。但是大家可以将其整体结构就理解为上图所示。
由此如果我们想从MYSQL_RES内拿到数据其实就可以按照数组的方式从这个结构体的各个成员变量中以下标的形式拿取。
但是我们怎么知道表数据一共有多少行和多少列呢此时就可以调用C++客户端库内的其他相关函数了。
6.3 提取表的行和列
要提取表的行可以使用“mysql_num_rows()”接口
它的参数就是“mysql_store_result()”的返回值。
要提取表的列可以使用“mysql_num_fields()”接口
它的参数也是“mysql_store_result()”的返回值。
通过这两个接口就可以分别获得查询出来的表的行和列了。那么这两个接口到底能不能正确获取呢我们来测试一下
传入如下select语句
重新编译并运行
打印的结果和我们的预期一样没有问题。
6.4 获取表数据
要获取表数据还需要了解一个接口即“mysql_fetch_row()”接口
这个接口的参数就是mysql_store_result()接口的返回值。当它调用成功时它会返回一个MYSQL_ROW变量。
对于这个接口大家可以将其看做一个迭代器当第一次调用这个接口时它会指向MYSQL_RES结构中的第一行的数据的起点。让我们可以通过指针的形式访问数据。当再次调用的时候它会自动跳到第二行数据的起点。
转到这个类型的定义上去看看
可以看到在源码中它就是一个char**。
通过这个接口我们就能以下标的形式获取表数据。
注意mysql中的所有数据在上层应用读取出来时全部都是被看做字符串。
有了上面的认识就可以在程序中添加如下代码了
然后选择查询表内的所有数据
重新编译并执行
成功拿取到了表数据。由此我们就可以拿到我们对应的表数据了。未来大家想拿着这份数据去做什么就由大家自己决定。
6.5 获取列名
现在我们已经可以获取表数据了。那如果还想获取列名呢此时就需要使用“mysql_fetch_fields()”接口了
这个接口可以一次性获取所有列的列名。它的参数就是mysql_store_result()的返回值。当调用成功时它返回一个MYSQL_FIELD结构体里面就保存了列的各项属性
例如列名、列的原生名给列取别名的情况、属于哪个表、属于哪个原生表、属于哪个数据库、列的类型等等属性。
此时就可以解答大家的一个问题了。上文中说了查询到的表数据在上层应用中是以字符串的形式保存的。但是我们在使用的时候不一定是用字符串形式使用啊。例如age就是int类型的。那我们在上层使用这些数据时如何将它们从字符串转化为原来的类型呢其实就是通过这个接口拿到列属性然后通过MSYQL_FIELD结构体中的type变量里面保存的类型来将其重新转化回去。
由此就可以在程序中添加如下代码来获取列了
重新编译并执行
此时就成功的将列名获取了。当然大家也可以尝试下获取其他列属性这里就不再测试了。
7. 释放空间
在上文中我们知道了当我们查询数据时查询结果其实已经被保存在内存中了。而这些数据其实就是被保存在用new这类申请内存空间的接口申请的内存中。而我们知道在C++中用new这类接口申请的空间是需要用户自行释放的。这里也是如此。
但是和以前不同在这里我们最好不要用free()、delete这类释放空间的接口而是使用C++客户端库为我们提供的接口即“mysql_free_result()”接口
这个接口就会释放掉调用mysql_store_result()接口后开辟的内存空间。
8. 事务支持
在C++客户端库中也是支持事务的。可以选择一次性将一批sql语句传给数据库。可以采用如下接口
这里就不再演示了大家可以自行尝试使用一下。
9. 程序代码
在上文中所有的接口整合起来就可以写出如下的程序
#include <iostream>
#include <string>
#include <mysql/mysql.h>
// const std::string host = "127.0.0.1";
const std::string host = "localhost";//当使用本地登录时本地换回和localhost都是可行的
const std::string user = "connector";
const std::string password = "123456";
const std::string db = "conn";
const unsigned int port = 3306;
int main()
{
//1. 初始化
MYSQL *ms = mysql_init(nullptr);
if(ms == nullptr)
{
std::cerr << "init MYSQL error" << std::endl;
return 1;
}
//2. 连接mysql
if(mysql_real_connect(ms, host.c_str(), user.c_str(), password.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
std::cerr << "connect MYSQL error" << std::endl;
return 2;
}
mysql_set_character_set(ms, "utf8");//设置编码格式
//3. 执行各类sql语句
// std::string sql = "update user set name='jimmy' where id=2";
// std::string sql = "delete from user where id=2";
std::string sql = "select * from user";
// std::string sql = "insert into user (name, age, telphone) values ('张三', 10, '3456')";
// std::string sql = "insert into user (name, age, telphone) values ('李四', 13, '4567')";
// std::string sql = "select * from user where id=1";
int n = mysql_query(ms, sql.c_str());
if(n == 0)
std::cout << "success" << std::endl;
else
{
std::cerr << "failed: " << n << std::endl;
return 3;
}
// std::string sql;//接收sql指令
// while(true)
// {
// std::cout << "MYSQL>> ";
// if(!std::getline(std::cin, sql) || sql == "quit")//从键盘读取sql指令
// {
// std::cout << "bye bye" << std::endl;
// break;
// }
// int n = mysql_query(ms, sql.c_str());
// if(n == 0)
// std::cout << sql << " success " << n << std::endl;
// else
// std::cerr << sql << " error " << n << std::endl;
// }
// std::cout << "connect MYSQL success" << std::endl;
//4. 查询结果转储
MYSQL_RES *res = mysql_store_result(ms);
if(res == nullptr)
{
std::cerr << "mysql_store_result failed" << std::endl;
return 4;;
}
//5. 获取表的行和列
my_ulonglong rows = mysql_num_rows(res);
my_ulonglong fields = mysql_num_fields(res);
// std::cout << "行: " << rows << " 列: " << fields << std::endl;
//6. 获取列属性
MYSQL_FIELD *field_array = mysql_fetch_fields(res);
for(int i = 0; i < fields; ++i)
std::cout << field_array[i].name << "\t";
std::cout << std::endl;
//7. 获取表数据
for(int i = 0;i < rows; ++i)//先遍历行
{
MYSQL_ROW row = mysql_fetch_row(res);
for(int j = 0; j < fields; ++j)
std::cout << row[j] << "\t";
std::cout << std::endl;
}
//8. 释放空间并关闭mysql
mysql_free_result(res);
mysql_close(ms);
return 0;
}
里面的各个部分所使用的接口和出现的结果在上文中已经演示了这里不再赘述。