mysql 简单操作
1 通讯管道的初始化
核心数据结构:MYSQL mysql
所有后续的数据库操作(如执行 SQL、读写 BLOB 数据)都必须基于这个结构体完成 —— 它是连接程序与数据库的 “唯一入口”。
//MYSQL *handle:MySQL 连接句柄,相当于 “快递总管道”—— 已经提前和数据库建立好连接(//比如通过 mysql_init + mysql_real_connect 创建),所有数据库操作都要通过这个 “管道” 进行。MYSQL mysql;if(mysql_init(&mysql) == NULL){printf("mysql_init : %s\n", mysql_error(&mysql));return -1;}//向数据库端发起连接,传入对应数据库端的ip地址和端口,用户名,密码if (!mysql_real_connect(&mysql, ZZ_DB_SERVER_IP, ZZ_DB_USERNAME,ZZ_DB_PASSWORD, ZZ_DB_DEFAULTDB, ZZ_DB_SERVER_PORT, NULL, 0)) { // 注意这里等于零是不成功printf("mysql_real_connect: %s\n", mysql_error(&mysql));goto Exit; }
简单数据查询:
mysql_real_query 核心作用:向 MySQL 服务器发送查询指令,执行 SELECT * FROM TBL_USER
语句(宏 SQL_SELECT_TBL_USER
定义的内容)。mysql_real_query
执行完成后,查询结果(数据)仍在 MySQL 服务器端。
mysql_store_result 核心作用:从 MySQL 服务器拉取查询结果,并将结果完整存储到客户端的内存中,返回一个 “结果集句柄”(MYSQL_RES *res
)
int zz_mysql_select(MYSQL *handle){ // //执行sqlif(mysql_real_query(handle, SQL_SELECT_TBL_USER, strlen(SQL_SELECT_TBL_USER))){printf("mysql_real_query: %s\n", mysql_error(handle));return -1;}//存储到管道MYSQL_RES *res = mysql_store_result(handle);if(res == NULL){printf("mysql_store_result: %s\n", mysql_error(handle));return -2;}//row/fieldsint rows = mysql_num_rows(res);printf("rows: %d\n", rows);int fields = mysql_num_fields(res);printf("fields: %d\n", rows);MYSQL_ROW row;while((row = mysql_fetch_row(res))){int i = 0;for(i = 0;i < fields; i++){printf("%s\t", row[i]);}printf("\n");}mysql_free_result(res);
}
插入和删除
直接通过mysql_real_query插入sql语句完成
printf("case: mysql --> insert \n");if (mysql_real_query(&mysql, SQL_INSERT_TBL_USER, strlen(SQL_INSERT_TBL_USER))) { // 零是成功printf("mysql_real_query: %s\n", mysql_error(&mysql));goto Exit;}zz_mysql_select(&mysql);printf("case: mysql --> delete \n");if (mysql_real_query(&mysql, SQL_DELETE_TBL_USER, strlen(SQL_DELETE_TBL_USER))) { // 零是成功printf("mysql_real_query: %s\n", mysql_error(&mysql));goto Exit;}zz_mysql_select(&mysql);
图片的插入:
例如客户端现在有一个图片dog.jpg,现在我们要把他插入到服务端的数据库里
首先第一步我们要把这个照片读取到一个buffer数组里面
int read_image(char *filename, char *buffer){if(filename == NULL || buffer == NULL) return -1;// "rb"表示read binary,确保二进制文件(如图像)正确读取FILE *fp = fopen(filename, "rb");if(fp == NULL){printf("fopen failed\n");return -2;}fseek(fp, 0, SEEK_END);//指针指向最末尾//获取当前文件指针相对于文件开头的偏移量(单位:字节)。int length= ftell(fp);// Seek from beginning of file. fseek(fp, 0, SEEK_SET);int size = fread(buffer, 1, length, fp);if(size != length){printf("fread failed: %d %d\n", size, length);return -3;}fclose(fp);return size;
}
然后我们要把数据传到服务端了,但是照片数据是大批量的数据存储不能像简单的数据条那样存
因此,我们进行一次预处理,先创建一个预处理
/**相当于在 “总管道” 的一端搭建一个 “临时储物间”—— 后续所有和 “插入图片” 相关的操作(填地址、放包裹、发快递),都在这个 “储物间” 里准备。*/MYSQL_STMT *stmt = mysql_stmt_init(handle);//管道一端的储物间
然后插入对应要执行的sql语句,大字段我们用“?”当做占位符去暂时替代,后面我们会为每个对应的“?”占位符准备对应的数据和参数也就是存储的图片。
/*#define SQL_INSERT_IMG_USER "insert TBL_USER(U_NAME, U_GENGDER, U_IMG) VALUES('zzzgw666', 'man', ?);"里面的?表示参数占位符,此处只有一个如果有多个?的话比如两个的话就是MYSQL_BIND param[2] = {0};*///mysql_stmt_prepare:将 “插入图片的 SQL 语句” 与 stmt 绑定,告诉数据库 “我要执行什么操作”。int ret = mysql_stmt_prepare(stmt, SQL_INSERT_IMG_USER, strlen(SQL_INSERT_IMG_USER));if(ret){printf("mysql_stmt_prepare: %s\n", mysql_error(handle));return -2;}
打包,填入对应占位符的具体参数并绑定
//包裹是什么类型MYSQL_BIND param = {0};param.buffer_type = MYSQL_TYPE_LONG_BLOB;param.buffer = NULL;param.is_null = 0;param.length = NULL;// 相当于给 “包裹” 贴一张 “物品类型标签”,告诉// 快递员 “这是大件易碎品(LONG BLOB),需要特殊运输通道”,但暂时不把包裹放进储物间。ret = mysql_stmt_bind_param(stmt, ¶m);if(ret){printf("mysql_stmt_bind_param : %s\n", mysql_error(handle)); return -3;}
把存储照片的buffer 和当时的预处理的句柄传入并一同发给数据库所在的服务端,执行完此处后照片数据会暂存在服务端的一块内存中
//专门用于传输 “大容量数据”(如 BLOB/TEXT)的 API,支持分批传输(即使数据很大,也能分多次传)。/*mysql_stmt_send_long_data 执行完后,数据处于 “数据库端临时存储” 的状态,还没有正式 “入库”,就像 “包裹已经送到仓库的临时中转站,但还没放到最终货架上 */ret = mysql_stmt_send_long_data(stmt, 0, buffer, length);if(ret){printf("mysql_stmt_send_long_data: %s\n", mysql_error(handle));return -4;}
执行sql语句把照片和相应的数据插入到数据库中
/**mysql_stmt_execute 执行成功后,图片数据会直接存入数据库中,相当于 “快递已经成功送到仓库并上架”。*/ret = mysql_stmt_execute(stmt);if(ret){printf("mysql_stmt_execute : %s\n", mysql_error(handle));return -5;}
2 从服务端数据库读取照片到客户端
思路和上传差不多反过来
int mysql_read(MYSQL *handle, char *buffer, int length){if (handle == NULL || buffer == NULL || length <= 0)return -1;MYSQL_STMT* stmt = mysql_stmt_init(handle); // 管道一端的储物间int ret = mysql_stmt_prepare(stmt, SQL_SELECT_IMG_USER, strlen(SQL_SELECT_IMG_USER));if (ret) {printf("mysql_stmt_prepare: %s\n", mysql_error(handle));return -2;}MYSQL_BIND result = {0};result.buffer_type = MYSQL_TYPE_LONG_BLOB;unsigned long total_length = 0;result.length = &total_length;ret = mysql_stmt_bind_result(stmt, &result);if (ret) {printf("mysql_stmt_bind_result: %s\n", mysql_error(handle));return -3;}//execute → 数据库找到数据并暂存在服务器端ret = mysql_stmt_execute(stmt);if (ret) {printf("mysql_stmt_execute: %s\n", mysql_error(handle));return -4;}//把服务器的 “货架数据” 搬到客户端的 “临时取货点”ret = mysql_stmt_store_result(stmt);if (ret) {printf("mysql_stmt_store_result: %s\n", mysql_error(handle));return -5;}while (1){/*stmt里面是存有多条数据外层循环一次mysql_stmt_fetch抓取一条记录这里的 “一条记录”,就是数据库表中符合查询条件的某一行数据(对应 SQL 中的 row)。*/ret = mysql_stmt_fetch(stmt);if(ret !=0 && ret != MYSQL_DATA_TRUNCATED) break;int start = 0;while(start < (int)total_length){/*result.buffer和buffer用的是同一个空间,buffer是空间最开始的地方result.buffer是当前存到哪个地方*/result.buffer = buffer + start;//一次取多少字节出来result.buffer_length = 1;//开始抓取里面的核心信息也就是图片,分批存,本批数据是从result.buffer开始存mysql_stmt_fetch_column(stmt, &result, 0, start);//记录存了多少start += result.buffer_length;}}mysql_stmt_close(stmt);return total_length;
}
整体代码实现
#include<stdio.h>
#include<string.h>
#include <mysql/mysql.h>#define ZZ_DB_SERVER_IP "192.168.150.133"#define ZZ_DB_SERVER_PORT 3306#define ZZ_DB_USERNAME "admin"#define ZZ_DB_PASSWORD "123456"#define ZZ_DB_DEFAULTDB "ZZ_DB" #define SQL_INSERT_TBL_USER "insert TBL_USER(U_NAME, U_GENGDER) VALUES('zzzgw', 'man');"#define SQL_SELECT_TBL_USER "select * from TBL_USER;"#define SQL_DELETE_TBL_USER "CALL PROC_DELETE_USER('zzzgw666');"#define SQL_INSERT_IMG_USER "insert TBL_USER(U_NAME, U_GENGDER, U_IMG) VALUES('zzzgw666', 'man', ?);"#define SQL_SELECT_IMG_USER "select U_IMG from TBL_USER where U_NAME='zzzgw';"#define FILE_IMAGE_LENGTH (114468)int zz_mysql_select(MYSQL *handle){ // //执行sqlif(mysql_real_query(handle, SQL_SELECT_TBL_USER, strlen(SQL_SELECT_TBL_USER))){printf("mysql_real_query: %s\n", mysql_error(handle));return -1;}//存储到管道MYSQL_RES *res = mysql_store_result(handle);if(res == NULL){printf("mysql_store_result: %s\n", mysql_error(handle));return -2;}//row/fieldsint rows = mysql_num_rows(res);printf("rows: %d\n", rows);int fields = mysql_num_fields(res);printf("fields: %d\n", rows);MYSQL_ROW row;while((row = mysql_fetch_row(res))){int i = 0;for(i = 0;i < fields; i++){printf("%s\t", row[i]);}printf("\n");}mysql_free_result(res);
}int read_image(char *filename, char *buffer){if(filename == NULL || buffer == NULL) return -1;// "rb"表示read binary,确保二进制文件(如图像)正确读取FILE *fp = fopen(filename, "rb");if(fp == NULL){printf("fopen failed\n");return -2;}fseek(fp, 0, SEEK_END);//指针指向最末尾//获取当前文件指针相对于文件开头的偏移量(单位:字节)。int length= ftell(fp);// Seek from beginning of file. fseek(fp, 0, SEEK_SET);int size = fread(buffer, 1, length, fp);if(size != length){printf("fread failed: %d %d\n", size, length);return -3;}fclose(fp);return size;
}int write_image(char *filename, char *buffer, int length){if(filename == NULL || buffer == NULL || length <= 0) return -1;FILE *fp = fopen(filename, "wb+");//写二进制文件没有就创建if(fp == NULL){printf("fopen failed\n");return -2;}int size = fwrite(buffer, 1, length, fp);if(size != length){printf("fwrite failed: %d\n", size);return -3;}fclose(fp);return size;}// mysql *handle 相当于传送管道
//statement 发射台int mysql_write(MYSQL *handle, char *buffer, int length){//char *buffer:存储图片二进制数据的缓冲区,相当于 “装好图片的包裹”(由 read_image 函数提前打包好)。if(handle == NULL || buffer == NULL || length <= 0) return -1;/**相当于在 “总管道” 的一端搭建一个 “临时储物间”—— 后续所有和 “插入图片” 相关的操作(填地址、放包裹、发快递),都在这个 “储物间” 里准备。*/MYSQL_STMT *stmt = mysql_stmt_init(handle);//管道一端的储物间/*#define SQL_INSERT_IMG_USER "insert TBL_USER(U_NAME, U_GENGDER, U_IMG) VALUES('zzzgw666', 'man', ?);"里面的?表示参数占位符,此处只有一个如果有多个?的话比如两个的话就是MYSQL_BIND param[2] = {0};*///mysql_stmt_prepare:将 “插入图片的 SQL 语句” 与 stmt 绑定,告诉数据库 “我要执行什么操作”。int ret = mysql_stmt_prepare(stmt, SQL_INSERT_IMG_USER, strlen(SQL_INSERT_IMG_USER));if(ret){printf("mysql_stmt_prepare: %s\n", mysql_error(handle));return -2;}//包裹是什么类型MYSQL_BIND param = {0};param.buffer_type = MYSQL_TYPE_LONG_BLOB;param.buffer = NULL;param.is_null = 0;param.length = NULL;// 相当于给 “包裹” 贴一张 “物品类型标签”,告诉// 快递员 “这是大件易碎品(LONG BLOB),需要特殊运输通道”,但暂时不把包裹放进储物间。ret = mysql_stmt_bind_param(stmt, ¶m);if(ret){printf("mysql_stmt_bind_param : %s\n", mysql_error(handle)); return -3;}//专门用于传输 “大容量数据”(如 BLOB/TEXT)的 API,支持分批传输(即使数据很大,也能分多次传)。/*mysql_stmt_send_long_data 执行完后,数据处于 “数据库端临时存储” 的状态,还没有正式 “入库”,就像 “包裹已经送到仓库的临时中转站,但还没放到最终货架上 */ret = mysql_stmt_send_long_data(stmt, 0, buffer, length);if(ret){printf("mysql_stmt_send_long_data: %s\n", mysql_error(handle));return -4;}/**mysql_stmt_execute 执行成功后,图片数据会直接存入数据库中,相当于 “快递已经成功送到仓库并上架”。*/ret = mysql_stmt_execute(stmt);if(ret){printf("mysql_stmt_execute : %s\n", mysql_error(handle));return -5;}ret = mysql_stmt_close(stmt);if(ret){printf("mysql_stmt_close : %s\n", mysql_error(handle));return -6;}return ret;
}int mysql_read(MYSQL *handle, char *buffer, int length){if (handle == NULL || buffer == NULL || length <= 0)return -1;MYSQL_STMT* stmt = mysql_stmt_init(handle); // 管道一端的储物间int ret = mysql_stmt_prepare(stmt, SQL_SELECT_IMG_USER, strlen(SQL_SELECT_IMG_USER));if (ret) {printf("mysql_stmt_prepare: %s\n", mysql_error(handle));return -2;}MYSQL_BIND result = {0};result.buffer_type = MYSQL_TYPE_LONG_BLOB;unsigned long total_length = 0;result.length = &total_length;ret = mysql_stmt_bind_result(stmt, &result);if (ret) {printf("mysql_stmt_bind_result: %s\n", mysql_error(handle));return -3;}//execute → 数据库找到数据并暂存在服务器端ret = mysql_stmt_execute(stmt);if (ret) {printf("mysql_stmt_execute: %s\n", mysql_error(handle));return -4;}//把服务器的 “货架数据” 搬到客户端的 “临时取货点”ret = mysql_stmt_store_result(stmt);if (ret) {printf("mysql_stmt_store_result: %s\n", mysql_error(handle));return -5;}while (1){/*stmt里面是存有多条数据外层循环一次mysql_stmt_fetch抓取一条记录这里的 “一条记录”,就是数据库表中符合查询条件的某一行数据(对应 SQL 中的 row)。*/ret = mysql_stmt_fetch(stmt);if(ret !=0 && ret != MYSQL_DATA_TRUNCATED) break;int start = 0;while(start < (int)total_length){/*result.buffer和buffer用的是同一个空间,buffer是空间最开始的地方result.buffer是当前存到哪个地方*/result.buffer = buffer + start;//一次取多少字节出来result.buffer_length = 1;//开始抓取里面的核心信息也就是图片,分批存,本批数据是从result.buffer开始存mysql_stmt_fetch_column(stmt, &result, 0, start);//记录存了多少start += result.buffer_length;}}mysql_stmt_close(stmt);return total_length;
}int main(){//MYSQL *handle:MySQL 连接句柄,相当于 “快递总管道”—— 已经提前和数据库建立好连接(//比如通过 mysql_init + mysql_real_connect 创建),所有数据库操作都要通过这个 “管道” 进行。MYSQL mysql;if(mysql_init(&mysql) == NULL){printf("mysql_init : %s\n", mysql_error(&mysql));return -1;}//向数据库端发起连接,传入对应数据库端的ip地址和端口,用户名,密码if (!mysql_real_connect(&mysql, ZZ_DB_SERVER_IP, ZZ_DB_USERNAME, ZZ_DB_PASSWORD, ZZ_DB_DEFAULTDB, ZZ_DB_SERVER_PORT, NULL, 0)) { // 注意这里等于零是不成功printf("mysql_real_connect: %s\n", mysql_error(&mysql));goto Exit; }// printf("case: mysql --> insert \n");// if (mysql_real_query(&mysql, SQL_INSERT_TBL_USER, strlen(SQL_INSERT_TBL_USER))) { // 零是成功// printf("mysql_real_query: %s\n", mysql_error(&mysql));// goto Exit;// }// zz_mysql_select(&mysql);// printf("case: mysql --> delete \n");// if (mysql_real_query(&mysql, SQL_DELETE_TBL_USER, strlen(SQL_DELETE_TBL_USER))) { // 零是成功// printf("mysql_real_query: %s\n", mysql_error(&mysql));// goto Exit;// }// zz_mysql_select(&mysql);// printf("case : mysql --> read image and write mysql\n");char buffer[FILE_IMAGE_LENGTH] = {0};// int length = read_image("dog.jpg", buffer);// if (length < 0)// goto Exit;// mysql_write(&mysql, buffer, length); // 插入图片到数据库printf("case : mysql --> read mysql and read image\n");memset(buffer, 0, FILE_IMAGE_LENGTH);int length = mysql_read(&mysql, buffer, FILE_IMAGE_LENGTH);printf("length: %d\n",length); write_image("a.jpg", buffer, length);
Exit:mysql_close(&mysql);return 0;
}
引用自0voice