【MySQL】C语言连接数据库

一、安装MySQL库

我们之前学习数据库都是在 Linux 的 mysql 客户端下以纯命令行的方式操作的,但其实,我们也可以使用 C/C++/Java/Python 等语言来连接数据库,向 mysqld 下达 sql 语句并获取执行结果。不过,在这之前,我们需要先安装 MySQL 对应的库,这里我们以 C 语言连接数据库为例。

关于 MySQL 的 C语言库,我们可以直接到 MySQL 官网中去下载,然后使用 rz指令 上传到 Linux 中解压。当然我们也可以通过 yum 来直接安装 mysql client 以及 mysql devel。
我们可以使用如下指令安装mysql client 和mysql devel

sudo yum install -y mysql-community-server
sudo yum install -y mysql-community-devel

我们可以使用如下指令进行查看:

ll /etc/yum.repos.d/

在这里插入图片描述

安装完毕后我们就可以在 /usr/include/mysql 目录下找到 mysql 相关的头文件了:

在这里插入图片描述

同时,我们也可以在 /lib64/mysql/ 以及 /usr/lib64/mysql 目录下找到 mysql 对应的动态库以及静态库了:

在这里插入图片描述

验证引入是否成功

现在,我们就可以使用 mysql 目录下头文件中提供的相关函数来连接数据库了。

不过,在正式连接数据库之前,我们可以先通过 mysql_get_client_info() 函数来验证一下 mysql 相关头文件以及动态库是否被成功引入:

函数原型:const char *mysql_get_client_info(void);
返回值: A character string that represents the MySQL client library version.
#include <iostream>
#include <mysql/mysql.h>

int main()
{
    std::cout << "mysql version :" << mysql_get_client_info() << std::endl;
    return 0;
}

在这里插入图片描述

这里有几个需要注意的地方:

  1. 由于我们要使用 mysql C库中的函数,所以在 mysql.cc 中我们需要包含 mysql 相关头文件。
  2. 在程序编译时,我们需要使用 -l 选项来指定我们要链接的 mysql 动态库,并且动态库的库名称是去掉前缀 lib 以及后缀 .so 后的剩余部分。(libmysqlclient.so -> mysqlclient)
  3. 由于动态库在 /usr/lib64/mysql/ 目录下,而系统的默认库路径是 /usr/lib64/,所以我们还需要使用 -L 选项来指定动态库的路径。
  4. mysql C语言相关头文件在 /usr/include/mysql/ 目录下,而系统默认的头文件搜索路径是 /usr/include/,所以按道理来说,我们也是需要使用 -I 选项指明头文件路径的;但这里由于我们在编写源程序,包含头文件时使用的是 mysql/mysql.h,而不仅仅是 mysql.h,所以不需要指定。

二、MySQL C API 相关接口

1.C API官方文档

关于C语言连接数据所涉及到的各种数据结构的介绍以及相关函数的使用其实在 MySQL C API 官方文档中已经给出了,我们可以通过它来快速了解并上手 MySQL C API。
在这里插入图片描述

2.初始化 MYSQL句柄

要使用 MySQL C语言库,需要先使用 mysql_init 函数完成对 MYSQL 结构体指针的初始化工作。

// Mysql操作句柄初始化
// 参数说明:
// mysql为空则动态申请句柄空间进⾏初始化
// 返回值: 成功返回句柄指针, 失败返回NULL
MYSQL *mysql_init(MYSQL *mysql)

注意:mysql_init 函数的参数以及返回值都是 MYSQL 指针类型,对于 MYSQL,大家把它类比到C语言中的文件指针来理解即可。MYSQL 和C语言文件 FILE 一样,本质上都是一个结构体。

在这里插入图片描述

在这里插入图片描述

3.连接MySQL

// 连接mysql服务器
// 参数说明:
// mysql--初始化完成的句柄
// host---连接的mysql服务器的地址
// user---连接的服务器的⽤户名
// passwd-连接的服务器的密码
// db ----默认选择的数据库名称
// port---连接的服务器的端⼝: 默认是3306端⼝
// unix_socket---通信管道⽂件或者socket⽂件,通常置NULL
// client_flag---客户端标志位,通常置0
// 返回值:成功返回句柄指针,失败返回NULL
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
client_flag);

第一个参数 MYSQL是 C API中一个非常重要的变量(mysql_init的返回值),里面内存非常丰富,有port,dbname,charset等连接基本参数。它也包含了一个叫 st_mysql_methods的结构体变量,该变量里面保存着很多函数指针,这些函数指针将会在数据库连接成功以后的各种数据操作中被调用。

4.设置客户端字符集

需要注意的是,我们之前在创建数据库时默认使用的字符集是 utf8,而C语言连接数据时默认的字符集是 latin的,这就会导致我们在向表中插入中文数据时,由于字符集不匹配,最终数据库中存储的数据显式出来是乱码。所以,我们需要使用 mysql_set_character_set 函数设置连接字符集为 utf8。

// 设置当前客⼾端的字符集
// 参数说明:
// mysql--初始化完成的句柄
// csname--字符集名称,通常:"utf8"
// 返回值:成功返回0, 失败返回⾮0
int mysql_set_character_set(MYSQL *mysql, const char *csname)

5.选择要操作的数据库

// 选择操作的数据库
// 参数说明:
// mysql--初始化完成的句柄
// db-----要切换选择的数据库名称
// 返回值:成功返回0, 失败返回⾮0
int mysql_select_db(MYSQL *mysql, const char *db)

6.执行MySQL指令

// 执⾏sql语句
// 参数说明:
// mysql--初始化完成的句柄
// stmt_str--要执⾏的sql语句
// 返回值:成功返回0, 失败返回⾮0
int mysql_query(MYSQL *mysql, const char *stmt_str)

我们对数据库执行增删改操作,它们相对来说比较简单,因为我们只需要将指令下发给数据库即可,后面的事情我们不必关心。但如果我们执行的是查询操作,则需要通过 mysql_store_result 函数来获取查询结果。

7.获取MySQL查询结果

MYSQL_RES *mysql_store_result(MYSQL *mysql)
// 保存查询结果到本地
// 参数说明:
// mysql--初始化完成的句柄
// 返回值:成功返回结果集的指针, 失败返回NULL

uint64_t mysql_num_rows(MYSQL_RES *result)// 获取结果集中的⾏数
// 参数说明:
// result--保存到本地的结果集地址
// 返回值:结果集中数据的条数

unsigned int mysql_num_fields(MYSQL_RES *result)
// 获取结果集中的列数
// 参数说明:
// result--保存到本地的结果集地址
// 返回值:结果集中每⼀条数据的列数

MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)    
// 遍历结果集, 并且这个接⼝会保存当前读取结果位置,每次获取的都是下⼀条数据
// 参数说明:
// result--保存到本地的结果集地址
// 返回值:实际上是⼀个char **的指针,将每⼀条数据做成了字符串指针数组 
// row[0]-第0列 row[1]-第1列 ...

mysql_store_result函数会调用MYSQL变量中的st_mysql_methods中的 read_rows 函数指针来获取查询的结果。同时该函数会返回MYSQL_RES 这样一个变量,该变量主要用于保存查询的结果。同时该函数malloc了一片内存空间来存储查询过来的数据,所以我们一定要记的 free(result),不然是肯定会造成内存泄漏的。 执行完mysql_store_result以后,其实数据都已经在MYSQL_RES 变量中了,下面的API基本就是读取MYSQL_RES 中的数据。

需要注意的是,MYSQL_RES 是通过 malloc/new 空间的方式来保存查询结果的,所以当我们使用完毕之后,一定要记得释放 MYSQL_RES 对象,否则就会造成内存泄漏。同时,MYSQL_RES 结构体中存在查询结果的列数、列信息、行数、行内容等属性,我们需要使用对应的函数来获取这些信息。
在这里插入图片描述

MYSQL_ROW 本质上其实是一个二级指针,我们可以把它当作一个一级指针数组来看待,数组中的每个元素都是一级指针。同时,由于 MYSQL_RES 中保存的是查询到的多行结果,所以我们可以将 MYSQL_RES 看作是一个二级指针数组,数组中的每个元素都是二级指针 (MYSQL_ROW)。

在这里插入图片描述

将 MYSQL_RES 当作一个二维数组,那么 MYSQL_RES 中的每一个元素就代表查询结果中的一行数据 (不包含属性行),这行数据是一个一维数组,且数组中的每个元素都是 char* 类型 (mysql 在读取数据时会将所有的数据都当作字符串)。这样,我们就可以先使用 mysql_num_rows 和 mysql_num_fields 获取到结果集的行数和列数,然后以遍历二维数组的方式即可获取到全部行的内容了。

8.获取列属性

MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *res);
// 获取结果集中的列属性
// 参数说明:
// result--保存到本地的结果集地址
// 返回值:结果集中属性结构体的起始地址

在这里插入图片描述

获取各个列名,由于列名的列数和内容数据的列数相同,所以可以直接遍历获取

9.释放MySQL_RES对象-释放结果集

由于 MYSQL_RES 保存查询结果的空间是通过 malloc/new 得到的,所以当我们使用完毕后需要释放掉 MYSQL_RES 对象,防止内存泄露

// 释放结果集
// 参数说明:
// result--保存到本地的结果集地址
void mysql_free_result(MYSQL_RES *result)

10.关闭MySQL连接

当我们使用完 MySQL 后,需要关闭 MySQL 之前建立的连接。

// 释放结果集
// 参数说明:
// result--保存到本地的结果集地址
void mysql_close(MYSQL_RES *result)

10.获取mysql执行错误的原因

// 获取mysql接⼝执⾏错误原因
// 参数说明:
// mysql--初始化完成的句柄
const char *mysql_error(MYSQL *mysql)

11.MySQL的其他操作

除了上述这些操作外,MySQL C API 还支持事务、回滚等常见操作,感兴趣的同学可以了解一下。

my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
my_bool STDCALL mysql_commit(MYSQL * mysql);
my_bool STDCALL mysql_rollback(MYSQL * mysql);

12.使用案例

创建测试数据库

create database if not exists test_db;
use test_db;
create table stu(
    id int primary key auto_increment, -- 学⽣id
    name varchar(32) -- 学⽣姓名
    age int, -- 学⽣年龄
 	chinese decimal(4,2), -- 语文成绩
    math decimal(4,2), -- 数学成绩
    english decimal(4,2) -- 英语成绩
);

我们可以使用如下代码对上面创建的表进行增删改查的操作:

#include <iostream>
#include <string>
#include <mysql/mysql.h>

#define HOST "127.0.0.1"
#define USER "root"
#define PASSWORD "123456"
#define PORT 3306
#define DBNAME "gobang"

int main()
{
    // 1.初始化mysql句柄
    MYSQL *mysql = mysql_init(nullptr);
    if (mysql == nullptr)
    {
        std::cerr << "mysql init failed" << std::endl;
        return -1;
    }
    
    // 2.连接服务器
    if (mysql_real_connect(mysql, HOST, USER, PASSWORD, DBNAME, PORT, nullptr, 0) == nullptr)
    {
        std::cerr << "mysql connect failed" << std::endl;
        return -1;
    }
    
    // 3.设置客户端字符集
    if (mysql_set_character_set(mysql, "utf8") != 0)
    {
        std::cerr << "mysql set character failed" << std::endl;
        return -1;
    }
    
    // 4.选择要操作的数据库
    if (mysql_select_db(mysql, DBNAME) != 0)
    {
        std::cerr << "mysql select db failed" << std::endl;
        return -1;
    }
    
    // 5.执行sql语句
    // std::string sql = "insert stu values (1,'小明',18,82.5,98.5,86);";
    // std::string sql = "update stu set name='小红' where id=1;";
    // std::string sql = "delete from stu where id=1;";
    std::string sql = "select * from stu;";
    if (mysql_query(mysql, sql.c_str()) != 0)
    {
        std::cerr << "mysql query failed" << std::endl;
        std::cout << "sql: " << sql << std::endl;
        mysql_close(mysql);
        return -1;
    }
    
    // 6.如果sql语句是查询语句,则需要保存结果到本地
    MYSQL_RES *res = mysql_store_result(mysql);
    if (res == nullptr)
    {
        mysql_close(mysql);
        return -1;
    }
    
    // 7.获取结果集中的结果条数
    uint64_t num_row = mysql_num_rows(res);
    int num_col = mysql_num_fields(res);
    
    // 8.获取列名
    MYSQL_FIELD *file_array = mysql_fetch_fields(res);
    for (int i = 0; i < num_col; i++)
    {
        std::cout << file_array[i].name << "\t";
    }
    
    std::cout << "\n";
    
    // 9.遍历保存到本地的结果集
    for (int i = 0; i < num_row; i++)
    {
        MYSQL_ROW rows = mysql_fetch_row(res);
        for (int j = 0; j < num_col; j++)
        {
            printf("%s\t", rows[j]);
        }
        std::cout << std::endl;
    }
    
    // 10.释放结果集
    mysql_free_result(res);
    
    // 11.关闭连接,释放句柄
    mysql_close(mysql);
    return 0;
}

11.总结

使用 MySQL C API 连接数据库进行简单操作的步骤如下:

1.初始化mysql句柄

2.连接服务器

3.设置客户端字符集

4.选择要操作的数据库

5.执行sql语句

6.如果sql语句是查询语句,则需要保存结果到本地

7.获取结果集中的结果条数

8.遍历保存到本地的结果集

9.释放结果集

10.关闭连接,释放句柄

三、使用图形化工具连接MySQL

其实除了使用各种编程语言来连接数据库之外,在实际开发中另一种比较常用的方式是使用图形化工具来连接数据库。

市场上关于 MySQL 的图形化工具有很多,其中比较优秀的是 Navicat 和 SQLyog,但他们都是收费的,当然如果个人使用的话可以在网上下载破解版的。免费的工具也有很多,但大都不怎么好用,在免费工具中表现比较优秀的是 MySQL 官方开发的 Workbench。
相关文章阅读推荐:https://blog.csdn.net/wpc2018/article/details/122862956

四、MySQL连接池

我们可以通过线程池来实现一个MySQL的连接池,在任务队列中,创建线程池时初始化MySQL句柄,启动线程池的时候连接数据库,然后进行等待任务队列中的任务然后执行即可。对于任务,我们设计一个类,以sql语句和回调函数作为成员函数,回调函数可以在完成sql语句执行之后,将结果作为参数进行回调,在应用层进行执行。

在这里插入图片描述

网页注册和登录逻辑:

首先发送一个请求网站的请求,服务器返回一个注册页面,在服务器中可以使用php/python/java等语言提取提交的各个参数,然后在服务器和mysqld之间可以设计一个组件(软件层),遵循负载均衡的原则将请求发送给各个mysqld服务器。插入的时候只需要在一台mysqld服务器上进行插入,然后定期进行同步即可。查询的时候,在任何一台机器上查询即可。所以有些数据库不支持事物也可以。登录的时候只需要在数据库中按照用户名和密码进行查询即可。

在这里插入图片描述

  • 60
    点赞
  • 65
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 38
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

椿融雪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值