一、MySQL访问的本质
在前面几篇博客中已完成了数据库的基本使用和原理相关学习和梳理,本篇介绍如何使用语言连接MySQL。
已知MySQL有客户端和服务端,程序员要做的就是编写业务逻辑,将需求发给客户端,客户端进而访问服务端,并将结果返回给主程序(所以一开始在shell中调用systemctl start mysql就是启动服务端进程,而mysql登录就是打开客户端并连接服务器)
二、访问相关方法介绍
为了操作数据库资源,一开始需创建对应的MYSQL结构体对象 并初始化。像这种用于在程序中唯一标识、引用和操作特定的对象或系统资的数据类型通常被称为句柄。
cpp
MYSQL *my = mysql_init(NULL);
初始化完毕后,需连接到数据库(如果是远端登录,要求数据库提前创建好允许远端登录的用户)用户需要具备操作的权限。连接需要的参数大多和在shell上进行MySQL登录用到的差不多。
需注意的是,链接也有自己的字符集,可以理解成当前程序的字符集,比如你要插入数据的话是以当前程序的字符集为标准的。而已为了不和数据库字符集冲突,需要重新设置链接字符集。
cpp
mysql_set_character_set(my, "utf8");
代码示例如下:
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);*/
const std::string host = "127.0.0.1";//本地登录
const std::string user = "Cuser";
const std::string passward = "1234567";
const std::string db = "Ctest";//数据库名
const unsigned int port = 3306;//这里端口号和mysql配置文件中的端口号相同,可查询或修改
int main()
{
// printf("mysql client Version: %s\n", mysql_get_client_info());
MYSQL *my = mysql_init(NULL); // 初始化MYSQL类
if (my == nullptr)
{
std::cerr << "mysql init fail" << std::endl;
return 1;
}
// 连接数据库
if (mysql_real_connect(my, host.c_str(), user.c_str(), passward.c_str(), db.c_str(), port, nullptr, 0) == nullptr)
{
std::cerr << "mysql connect fail" << std::endl;
return 2;
}
// 无法连接
std::cout << "mysql connect success" << std::endl;
// 更改编码集
mysql_set_character_set(my, "utf8");
}
随后就可以调用接口操作数据库了:
cpp
std::string insertsql="insert into user values('张三',555,'金榜花园')";
int flag = mysql_query(my, insertsql.c_str());
if (flag < 0)
{
std::cerr << "do sql fail" << std::endl;
return 3;
}
插入、删除和修改都是单向的,客户端只需要返回是否执行成功即可,比较复杂的是查询语句,即便客户端能把结果给到主程序,也需要配套的读取数据的机制。
当读取命令执行成功后,创建MYSQL_RES结构体指针并调用mysql_store_resul函数来接收指定MYSQL对象的结果。既然创建的是指针,说明函数自动为其申请了空间,使用完毕后需释放。
cpp
MYSQL_RES *res = mysql_store_result(my);
/*后续操作...*/
mysql_free_result(res);//释放结果集
数据在MYSQL_RES结构体中的存放形式如图:

在一维数组中存放数据行数个char**,每个二级指针又指向一个char*数组,每个char*指向的就是某行某列的数据。随后可以调用mysql_num_rows()和mysql_num_fields()获取返回数据的行列数,再引入MYSQL_ROW(char**类型别名)并调用 mysql_fetch_row()来获取单行指针(该结构相当于指向结果某行的char**),根据行数决定要调用多少次mysql_fetch_row(),根据列数遍历具体全部数据。
注1:mysql_fetch_row()每次调用后会自动指向下一行,不需要主程序控制。
注2:如果想获取列名则调用mysql_fetch_fields(),一次返回所有字段定义的数组,配合mysql_num_fields()使用
代码如下:
cpp
MYSQL_RES *res = mysql_store_result(my);
int row = mysql_num_rows(res);
int fi = mysql_num_fields(res);
MYSQL_FIELD *field = mysql_fetch_fields(res);
int i = 0;
for (; i < fi; i++)
{
std::cout << field[i].name << " ";
}
std::cout << std::endl;
i = 0;
MYSQL_ROW line;
for (; i < row; i++)
{
line = mysql_fetch_row(res);
int j = 0;
for (; j < fi; j++)
{
std::cout << line[j] << " ";
}
std::cout << std::endl;
}
三、浅谈从网页到数据库的流程
注册
在正式提供服务之前,需要提前为待存放的数据建立多个数据库。
用户打开浏览器网页,发起连接请求(即三次握手那一套流程),服务器随即应答并返回登录页面。
用户点击注册,跳转到注册页面后输入用户名和密码
服务器通过脚本语言(如 Java、Python 等)对请求进行解析,随后经由中间件和负载均衡机制,将数据分发到对应的数据库及数据表中。
各数据库之间会定期进行数据同步,以保证数据的一致性。
登录浏览器会先检查该账号是否存在 Cookie 缓存,若存在则可能直接完成登录(需要注意的是,Cookie 并非账号密码的副本,而是用于验证当前会话是否有效的标志)
若不存在,服务器会先调用 select 语句验证该账号在数据库中是否存在
若存在则正常登录,即校验密码哈希运算的结果是否与数据库中的记录相匹配。