Windows 下 使用 MySQL C API 存取 BLOB 数据
目录
2.在 C++ 项目中,使用MySql C API 存取这张表
写作背景
有时候开发中会需要将一些不太基本,不太规范的数据存到数据库。比如不定长的数组,对象,这些内容无法与MySql中的普通字段类型对应上。如果将这些数组,对象中的基本数据类型拆分出来,又需要创建额外的表来存取。为了不创建额外的表 ,将复合型的数据类型和基本数据类型存在同一张表,于是有了这篇文章。
内容提要
1.通过MySql C API,以C++语言对数据库表进行操作
2.使用 msgpack 库对复杂数据,进行序列化/反序列化便于存取 BLOB 类型
注:文中进行的 数据库操作都是同步阻塞的,未来可能考虑换成异步非阻塞的
环境部署
安装MySql版本:8.0
这部分内容,网络上有很多文章可供参考不再赘述。
项目配置
IDE:Visual Studio 2019,解决方案平台 x64
如果编译的 libmysql 是32位的,就选 x86
1.MySql 部分
项目包含目录:mysql安装目录\include
项目包含库文件:mysql安装目录\lib //这里我们只需要 libmysql.lib
项目执行程序需要的dll:libmysql.dll //可放到系统目录下,也可以和可执行程序同一目录
2.msgpack 部分
下载msgpack-c-cpp-4.1.1:C++ msgpack GitHub
项目包含文件:……\msgpack-c-cpp-4.1.1\include
相关代码
1.MySQL 表的设计
- 创建一张玩家背包表
/* Navicat Premium Data Transfer Source Server : localhost Source Server Type : MySQL Source Server Version : 80027 Source Host : localhost:3306 Source Schema : test Target Server Type : MySQL Target Server Version : 80027 File Encoding : 65001 Date: 30/07/2022 14:38:41 */ SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_bag -- ---------------------------- DROP TABLE IF EXISTS `t_bag`; CREATE TABLE `t_bag` ( `uid` bigint(0) NOT NULL COMMENT '玩家唯一标识', `fixed_bag` blob NULL COMMENT '玩家的固定背包,容量固定', `var_bag` blob NULL COMMENT '玩家的无限背包,容量不固定', PRIMARY KEY (`uid`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;
2.在 C++ 项目中,使用MySql C API 存取这张表
-
首先是 mysql_tool.h 头文件,对 MySQL C API进行了一些封装处理便于使用
#ifndef _MSYQL_TOOL_H #define _MSYQL_TOOL_H #include "mysql.h" //包含这个文件以使用 C API #include <iostream> #include <string> #include <functional> using namespace std; //这文件对 API 进行一些封装,便于使用 //数据库配置,需要修改成自己的 constexpr auto dbip = "127.0.0.1"; //数据库ip ; constexpr auto dbuser = "root"; //数据库用户 constexpr auto dbpwd = "123456"; //数据库密码 constexpr auto dbname = "test"; //数据库名字 //mysql 执行返回 //插入,查询,存储过程…… struct sReuslt { int rtCode; //错误码 , 0:成功…… MYSQL_RES* res; //结果集 void afterExecute(std::function<void(MYSQL_RES* res)> func) { func(res); mysql_free_result(res); //需要释放 } }; //将 %s 转为 "%s" std::string getNewQuery(std::string qry) { std::string new_qry(qry); auto offset = 0; auto pos = new_qry.find("%s",offset); while (pos != std::string::npos) { new_qry = new_qry.insert(pos + 2, "\""); new_qry = new_qry.insert(pos, "\""); offset = pos + 2; pos = new_qry.find("%s", offset); } return new_qry; } class mysql_tool { private: MYSQL osql; bool is_init = false; public: void init() { mysql_init(&osql); is_init = true; } auto get() { if (false == is_init) init(); return &osql; } void connectMySql() { if (mysql_real_connect(get(), dbip, dbuser, dbpwd, dbname, 3306, NULL, 0)) cout << "数据库连接成功!\n"; else { auto errcode = mysql_errno(get()); cout <<"error code : "<<errcode<<"error info :"<< mysql_error(get()); } } auto getDBbyName(std::string dbname) { auto ret = mysql_select_db(get(), dbname.data()); return ret; } //只带有返回值,不带数据 int execute(std::string oldqry, ...) { std::string newqry = getNewQuery(oldqry); const char* qry = newqry.data(); char query[1024] = { 0 }; va_list vl; va_start(vl, oldqry); //oldary 是第一个参数的起始地址,不能替换为 qry vsnprintf(query, sizeof(query), qry, vl); va_end(vl); std::string mmm(query); auto ret = mysql_real_query(get(), mmm.c_str(), (unsigned long)strlen(mmm.c_str())); return ret; } //带有返回值,带数据 sReuslt executeEx(std::string oldqry, ...) { std::string newqry = getNewQuery(oldqry); const char* qry = newqry.data(); char query[1024] = { 0 }; va_list vl; va_start(vl, oldqry); vsnprintf(query, sizeof(query), qry, vl); va_end(vl); std::string mmm(query); auto ret = mysql_real_query(get(), mmm.c_str(), (unsigned long)strlen(mmm.c_str())); sReuslt rst; rst.rtCode = ret; auto res = mysql_store_result(get()); rst.res = res; return rst; } //输出 mysql 错误信息 void printError() { std::cout << "error info : " << mysql_error(get()) << "error code: " << mysql_errno(get()) << std::endl;; } auto getRowCount() { auto res = mysql_store_result(get()); auto rows = mysql_num_rows(res); return rows; } }; #endif
- 然后就是使用部分了,这里我是用了 main.cpp 源文件
#include <iostream> #include <memory> #include "mysql.h" #include <string> #include <vector> #include <sstream> #include "mysql_tool.h" #define MSGPACK_NO_BOOST //解决 无法打开包括文件: “boost/predef/other/endian.h”: No such file or directory #include "msgpack.hpp" using namespace std; //用来存储的复合对象 struct item { int id; //道具id int count; //道具数量 MSGPACK_DEFINE(id, count); //启用 msgpack 序列化,反序列化 }; //为了简便,这里只传了指针,没有传数组的长度 void loadData(item* fixedBag, std::vector<item>& varBag) { fixedBag[0].id = 5; fixedBag[0].count = 3; fixedBag[1].id = 4; fixedBag[1].count = 20; for (size_t i = 0; i < 10; ++i) { item tmp; tmp.id = i + 2; tmp.count = 22; varBag.push_back(tmp); //10 个道具 } } int main() { //连接数据库 mysql_tool sql; sql.connectMySql(); //选择数据库 auto ret = sql.getDBbyName("test"); if (0 != ret) { sql.printError(); return -1; } item fixedBag[2] = { 0 }; //固定背包的结构 2 个道具 std::vector<item> varBag; //可变背包 loadData(fixedBag, varBag); //填充数据 //序列化 std::stringstream ss; msgpack::pack(ss, fixedBag); std::string ser_str = ss.str(); //另外一种序列化方式 //msgpack::sbuffer buff; //msgpack::pack(buff, fixedBag); std::stringstream var_bag_ss; msgpack::pack(var_bag_ss, varBag); //序列化操作 string ser_str_var_bag = var_bag_ss.str(); //执行插入 auto rst = sql.execute("INSERT into t_bag (uid, fixed_bag, var_bag) VALUES(%d,%s,%s);", 100011, ser_str.c_str(), ser_str_var_bag.c_str()); //判断插入结果 if (0 != rst) { sql.printError(); } else { cout << "insert success!" << endl; } //查询查询数据库 string query_str = "select fixed_bag, var_bag from t_bag where uid = %d"; sql.executeEx(query_str, 100011).afterExecute( [=](MYSQL_RES* res) { auto row = mysql_fetch_row(res); while (row) { auto one = row[0]; auto tow = row[1]; item newFixedBag2[2] = { 0 }; vector<item> new_var_bag2; auto fb_obj_hdl = msgpack::unpack(row[0], strlen(row[0])); fb_obj_hdl.get().convert(newFixedBag2); auto nvb_obj_hdl = msgpack::unpack(row[1], strlen(row[1])); nvb_obj_hdl.get().convert(new_var_bag2); //bags* fixedBag = (bags*)one; int abc = 1111; row = mysql_fetch_row(res); } }); system("pause"); return 0; }
本文来自博客园,作者:狂野先森,转载请注明原文链接:https://www.cnblogs.com/geeon/p/17759623.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)