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

3 篇文章 0 订阅

目录

写作背景

内容提要

环境部署

项目配置

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值