Windows 下 使用 MySQL C API 存取 BLOB 数据

目录

写作背景

内容提要

环境部署

项目配置

1.MySql 部分

2.msgpack 部分

相关代码

1.MySQL 表的设计

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;
}

posted @   狂野先森  阅读(29)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示