数据模块开发设计
数据管理模块,基于mysql数据库进行数据管理以及封装数据管理模块实现数据库访问。因此,在数据库中,我需要为每一张表创建出对应类,通过类实例化的对象来访问这张数据库表中的数 据,这样的话当我们要访问哪张表的时候,使⽤哪个类实例化的对象即可。
那么在五子棋对战项目当中,数据库表只有一张user表,因此我只需要为user表创建一个类即可。
在user类中,该类的作用是通过数据库接口去管理用户数据,因此需要实现的方法:
select_by_name:根据用户名查找用户信息,用于实现登录功能 select_by_id:根据用户id查找用户信息 loser:给失败玩家修改分数 win:给胜利玩家修改分数 insert:注册用户时将用户数据插入到数据库智能 login:登录验证,获取完整的用户信息
接下来是代码的实现
成员变量
由于是对数据库进行操作,因此需要用到MySQL的操作句柄,而且在进行对数据库数据操作的时候,需要添加互斥锁,因此需要用到mutex。
因此,成员变量为MySQL操作句柄以及互斥锁。
代码语言:javascript复制class user_table
{
private:
MYSQL* _mysql;//MySQL数据库操作句柄
std::mutex _mutex;//互斥锁
public:
/*.....*/
};
成员函数
成员函数即上述所说的六个方法,以及构造方法和析构方法。
代码语言:javascript复制 /*通过用户名获取用户信息*/
/*返回值为bool,判断是否成功获取,参数username为用户名,输出型参数user用于保存获取到的用户数据*/
bool select_by_name(const std::string& username,Json::Value& user);
/*通过用户id获取用户信息*/
/*返回值为bool,判断是否成功获取,参数id为用户id,输出型参数user用于保存获取到的用户数据*/
bool select_by_id(uint16_t id,Json::Value& user);
/*insert:用户注册时,将用户的数据插入到表中,即可注册成功*/
bool insert(Json::Value& user);
/*登录验证,并获取用户信息*/
bool login(Json::Value& user);
/*胜利时修改分数,传入胜利玩家的id,通过id查找出对应的数据并进行修改*/
bool win(uint16_t id);
/*失败时修改分数,传入失败玩家的id,通过id查找出对应的数据并进行修改*/
bool loser(uint16_t id);
代码实现
①构造方法
我首先需要获取MySQL数据库的操作句柄,并且连接到MySQL的服务器中,将MySQL数据库的字符集设置成utf8,因此,在构造方法中,参数有MySQL数据库服务器的地址host、MySQL数据库的用户名、密码、数据库名称、端口号。
代码语言:javascript复制 user_table(
const std::string& host,
const std::string& username,
const std::string& password,
const std::string& dbname,
uint16_t port = 3306)
{
_mysql = mysql_util::mysql_create(host,username,password,dbname,port);
assert(_mysql!=nullptr);
}
②析构方法
调用销毁句柄的方法即可;
代码语言:javascript复制 ~user_table()
{
mysql_util::mysql_destory(_mysql);
_mysql==nullptr;
}
③通过用户名获取用户信息的方法
返回值为bool,判断是否成功获取,参数username为用户名,输出型参数user用于保存获取到的用户数据。
流程:
1.先定义出MySQL的查询语句的字符串,将其保存到字符数组sql中。
2.我们需要对操作进行互斥锁,保护起来,以免其它线程进行了数据的修改。因此,划出一段空间出来,形成互斥锁的生命周期。
3.进行语句查询,查询后,获取结果集保存到本地,如果获取失败,则说明没有该用户,如果有,那么往下走。
4.获取结果集的行数,然后遍历结果集,将该用户的数据填入user中,返回回去。
5.最后释放结果集。
代码语言:javascript复制bool select_by_name(const std::string& username,Json::Value& user)
{
#define USER_BY_NAME "select id,score,total_count,win_count from user where username='%s';"
char sql[4096]={0};
sprintf(sql,USER_BY_NAME,username.c_str());/*将查询表的字符串写入到sql中*/
/*MYSQL_RES保存查询结果到本地:mysql_store_resul(_mysql)*/
MYSQL_RES* res=NULL;
/*查询操作需要使用互斥锁进行保护*/
{
std::unique_lock<std::mutex> lock(_mutex);
bool ret = mysql_util::mysql_exec(_mysql,sql);//执行查询语句
if(ret==false)
{
DLOG("user select_by_name failed!!n");
return false;
}
//查询成功,将查询结果保存到本地
res = mysql_store_result(_mysql);
if(res==NULL)
{
DLOG("have no user info!!");
return false;
}
}
/*保存到本地之后,需要将结果放入到Json::Value user中
因此,先获取到结果集的行数,然后遍历结果集,将其放入user中*/
/*获取结果集的行数*/
int row_num = mysql_num_rows(res);
if(row_num!=1)
{
DLOG("the user information queried is not unique!");
return false;
}
/*遍历结果集,将数据存储在user中,row是列数噢,row[0]为第0列*/
MYSQL_ROW row = mysql_fetch_row(res);
user["id"] = (Json::UInt64)std::stol(row[0]);/*先转化成长整型,然后转化成json形式的无符号整型*/
user["username"] = username;
user["score"] = (Json::UInt64)std::stol(row[1]);
user["total_count"] = std::stoi(row[2]);
user["win_count"] = std::stoi(row[3]);
mysql_free_result(res);
return true;
}
④通过用户id获取用户信息
返回值为bool,判断是否成功获取,参数id为用户id,输出型参数user用于保存获取到的用户数据。
流程:
1.先定义出MySQL的查询语句的字符串,将其保存到字符数组sql中。
2.我们需要对操作进行互斥锁,保护起来,以免其它线程进行了数据的修改。因此,划出一段空间出来,形成互斥锁的生命周期。
3.进行语句查询,查询后,获取结果集保存到本地,如果获取失败,则说明没有该用户,如果有,那么往下走。
4.获取结果集的行数,然后遍历结果集,将该用户的数据填入user中,返回回去。
5.最后释放结果集。
代码语言:javascript复制bool select_by_id(uint16_t id,Json::Value& user)
{
#define USER_BY_ID "select username,score,total_count,win_count from user where id='%d';"
char sql[4096]={0};
sprintf(sql,USER_BY_ID,id);/*将查询表的字符串写入到sql中*/
/*MYSQL_RES保存查询结果到本地:mysql_store_resul(_mysql)*/
MYSQL_RES *res=NULL;
/*查询操作需要使用互斥锁进行保护*/
{
std::unique_lock<std::mutex> lock(_mutex);
bool ret = mysql_util::mysql_exec(_mysql,sql);//执行查询语句
if(ret==false)
{
DLOG("user select_by_name failed!!n");
return false;
}
//查询成功,将查询结果保存到本地
res = mysql_store_result(_mysql);
if(res==NULL)
{
DLOG("have no user info!!");
return false;
}
}
/*保存到本地之后,需要将结果放入到Json::Value user中
因此,先获取到结果集的行数,然后遍历结果集,将其放入user中*/
/*获取结果集的行数*/
int row_num = mysql_num_rows(res);
if(row_num!=1)
{
DLOG("the user information queried is not unique!");
return false;
}
/*遍历结果集,将数据存储在user中,row是列数噢,row[0]为第0列*/
MYSQL_ROW row = mysql_fetch_row(res);
user["id"] = (Json::UInt64)id;;/*化成json形式的无符号整型*/
user["username"] = row[0];
user["score"] = (Json::UInt64)std::stol(row[1]);
user["total_count"] = std::stoi(row[2]);
user["win_count"] = std::stoi(row[3]);
mysql_free_result(res);
return true;
}
⑤用户注册方法
用户注册,即将用户名和用户的密码插入到数据库中,如果插入成功,即注册成功。在密码插入这一块,需要对密码进行加密。
流程:
1.首先判断传进来的用户的数据是否完整。
2.定义出MySQL的插入语句的字符串。
3.然后将字符串保存到字符数组中
4.然后上锁,接着执行插入语句。
5.成功插入,则注册成功。
代码语言:javascript复制bool insert(Json::Value& user)
{
#define INSERT_USER "insert user values(null,'%s',password('%s'),1000,0,0);"
/*判断user中的用户数据是否完整*/
if(user["username"].isNull() || user["password"].isNull())
{
DLOG("INPUT PASSWORD OR USERNAME");
return false;
}
/*数据完整,可以进行注册*/
char sql[4096]={0};
/*将执行语句的字符串形式放入到sql中*/
sprintf(sql,INSERT_USER,user["username"].asCString(),user["password"].asCString());
bool ret = mysql_util::mysql_exec(_mysql,sql);/*执行*/
if(ret==false)
{
ELOG("insert user info failed!!n");
return false;
}
return true;
}
⑥登录验证方法
流程:
1.首先对传过来的用户数据进行验证,是否具有完整性,不能缺失用户名和密码。
2.接着定义出MySQL的查询语句,目的是,通过查询语句,去查询是否能够通过该用户名和密码查询出结果,而且结果只能由一条。
3.定义出MySQL的查询语句的字符串之后,将其存储在字符数组中。
4.我们需要对操作进行互斥锁,保护起来,以免其它线程进行了数据的修改。因此,划出一段空间出来,形成互斥锁的生命周期。
5.进行语句查询,查询后,获取结果集保存到本地,如果获取失败,则说明没有该用户,因此验证失败。如果由,那么往下走。
6.获取结果集的行数,然后遍历结果集,将该用户的其它数据进行填入,返回回去。
7.最后释放结果集。
代码语言:javascript复制 bool login(Json::Value& user)
{
/*先判断登录的数据是否完整*/
if(user["password"].isNull() || user["username"].isNull())
{
DLOG("INPUT PASSWORD OR USERNAME");
return false;
}
/*以用户名和密码共同作为查询过滤条件,查询到数据则表示用户密码一致,否则错误*/
#define LOGIN_USER "select id, score, total_count, win_count from user where username='%s' and password=password('%s');"
char sql[4096]={0};
sprintf(sql,LOGIN_USER,user["username"].asCString(),user["password"].asCString());
/*查询语句执行,查看是否有数据,如果有,也只能有一条数据,如果没数据,说明登录验证不通过*/
/*查看数据,使用保存结果到本地的函数mysql_store_result()*/
MYSQL_RES* res=NULL;
{
std::unique_lock<std::mutex> lock(_mutex);
bool ret = mysql_util::mysql_exec(_mysql,sql);
if(ret==false)
{
DLOG("user login failed!!n");
return false;
}
res = mysql_store_result(_mysql);
if(res==NULL)/*没有数据,登录验证失败*/
{
DLOG("have no login user info!!");
return false;
}
}
/*有数据,获取该用户的其它数据:分数、场次等*/
int row_num = mysql_num_rows(res);
if(row_num!=1)
{
DLOG("the user information queried is not unique!");
return false;
}
MYSQL_ROW row = mysql_fetch_row(res);
user["id"] = (Json::UInt64)std::stol(row[0]);
user["score"] = (Json::UInt64)std::stol(row[1]);
user["total_count"] = std::stoi(row[2]);
user["win_count"] = std::stoi(row[3]);
mysql_free_result(res);
return true;
}
⑦胜利时修改分数
流程:先定义出MySQL更新语句的字符串,由于是胜利的,因此分数 30,对战总场次 1,胜利场次 1。
将字符串保存到sql数组中,然后上互斥锁,不能让修改数据的时候,有其它线程同时访问,造成数据的错误。
最后执行语句
代码语言:javascript复制 bool win(uint16_t id)
{
#define USER_WIN "update user set score=score 30, total_count=total_count 1, win_count=win_count 1 where id=%d;"
char sql[4096]={0};
sprintf(sql,USER_WIN,id);
std::unique_lock<std::mutex> lock(_mutex);
bool ret = mysql_util::mysql_exec(_mysql,sql);
if(ret==false)
{
DLOG("update winner info failed!n");
return false;
}
return true;
}
⑧失败时修改分数
流程:先定义出MySQL更新语句的字符串,由于是失败的,因此分数减去30,对战总场次 1。
将字符串保存到sql数组中,然后上互斥锁,不能让修改数据的时候,有其它线程同时访问,造成数据的错误。
最后执行语句。
代码语言:javascript复制 bool loser(uint16_t id)
{
#define USER_LOSER "update user set score=score-30, total_count=total_count 1 where id=%d;"
char sql[4096]={0};
sprintf(sql,USER_LOSER,id);
std::unique_lock<std::mutex> lock(_mutex);
bool ret = mysql_util::mysql_exec(_mysql,sql);
if(ret==false)
{
DLOG("update loser info failed!n");
return false;
}
return true;
}