MySQL C API入门

MySQL C API

1 环境配置

软件:MySQL8.0、Visual Studio 2022
在VS2022中选择新建项目:C++空项目
在项目中添加新的源文件cpp
找到MySQL安装目录,在安装文件中找到 include 文件夹和 lib 文件夹
选择 项目 - 属性 - VC++目录
在 包含目录 中添加 include 文件夹的路径
在这里插入图片描述
选择 C/C++
在 附加包含目录 中添加 include 文件夹的路径
在这里插入图片描述
选择 链接器 - 常规
在 附加库目录 中添加lib 文件夹的路径
在这里插入图片描述
选择 输入
在 附加依赖项 中添加 libmysql.lib

在这里插入图片描述
将MySQL安装目录中的 libmysql.lib 文件复制到 C:\Windows\System32 中
或者复制到创建的cpp文件目录下

2 API程序

2.1 头文件

#include<mysql.h> //MySQL官方提供的头文件,是使用MySQL的C API的必要

#pragma comment(lib,"libmysql.lib") //指示程序连接libmysql.lib这个库,libmysql也由官方提供

2.2 初始化MySQL数据库

在调用任何其他 MySQL 函数之前,无论应用程序是常规 Client 端程序还是使用嵌入式服务器,都请调用此函数以初始化 MySQLClient 端库。如果应用程序使用嵌入式服务器,则此调用将启动服务器并初始化服务器使用的所有子系统(mysys,InnoDB等)。
返回值为零,成功。如果发生错误,则为非零值。
在非多线程环境中,可以省略对mysql_library_init()的调用,因为mysql_init()将根据需要自动调用它。
但是,mysql_library_init()在多线程环境中不是线程安全的,因此mysql_init()也不是。
无论是调用mysql_library_init()还是间接通过mysql_init(),都必须在生成任何线程之前调用mysql_library_init(),或者使用 Mutex 来保护该调用。在进行任何其他 Client 端库调用之前,请执行此操作。

mysql_library_init(int argc,char **argv,char **groups);
//如果没有服务器的命令行参数,则argc可以是0(零)。
//对于仅用作常规(非嵌入式)Client 端的应用程序,这是常见情况,
//并且该调用通常写为mysql_library_init(0,NULL,NULL)。

//示例
mysql_library_init(0,NULL,NULL);

//当要传递参数(argc大于0)时,argv的第一个元素将被忽略(通常包含程序名称)。 
//mysql_library_init()复制参数,因此可以安全地在调用后销毁argv或groups。
//对于嵌入式应用程序,如果要连接到外部服务器而不启动嵌入式服务器,则必须为argc指定一个负值。
//groups参数是一个字符串数组,用于指示要从中读取选项的选项文件中的组。
//参见参考手册第 4.2.2.2 节“使用选项文件”。在数组NULL中进行最后的 Importing。
//为了方便起见,如果groups参数本身是NULL,则默认使用[server]和[embedded]组。

mysql_library_end();
//完成所有对数据库的操作后使用结束语句

//应用实例
#include <mysql.h>
#include <stdlib.h>

int main(void) {
  if (mysql_library_init(0, NULL, NULL)) {
    fprintf(stderr, "could not initialize MySQL client library\n");
    exit(1);
  }

  /* Use any MySQL API functions here */

  mysql_library_end();

  return EXIT_SUCCESS;
}
/***********************************/
#include <mysql.h>
#include <stdlib.h>

static char *server_args[] = {
  "this_program",       /* this string is not used */
  "--datadir=.",
  "--key_buffer_size=32M"
};
static char *server_groups[] = {
  "embedded",
  "server",
  "this_program_SERVER",
  (char *)NULL
};

int main(void) {
  if (mysql_library_init(sizeof(server_args) / sizeof(char *),
                        server_args, server_groups)) {
    fprintf(stderr, "could not initialize MySQL client library\n");
    exit(1);
  }

  /* Use any MySQL API functions here */

  mysql_library_end();

  return EXIT_SUCCESS;
}

2.3 连接到数据库

● 通过调用mysql_init()初始化连接处理程序,并通过调用mysql_real_connect()连接到服务器。
● 发出SQL语句并处理其结果。
● 通过调用mysql_close(),关闭与MySQL服务器的连接。

mysql_init(MYSQL *mysql);
//分配或初始化一个适用于 mysql_real_connect() 的 MYSQL 对象。
//如果 mysql 是一个 NULL 指针,该函数分配、初始化并返回一个新对象。
//否则,初始化对象并返回对象的地址。
//如果 mysql_init() 分配了一个新对象,则在调用 mysql_close() 以关闭连接时将其释放。
//如果内存不足,则返回 NULL。

mysql_real_connect(MYSQL *mysql,              //
                   const char *host,          //主机名或地址
                   const char *user,          //用户名
                   const char *passwd,        //用户密码
                   const char *db,            //要连接的数据库
                   unsigned int port,         //MySQL服务的端口号
                   const char *unix_socket,   //一般为NULL,unix系统下可以是套接字或命名管道
                   unsigned long client_flag);//通常为0,也可以其他设置,见手册5.4.54                                           
//mysql_real_connect() 尝试建立与在主机上运行的 MySQL 服务器的连接。
//在执行任何其他需要有效 MYSQL 连接处理程序结构的 API 函数之前,客户端程序必须成功连接到服务器。

mysql_close(MYSQL *mysql);
//关闭以前打开的连接。
//如果处理程序是由 mysql_init() 或 mysql_connect() 自动分配的,
//mysql_close() 也会释放 mysql 指向的连接处理程序。关闭后不要使用处理程序。
#include<mysql.h> //包含<mysql.h头文件
#include<string>
#pragma comment(lib,"libmysql.lib") //链接libmysql.lib库

MYSQL* mysql1 = NULL; //定义一个名为mysql1的MYSQL结构体空指针

bool ini_mysql() //初始化程序,布尔类型函数,返回值为 true or false
{
    if (mysql_library_init(0, NULL, NULL)) //初始化MySQL,成功为0,不成功非0
    {
        printf("初始化失败!\n");
        return false;
    }
    else
    {
        printf("初始化成功!\n");
        return true;
    }
}

bool connect_mysql() //链接数据库程序
{
    mysql1 = mysql_init(NULL); //初始化指针
    
    if (mysql_real_connect(mysql1, "127.0.0.1", "root", "root", "world", 3306, NULL, 0))
    {                    //结构体指针、主机、     用户名、  密码、数据库名称、端口
        printf("connect ok");
        return true;
    }
    else
    {
        printf("error");
        return false;
    }
}

int main()
{
    ini_mysql(); //调用初始化程序
    
    connect_mysql(); //调用连接数据库程序
    
    /*
    MySQL API 语句
    */  
    
    mysql_close(mysql1); //关闭数据库连接
    
    mysql_library_end(); //关闭MySQL
    
    return 0;
}

2.4 API 查询

mysql_query
执行由“Null终结的字符串”查询指向的SQL查询。正常情况下,字符串必须包含1条SQL语句,而且不应为语句添加终结分号(‘;’)或“\g”。如果允许多语句执行,字符串可包含多条由分号隔开的语句。请参见25.2.9节,“多查询执行的C API处理”。
mysql_query()不能用于包含二进制数据的查询,应使用mysql_real_query()取而代之(二进制数据可能包含字符‘\0’,mysql_query()会将该字符解释为查询字符串结束)。
如果查询成功,返回0。如果出现错误,返回非0值。

mysql_query(MYSQL *mysql,const char *query);
//实例
mysql_query(mysql,"select * from table1 where column1 = 'value1' ");

mysql_real_query
执行由“query”指向的SQL查询,它应是字符串长度字节“long”。正常情况下,字符串必须包含1条SQL语句,而且不应为语句添加终结分号(‘;’)或“\g”。如果允许多语句执行,字符串可包含由分号隔开的多条语句。请参见25.2.9节,“多查询执行的C API处理”。
对于包含二进制数据的查询,必须使用mysql_real_query()而不是mysql_query(),这是因为,二进制数据可能会包含‘\0’字符。此外,mysql_real_query()比mysql_query()快,这是因为它不会在查询字符串上调用strlen()。
如果查询成功,返回0。如果出现错误,返回非0值。

mysql_real_query(MYSQL *mysql, const char *query, unsigned long length);
//实例
mysql_real_query(mysql, 
                 "select * from table1 where column1 = 'value1' ", 
                 (unsigned long)strlen("select * from table1 where column1 = 'value1' "))

除了 SELECT 命令外,mysql_query 和 mysql_real_query 还可执行如 INSERT、UPDATE、DELETE 等命令,此时可以通过:
mysql_affected_rows
发现有多少行已被改变(影响)
返回上次UPDATE更改的行数,上次DELETE删除的行数,或上次INSERT语句插入的行数。对于UPDATE、DELETE或INSERT语句,可在mysql_query()后立刻调用。对于SELECT语句,mysql_affected_rows()的工作方式与mysql_num_rows()类似。
返回值:大于0的整数表明受影响或检索的行数。“0”表示UPDATE语句未更新记录,在查询中没有与WHERE匹配的行,或未执行查询。“-1”表示查询返回错误,或者,对于SELECT查询,在调用mysql_store_result()之前调用了mysql_affected_rows()。由于mysql_affected_rows()返回无符号值,通过比较返回值和“(my_ulonglong)-1”或等效的“(my_ulonglong)~0”,检查是否为“-1”。

mysql_affected_rows(MYSQL *mysql);
//实例
mysql_query(mysql,"UPDATE products SET cost=cost*1.25 WHERE group=10");
printf("%ld products updated",(long) mysql_affected_rows(mysql));

对于 mysql_query 和 mysql_real_query 查询到的结果,可通过:
mysql_store_result()
对于成功检索了数据的每个查询(SELECT、SHOW、DESCRIBE、EXPLAIN、CHECK TABLE等),必须调用mysql_store_result()或mysql_use_result() 。
对于其他查询,不需要调用mysql_store_result()或mysql_use_result(),但是如果在任何情况下均调用了mysql_store_result(),它也不会导致任何伤害或性能降低。通过检查mysql_store_result()是否返回0,可检测查询是否没有结果集(以后会更多)。
mysql_store_result()将查询的全部结果读取到客户端,分配1个MYSQL_RES结构,并将结果置于该结构中。
如果查询未返回结果集,mysql_store_result()将返回Null指针(例如,如果查询是INSERT语句)。
如果读取结果集失败,mysql_store_result()还会返回Null指针。通过检查mysql_error()是否返回非空字符串,mysql_errno()是否返回非0值,或mysql_field_count()是否返回0,可以检查是否出现了错误。
如果未返回行,将返回空的结果集。(空结果集设置不同于作为返回值的空指针)。
一旦调用了mysql_store_result()并获得了不是Null指针的结果,可调用mysql_num_rows()来找出结果集中的行数。
可以调用mysql_fetch_row()来获取结果集中的行,或调用mysql_row_seek()和mysql_row_tell()来获取或设置结果集中的当前行位置。
一旦完成了对结果集的操作,必须调用mysql_free_result()。

mysql_store_result(MYSQL *mysql);
//实例
MYSQL_RES* res; //定义一个MYSQL_RES结构指针,用于存储任何的查询结果
mysql_query(mysql,"select * from table1 where column1 = 'value1' "); //查询
res = mysql_store_result(mysql); //查询结果传入res
mysql_free_result(res); //释放res

mysql_use_result()
mysql_use_result()将初始化结果集检索,但并不像mysql_store_result()那样将结果集实际读取到客户端。它必须通过对mysql_fetch_row()的调用,对每一行分别进行检索。这将直接从服务器读取结果,而不会将其保存在临时表或本地缓冲区内,与mysql_store_result()相比,速度更快而且使用的内存也更少。客户端仅为当前行和通信缓冲区分配内存,分配的内存可增加到max_allowed_packet字节。
另一方面,如果你正在客户端一侧为各行进行大量的处理操作,或者将输出发送到了用户可能会键入“^S”(停止滚动)的屏幕,就不应使用mysql_use_result()。这会绑定服务器,并阻止其他线程更新任何表(数据从这类表获得)。
使用mysql_use_result()时,必须执行mysql_fetch_row(),直至返回NULL值,否则,未获取的行将作为下一个检索的一部分返回。
C API给出命令不同步错误,如果忘记了执行该操作,将不能运行该命令。
不应与从mysql_use_result()返回的结果一起使用mysql_data_seek()、mysql_row_seek()、mysql_row_tell()、mysql_num_rows()或mysql_affected_rows(),也不应发出其他查询,直至mysql_use_result()完成为止。(但是,提取了所有行后,mysql_num_rows()将准确返回提取的行数)。
一旦完成了对结果集的操作,必须调用mysql_free_result()。
mysql_fetch_row()
检索结果集的下一行。在mysql_store_result()之后使用时,如果没有要检索的行,mysql_fetch_row()返回NULL。在mysql_use_result()之后使用时,如果没有要检索的行或出现了错误,mysql_fetch_row()返回NULL。
行内值的数目由mysql_num_fields(result)给出。如果行中保存了调用mysql_fetch_row()返回的值,将按照row[0]到row[mysql_num_fields(result)-1],访问这些值的指针。行中的NULL值由NULL指针指明。
mysql_num_fields()
行内值的数目由mysql_num_fields(result)给出。如果行中保存了调用mysql_fetch_row()返回的值,将按照row[0]到row[mysql_num_fields(result)-1],访问这些值的指针。

mysql_use_result(MYSQL *mysql);
mysql_fetch_row(MYSQL_RES *result);
mysql_num_fields(MYSQL_RES *result);

//实例
//实例
MYSQL_RES* res; //定义一个MYSQL_RES结构指针,用于存储任何的查询结果
MYSQL_ROW row; //定义一个MYSQL_ROW结构,用于存储行的内容
mysql_query(mysql,"select * from table1 where column1 = 'value1' "); //查询
res = mysql_use_result(mysql); //查询结果传入res
		while (row = mysql_fetch_row(res)) //用循环将每一行依次传入row
		{
            //用mysql_num_fields获取一行值的数目,作为循环条件
            for (i = 0; i < mysql_num_fields(res); i++)
			{
				printf("%s ", row[i]);//打印完整一行的内容
			}
        }
mysql_free_result(res); //释放res

mysql_store_result()的1个优点在于,由于将行全部提取到了客户端上,你不仅能连续访问行,还能使用mysql_data_seek()或mysql_row_seek()在结果集中向前或向后移动,以更改结果集内当前行的位置。通过调用mysql_num_rows(),还能发现有多少行。
但是,对于大的结果集,mysql_store_result()所需的内存可能会很大,你很可能遇到内存溢出状况。

mysql_use_result()的优点在于,客户端所需的用于结果集的内存较少,原因在于,一次它仅维护一行(由于分配开销较低,mysql_use_result()能更快)。
它的缺点在于,你必须快速处理每一行以避免妨碍服务器,你不能随机访问结果集中的行(只能连续访问行),你不知道结果集中有多少行,直至全部检索了它们为止。不仅如此,即使在检索过程中你判定已找到所寻找的信息,也必须检索所有的行。

mysql_errno()
对于由mysql指定的连接,mysql_errno()返回最近调用的API函数的错误代码,该函数调用可能成功也可能失败。“0”返回值表示未出现错误。在MySQL errmsg.h头文件中,列出了客户端错误消息编号。在附录B:错误代码和消息中,也列出了这些错误。
注意,如果成功,某些函数,如mysql_fetch_row()等,不会设置mysql_errno()。
经验规则是,如果成功,所有向服务器请求信息的函数均会复位mysql_errno()。
返回值:如果失败,返回上次mysql_xxx()调用的错误代码。“0”表示未出现错误。
mysql_error()
对于由mysql指定的连接,对于失败的最近调用的API函数,mysql_error()返回包含错误消息的、由Null终结的字符串。如果该函数未失败,mysql_error()的返回值可能是以前的错误,或指明无错误的空字符串。
经验规则是,如果成功,所有向服务器请求信息的函数均会复位mysql_error()。
返回值:返回描述错误的、由Null终结的字符串。如果未出现错误,返回空字符串。

mysql_errno(MYSQL *mysql);
mysql_error(MYSQL *mysql);
//实例
if (mysql_query(mysql, "select * from city"))
	{
		printf("query error %d:%s\n", mysql_errno(mysql), mysql_error(mysql));
	}

3 完整API查询实例

#include<mysql.h> //包含mysql头文件
#include<string>
#pragma comment(lib,"libmysql.lib") //链接到libmysql库

MYSQL* mysql = NULL; //定义名为mysql的MYSQL结构体指针
MYSQL_RES* res; //定义名为res的MYSQL_RES结构体指针
MYSQL_ROW row;  //定义名为row的MYSQL_ROW结构体

bool ini_mysql() //初始化MySQL服务程序
{
	if (mysql_library_init(0, NULL, NULL)) //初始化语句与条件语句结合,初始化成功非0,失败为0
	{
		printf("init error!"); //非0,提示初始化失败
		return false;
	}
	else
	{
		printf("init OK!\n"); //为0,初始化成功
		return true;
	}
}
bool connect_mysql() //连接数据库程序
{
	mysql = mysql_init(NULL); //初始化MYSQL结构体指针
    
    //连接数据库语句与条件语句结合,成功非0,失败为0
	if (mysql_real_connect(mysql, "127.0.0.1", "root", "root", "world", 3306, NULL, 0))
	{                    //MYSQL结构体,主机,   用户名, 密码, 数据库名称,MySQL端口,
                         //world数据库是MySQL自带的
		printf("connect ok\n"); //提示连接成功
		return true;
	}
	else
	{
        //连接失败,打印错误代码和提示
		printf("connect error %d:%s\n", mysql_errno(mysql), mysql_error(mysql));
		return false;
	}
}
int main()
{
	int i = 0; //定义循环数
	ini_mysql(); //调用MySQL初始化程序
	connect_mysql(); //调用连接数据库程序
    
    //查询语句与条件语句结合,查询成功为0,失败非0
	if (mysql_query(mysql, "select * from city limit 5"))
	{
        //查询失败,打印错误代码和提示
		printf("query error %d:%s\n", mysql_errno(mysql), mysql_error(mysql));
	}
	else
	{
		res = mysql_use_result(mysql); //查询结果集传入res
		printf("\tName\n\t----------\n");
		while (row = mysql_fetch_row(res)) //res结果集的每一行依次传入row,
                                           //传入NULL说明结束,跳出循环
		{
			printf("\t%s\n",row[1]);
		}
		printf("\t----------\n");
		mysql_free_result(res); //释放res的内存
	}
    
    //查询语句与条件语句结合,查询成功为0,失败非0
	if (mysql_real_query(mysql, 
		                 "select * from city limit 2",
		                 (unsigned long)strlen("select * from city limit 2")))
	{
        //查询失败,打印错误代码和提示
		printf("real query error %d:%s\n",mysql_errno(mysql),mysql_error(mysql));
	}
	else
	{
		res = mysql_store_result(mysql);//查询结果集传入res
	
		while (row = mysql_fetch_row(res))//res结果集的每一行依次传入row,
                                           //传入NULL说明结束,跳出循环
		{
            //用循环输出完整一行的内容
			for (i = 0; i < mysql_num_fields(res); i++)
			{
				printf("%s ", row[i]);
			}
			printf("\t%d\n", i);
		}
		
		mysql_free_result(res);//释放res的内存
	}
	mysql_close(mysql);//关闭MYSQL结构体指针
	mysql_library_end();//结束MySQL服务
	return 0;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值