实现后台数据库访问模块的框架,能够实现验证请求并响应(支持数据库操作)。
数据库设计
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 成功=!NULL
LOG_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的值通常为0
if (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=utf8
INSERT 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