# C MySQL API 详解

# 普通调用

# 开发环境

需要引用头文件 #include <mysql.h>

  • 默认已经安装了 MySQL 环境
  • 配置 VS2022 项目属性
    MySQL开发环境目录地址
    • 右键项目,进入属性
    • 需要在 属性 的 VC++ 目录中包含目录和引用目录中分别添加 MySQL 安装文件夹下的 include 文件夹和 lib 文件夹
      VS2022属性配置
    • 在属性中输入目录下的附加依赖项添加 MySQL 加密动态库
      加密动态库位置
      添加MySQL加密动态库

# 代码测试

#include <stdio.h>
#include <mysql.h>
int main(void)
{
	printf("MySQL Environment Successful\n");
	return 0;
}
// 引用头文件不报错,即环境匹配成功

# MySql.h 用法

# 运行流程图

C++调用MySQL流程图

# C 连接 MySQL 语法详解

一共 个部分,一个通过一个小程序实例,通过注释的形式将 MySQL API 吃透,另一种是分解版逐步吃透。其为 MySQL.h内部头文件

# 合集版

# 本地数据先进行的操作
show databases;
create database cpp;
use cpp;
show TABLES;
-- 创建 dept 部门表
CREATE TABLE dept ( 
d_id INT PRIMARY KEY,
d_name VARCHAR ( 20 ),
d_des VARCHAR ( 50 ) 
);
SHOW TABLES;
DESC dept;
INSERT INTO dept VALUES(01,'陆军','河南');
INSERT INTO dept VALUES(02,'第二陆军','开封');
INSERT INTO dept VALUES(03,'第三陆军','西安');
INSERT INTO dept VALUES(04,'第四陆军','山东');
INSERT INTO dept VALUES(05,'第五陆军','菏泽');
INSERT INTO dept VALUES(06,'第六陆军','商丘');
INSERT INTO dept VALUES(07,'第七陆军','郑州');
INSERT INTO dept VALUES(08,'第八陆军','洛阳');
SELECT * FROM dept;
# C 调用合集
#include <stdio.h>
#include <mysql.h>
int main(void)
{
	printf("MySQL Environment Successful\n");
	// 初始化运行环境
	// 该函数将分配初始化,并返回新对象,并通过返回这个对象去连接 MySQL 服务器
	MYSQL* mysql = mysql_init(NULL);
	// 判断是否初始化成功
	if (mysql == NULL)
	{
		printf("mysql_init() error");
		return -1;
	}
	printf("数据库初始化成功\n");
	// 连接 MySQL 服务器
	mysql = mysql_real_connect
	(
		mysql, //mysql_init () 函数返回值
		"localhost", //mysql 的 IP 地址
		"root", //myslq 的用户名
		"5211314", //mysql 的密码
		"cpp",   // 使用的数据库名称
		3306,     // 监听端口,0 为默认 3306 端口
		NULL,   // 本地套接字,不指定为 NULL
		0       // 默认 0
	);
	// 检查数据库连接情况
	if (mysql == NULL)
	{
		printf("mysql_real_connect() error\n");
		return -1;
	}
	printf("数据库连接成功\n");
	// 输出 MySQL API 使用的编码,参数为数据库对象
	printf("mysql api使用的默认编码:%s\n", mysql_character_set_name(mysql));
	// 设置数据库编码为 utf8,
	// 参数:数据库对象,编码
	mysql_set_character_set(mysql, "gbk"); // 将编码改为 gbk 才可以进行数据插入,报错,乱码
	printf("mysql api修改后的编码为:%s\n", mysql_character_set_name(mysql));
	// 执行 sql 语句
	// 查询 cpp 数据库下的 dept 部门表
	const char* sql = "select * from dept";
	// 执行 sql 语句
	// 参数:数据库对象,const char * 的数据库执行语句
	// 执行成功返回 0
	int ret = mysql_query(mysql, sql);
	if (ret != 0)
	{
		//mysql_error 返回 mysql 数据库对象错误描述
		printf("mysql_query()失败了,原因:%s\n", mysql_error(mysql));
		return -1;
	}
	// 取出结果,参数 mysql 数据对象
	//MYSQL_RES 对应一块内存八寸执行之后的结果集
	// 若错误,返回 NULL
	MYSQL_RES* res = mysql_store_result(mysql);
	if (res == NULL)
	{
		printf("mysql_store_result()失败了,原因是:%s\n", mysql_error(mysql));
		return -1;
	}
	// 得到结果集中的列数
	// 参数:传入 mysql_store_result () 得到的返回值
	int num = mysql_num_fields(res);
	// 得到所有列的名字,并且输出
	// 参数传入 mysql_store_result () 得到的返回值
	//MYSQL_FIELD 对应的是一个结构体
	MYSQL_FIELD* fields = mysql_fetch_fields(res);
	// 遍历输出 mysql 的 name
	for (int i = 0; i < num; i++)
	{
		printf("%s\t\t", fields[i].name);
	}
	printf("\n");
	
	// 遍历结果集中的所有行
	// 参数:mysql_store_result () 得到的返回值
	// 成功得到当前记录中每一个字段的值
	// 失败 / 数据读完 返回 NULL
	MYSQL_ROW row;
	while ((row = mysql_fetch_row(res)) != NULL)
	{
		// 将当前列中的每一列信息读出
		for (int i = 0; i < num; ++i)
		{
			printf("%s\t\t", row[i]);
		}
		printf("\n");
	}
	// 释放资源 - 结果集
	mysql_free_result(res);
	// 写入数据
	//MySQL 默认自动提交数据库
	// 设置事务为手动提交
	mysql_autocommit(mysql, 0);
	// 执行成功返回 0
	int ret1 = mysql_query(mysql, "insert into dept values(10,'第一海军','海南')");
	int ret2 = mysql_query(mysql, "insert into dept values(11,'第二海军','福建')");
	int ret3 = mysql_query(mysql, "insert into dept values(12,'第三海军','辽宁')");
	printf("ret1=%d  ret2 = %d  ret3 = %d\n", ret1, ret2, ret3);
	if (ret1 == 0 && ret2 == 0 && ret3 == 0)
	{
		printf("执行成功,正在提交事务\n");
		// 提交事务
		mysql_commit(mysql);
	}
	else
	{
		printf("执行失败,正在回滚....\n");
		printf("执行失败原因: %s\n", mysql_error(mysql));
		mysql_rollback(mysql);
	}
	// 释放数据库资源
	mysql_close(mysql);
	return 0;
}

# 分解头文件

# 初始化运行环境
// 该函数将分配初始化,并返回新对象,并通过返回这个对象去连接 MySQL 服务器,得到一块内存并保存
MYSQL* mysql_init(MYSQL * mysql);
// 参数 NULL
# 连接数据库
MYSQL * mysql_real_connect(
MYSQL *mysql,   //mysql_init () 函数的返回值即指向的指针
const char *host, // 主机地址 ip 地址,localhost,NULL 代表本地连接
const char *user, // 服务器用户名
const char *passwd,// 连接服务器密码
const char *db,   // 要使用数据库名称
unsigned int port,  // 监听的端口号,若为 0,则默认 mysql 的 3306 端口
const char *unix_socket, // 本地套接字,不指定为 NULL
unsigned long clientflag // 通常指定为 0
);
# 执行 SQL 语句
int mysql_query(MYSQL *mysql,const char *query);

参数:

  • mysql:mysql_real_connect () 的返回值
  • query : 可以执行的 sql 语句,不要 ;
    返回值:
  • 成功返回 0 就,结果集在 MySQL 对象中
  • 错误返回 非0
# 获取结果集
  • 将结果集从 MySQL (参数) 对象中取出
  • MYSQL_RES 对应一块内存,保存着查询之后的结果集
  • 返回具有多个结果的 MYSQL_RES 结果集合
  • 若错误,返回 NULL
MYSQL_RES *mysql_store_result(MYSQL *mysql);
# 获取结果集的列数
unsigned int mysql_num_fields(MYSQL_RES *result);

参数

  • 调用 mysql_store_result () 得到的返回值
    返回值
  • 结果集中的列数
# 获取结果集中所有列的名字
MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *result);

参数

  • mysql_store_result () 得到的结果集
    返回值
  • MYSQL_FIELD* 指向的一个结构体
# 结构体定义
typedef struct MYSQL_FIELD {
  char *name;               /* Name of column  */
  char *org_name;           /* Original column name, if an alias */
  char *table;              /* Table of column if column was a field */
  char *org_table;          /* Org table name, if table was an alias */
  char *db;                 /* Database for table */
  char *catalog;            /* Catalog for table */
  char *def;                /* Default value (set by mysql_list_fields) */
  unsigned long length;     /* Width of column (create length) */
  unsigned long max_length; /* Max width for selected set */
  unsigned int name_length;
  unsigned int org_name_length;
  unsigned int table_length;
  unsigned int org_table_length;
  unsigned int db_length;
  unsigned int catalog_length;
  unsigned int def_length;
  unsigned int flags;         /* Div flags */
  unsigned int decimals;      /* Number of decimals in field*/
  unsigned int charsetnr;     /* Character set */
  enum enum_field_types type; /* Type of field. See mysql_com.h for types */
  void *extension;
} MYSQL_FIELD;
# 获得结果集中字段的长度
unsigned long *mysql_fetch_lengths(MYSQL_RES *result);

参数:

  • result: 通过查询得到的结果集

返回值

  • 无符号长整数的数组表示各列的大小,若错误返回 NULL
# 遍历结果集
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);

参数:

  • result:通过查询得到的结果集
    返回值:

  • 成功:得到了当前记录中每个字段的值

  • 失败 / 数据已经读完:NULL,

# MYSQL_ROW
typedef char** MYSQL_ROW;
  • 返回值二级指针, char** 指向一个指针数组,类型是数组,里面的元素仍未指针, char* 类型,
  • char* [] :数组中的字符串对应的一列数据
  • 如果想要遍历整个结果集,需要对函数 MYSQL_ROW 进行循环调用
# 资源回收
// 释放结果集
void mysql_free_result(MYSQL_RES *result);
// 关闭 MySQL 实例
void mysql_close(MYSQL *mysql);
# 字符编码
# 返回当前编码
// 获取API默认使用的字符编码(当前连接返回默认的字符集)
const char * mysql_character_set_name(MYSQL *mysql);
# 设置编码
// 设置 API 使用的字符集
int mysql_set_character_set(MYSQL *mysql,char *csname);

参数:

  • csname 为要设置的字符集:常用 utf8
# 事务

MySql 会默认进行事务提交,需要设置逻辑处理时,多吃写操作,开始时创建事务,结束时判断是否全部成功,再提交或处理错误

my_bool mysql_autocommit(MYSQL *mysql,my_bool mode);

参数:

  • mode 如果模式为 1启用 autocommit 模式
  • 如果模式为 0禁止 autocommit 模式
# 事务提交
my_bool mysql_commit(MYSQL *mysql);

返回值:

  • 成功:0
  • 失败:非 0
# 数据回滚

回滚到事务处理前的状态

my_bool mysql_rollback(MYSQL *mysql);

返回值:

  • 成功: 0
  • 失败: 非 0
# 打印错误信息
// 返回错误的描述
const char *mysql_error(MYSQL *mysql);
// 返回错误的编码
unsigned int mysql_error(MYSQL *mysql);
# 参数传递依赖关系

参数传递依赖关系

# C++ 封装 MySQL API

# 什么时候调用释放结果集

  • 析构函数
  • 可能会对数据库进行多次查询,每次查询一次都会得到结果集,查询是清空掉上次的结果集

# 源代码

# MySQLConnect.h

#pragma once
#ifndef MYSQLCONNECT_H
#define MYSQLCONNECT_H
#include <iostream>
#include <mysql.h>
using namespace std;
class MySqlConnect
{
private:
	// 什么时候调用释放结果集
	//1, 析构函数 2,可能会对数据库进行多次查询,每次查询一次都会得到结果集,查询是清空掉上次的结果集
	void freeResult(); // 释放结果集
	MYSQL* m_conn = nullptr; // 保存 MySQL 初始化的私有成员
	MYSQL_RES* m_result = nullptr; // 报错结果集
	MYSQL_ROW m_row = nullptr; // 保存着当前字段的所有列的数值
public:
	// 初始化数据库连接
	MySqlConnect();
	// 释放数据库连接
	~MySqlConnect();
	// 连接数据库,使用默认端口可省略端口书写
	bool connect(string user, string passwd,string dbName,string ip,unsigned short port = 3306 );
	// 更新数据库 (插入,更新,删除),传递字符串
	bool update(string sql);
	// 查询数据库,单词 query: 查询
	bool query(string sql);
	// 遍历查询得到的结果集,每调一次,从结果集中取出一条数据
	bool next(); 
	// 得到结果集中的字段值,取记录里面字段方法
	string value(int index); 
	// 事务操作,关闭自动提交
	bool transaction();
	// 提交事务
	bool commit();
	// 事务回滚;
	bool rollback();
};
#endif // !MYSQLCONNECT_H

# MySQLConnect.cpp

#include "MySQLConnect.h"
void MySqlConnect::freeResult()
{
	if (m_result)
	{
		mysql_free_result(m_result);
		m_result = nullptr;
	}
}
MySqlConnect::MySqlConnect()
{
	m_conn = mysql_init(nullptr);
	mysql_set_character_set(m_conn, "utf8");
}
MySqlConnect::~MySqlConnect()
{
	if (m_conn != nullptr)
	{
		mysql_close(m_conn);
	}
	freeResult();
}
bool MySqlConnect::connect(string user, string passwd, string dbName, string ip, unsigned short port)
{
	//ip 传入为 string,使用.str 将 ip 转为 char * 类型
	MYSQL* ptr = mysql_real_connect(m_conn, ip.c_str(), user.c_str(), passwd.c_str(), dbName.c_str(), port, nullptr, 0);
	// 连接成功返回 true
	// 如果连接成功返回 TRUE,失败返回 FALSE
	return ptr!=nullptr;
}
bool MySqlConnect::update(string sql)
{
	//query 执行成功返回 0
	if (mysql_query(m_conn, sql.c_str()))
	{
		return false;
	};
	return true;
}
bool
MySqlConnect::query(string sql)
{
	freeResult();
	//query 执行成功返回 0
	if (mysql_query(m_conn, sql.c_str()))
	{
		return false;
	};
	m_result = mysql_store_result(m_conn);
	return true;
}
bool MySqlConnect::next()
{
	// 如果结果集为空则没有必要遍历
	if (m_result != nullptr)
	{
		// 保存着当前字段的所有列的数值
		m_row = mysql_fetch_row(m_result);
		return true;
	}
	return false;
}
string MySqlConnect::value(int index)
{ 
	// 表示列的数量
	int row_num = mysql_num_fields(m_result); // 函数得到结果集中的列数
	// 如果查询的的 index 列大于总列,或小于 0,是错误的
	if (index >= row_num || index < 0)
	{
		return string();
	}
	char* val = m_row[index]; // 若为二进制数据,中间是有 "\0" 的
	unsigned long length = mysql_fetch_lengths(m_result)[index];
	return string(val,length); // 传入 length 就不会以 "\0" 为结束符,而是通过长度把对应的字符转换为 string 类型
}
bool MySqlConnect::transaction()
{
	return mysql_autocommit(m_conn,false); // 函数返回值本身就是 bool 类型
}
bool MySqlConnect::commit()
{
	return mysql_commit(m_conn);// 提交
}
bool MySqlConnect::rollback()
{
	return mysql_rollback(m_conn);//bool 类型,函数成功返回 TRUE,失败返回 FALSE
}