实现后台数据库访问模块的框架,能够实现验证请求并响应(支持数据库操作)。
数据库设计
class SqlTabel //负责数据库表的创建
{
public:SqlTabel(std::shared_ptr<MysqlConnection> sqlconn) :sqlconn_(sqlconn) {}bool CreateUserInfo() //创建用户表{const char* pUserInfoTabel = " \CREATE TABLE IF NOT EXISTS userinfo( \id int(16) NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT'用户id', \mobile varchar(16) NOT NULL DEFAULT '13000000000' COMMENT'手机号', \username varchar(128) NOT NULL DEFAULT '' COMMENT'用户名', \verify int(4) NOT NULL DEFAULT 0 COMMENT'验证', \registertm timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT'注册时间', \money int(4) NOT NULL DEFAULT 0 COMMENT'余额', \INDEX mobile_index(mobile) \)";if (!sqlconn_->Execute(pUserInfoTabel)){LOG_ERROR("create table userinfo table failed. error msg: %s", sqlconn_->GetErrInfo());return false;}return true;}bool CreateBikeTable() //创建单车表{const char* pBikeInfoTabel = " \CREATE TABLE IF NOT EXISTS bikeInfo( \id int NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT'单车id', \devno int NOT NULL COMMENT'单车编号', \status tinyint(1) NOT NULL DEFAULT 0 COMMENT'单车状态', \trouble int NOT NULL DEFAULT 0 COMMENT'损坏类型编号', \tmsg varchar(256) NOT NULL DEFAULT '' COMMENT'损坏原因描述', \latitude double(10, 6) NOT NULL DEFAULT 0 COMMENT'维度', \longitude double(10, 6) NOT NULL DEFAULT 0 COMMENT'经度', \UNIQUE(devno) \)";if (!sqlconn_->Execute(pBikeInfoTabel)){LOG_ERROR("create table bikeinfo table failed. error msg: %s", sqlconn_->GetErrInfo());return false;}return true;}private:std::shared_ptr<MysqlConnection> sqlconn_;
};
数据库访问
class SqlRecordSet { //数据库访问接口
public:SqlRecordSet() :m_pRes(NULL){}explicit SqlRecordSet(MYSQL_RES* pRes)// 不能隐式构造{m_pRes = pRes;}MYSQL_RES* MysqlRes(){return m_pRes;}~SqlRecordSet() {if (m_pRes){mysql_free_result(m_pRes); // 释放一个结果集合使用的内存}}/** 你已经设置了结果集,此时若要再次设置结果集,那么之前的结果集就访问不到了.(那之前的结果集访问不到了,之间的结果集还没来的及释放,就是内存泄漏)* 所以,你要设置结果集的前提是:结果集是空的.* 不是空的,咱们不让他设置*///设置结果集inline void SetResult(MYSQL_RES* pRes){//如果此时已经保存了结果集,那么就应该让程序报错,防止内存泄漏assert(m_pRes == NULL);if (m_pRes){LOG_WARN("the MYSQL_RES has already stored result , maybe will case memory leak\n");}m_pRes = pRes;}//获取结果集inline MYSQL_RES* GetResult(){return m_pRes;}//获取里面的行void FetchRow(MYSQL_ROW& row){row = mysql_fetch_row(m_pRes); // 检索结果集的下一行}//返回具体行的数量inline i32 GetRowCount(){return m_pRes->row_count;}
private:MYSQL_RES* m_pRes; // 存放mysql结果集的数据结构
};
协调处理事务
class BusinessProcessor //负责协调处理事务
{
public:BusinessProcessor(std::shared_ptr<MysqlConnection> conn);bool init();virtual ~BusinessProcessor();private:std::shared_ptr<MysqlConnection> mysqlconn_; //数据库连接std::shared_ptr<UserEventHandler> ueh_; //用户事件处理器//...可以增添其它事件处理器
};
数据库操作
class MysqlConnection {
public:MysqlConnection();~MysqlConnection();MYSQL* Mysql(){return mysql_;}//初始化bool Init(const char* szHost, int nPort, const char* szUser, const char* szPasswd, const char* szDb); //不需要/需要拿到结果bool Execute(const char* szSql);//MYSQL_RES *bool Execute(const char* szSql, SqlRecordSet& recordSet);//将pSrc特殊字符进行转义,一些特殊字符如果不转义,sql查询就会报错int EscapeString(const char* pSrc, int nSrcLen, char* pDest);void Close();//得到错误信息的方法const char* GetErrInfo();//服务断掉了,重连void Reconnect();
private:MYSQL* mysql_;//mysql的句柄,用于操作数据库
};
初始化
bool MysqlConnection::Init(const char* szHost, int nPort, const char* szUser, const char* szPasswd, const char* szDb)
{LOG_INFO("enter Init.\n");//初始化if (mysql_init(mysql_) == NULL) { // mysql_init 用来分配或者初始化一个MYSQL对象,用于连接mysql服务端->失败=NULL 成功=!NULLLOG_ERROR("init mysql failed %s , %d", this->GetErrInfo(), errno);return false;}//因为网络等原因,断开后自动重连char cAuto = 1;if (mysql_options(mysql_, MYSQL_OPT_RECONNECT, &cAuto)!=0) // 用于设置 MySQL 连接的选项->成功返回0{LOG_ERROR("mysql_options MYSQL_OPT_RECONNEC failed.");}//连接//“host”的值必须是主机名或IP地址//“user”参数包含用户的MySQL登录ID//“passwd”参数包含用户的密码//“db”是数据库名称//如果“port”不是0,其值将用作TCP/IP连接的端口号//如果unix_socket不是NULL,该字符串描述了应使用的套接字或命名管道。//client_flag的值通常为0if (mysql_real_connect(mysql_, szHost, szUser, szPasswd, szDb, nPort, NULL, 0) == NULL) // 与运行在主机上的MySQL数据库引擎建立连接{LOG_ERROR("connect mysql failed : %s ", this->GetErrInfo());}return true;}
查询结果
bool MysqlConnection::Execute(const char* szSql)
{if (mysql_real_query(mysql_, szSql, strlen(szSql)) != 0) // 执行szSql语句->成功=0 失败=-1{if (mysql_errno(mysql_) == CR_SERVER_GONE_ERROR)//断开连接就重连{Reconnect();}return false;}return true;
}bool MysqlConnection::Execute(const char* szSql, SqlRecordSet& recordSet)
{//先进行sql查询,看是否能够执行成功?if (mysql_real_query(mysql_, szSql, strlen(szSql)) != 0){if (mysql_errno(mysql_) == CR_SERVER_GONE_ERROR) // error code{Reconnect(); // 重连 }return false;}//执行成功了,就将查到的结果设置到结果集中MYSQL_RES* pRes = mysql_store_result(mysql_);if (!pRes) {return NULL;//设置失败,返回空,说明mysql_store_result失败了}recordSet.SetResult(pRes);//将结果放入结果集return false;
}
转义
/*
* pSrc 转义前的字符
* pDest 转义后的字符
*/
int MysqlConnection::EscapeString(const char* pSrc, int nSrcLen, char* pDest)
{if (!mysql_){return 0;}// mysql必须是有效的开放式连接,将“from”中的字符串编码为转义SQL字符串,将结果置于“to”中,并添加1个终结用NULL字节return mysql_real_escape_string(mysql_, pDest, pSrc, nSrcLen);//将源src转义到目标子串dest}
相关知识
1.安装mysql c++库
sudo apt-get install libmysql++-dev
sudo systemctl mysql-server
2.安装mysql
sudo apt-get install mysql-server
sudo apt-get install mysql-client
systemctl status mysql.service #请检是否安装成功
3.进入 MySQL
sudo mysql -u root -p
4.创建数据库
CREATE TABLE `users`(`use` VARCHAR(50) NOT NULL COMMENT 'id',`pwd` VARCHAR(50) NOT NULL COMMENT 'passwd',PRIMARY KEY (`use`)
)ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO `users`(`use`,`pwd`) VALUES ('aa' , 'bb'),('cc','dd');
5.常用操作
https://learn.microsoft.com/zh-cn/azure/mysql/single-server/connect-cpp
https://blog.csdn.net/qq_60755751/article/details/136631798