限于不同数据库操作每次都要大量的重写代码,去适用不同的数据库,最近在github上做了个开源项目,把数据库这部分操作做了一个通用库,通过xml解析来简化操作数据库编码,有兴趣的同学可以去https://github.com/shukcs/GServer看看,数据库操作在vgmysql目录下,觉得好的,请帮忙在github上点赞
数据库操作对象管理器
头文件
#include <list>
#include <string>
class ExecutItem;
class VGTable;
class VGTrigger;
class TiXmlElement;
class TiXmlNode;
class TiXmlDocument;
class MySqlStruct;
class VGDBManager
{
public:
typedef std::list<std::string> StringList;
public:
~VGDBManager();
std::string Load(const TiXmlDocument &doc, StringList &tbs);
VGTable *GetTableByName(const std::string &name)const;
ExecutItem *GetSqlByName(const std::string &name)const;
VGTrigger *GetTriggerByName(const std::string &name)const;
const char *GetMysqlHost(const std::string &db ="")const;
int GetMysqlPort(const std::string &db="")const;
const char *GetMysqlUser(const std::string &db="")const;
const char *GetMysqlPswd(const std::string &db = "")const;
const char *GetMysqlCharSet(const std::string &db = "")const;
StringList GetDatabases()const;
StringList GetTriggers()const;
public:
static SHARED_SQL VGDBManager &Instance();
static SHARED_SQL long str2int(const std::string &str, unsigned radix=10, bool *suc=NULL);
static SHARED_SQL StringList SplitString(const std::string &str, const std::string &sp, bool bSkipEmpty = true);
template<typename T, typename Contianer = std::list<T> >
static bool IsContainsInList(const Contianer ls, const T &e)
{
for (const T &itr : ls)
{
if (itr == e)
return true;
}
return false;
}
private:
VGDBManager();
std::string parseDatabase(const TiXmlElement *e);
StringList parseTables(const TiXmlNode *node);
void parseSqls(const TiXmlNode *node);
void parseTriggers(const TiXmlNode *node);
MySqlStruct *_getDbStruct(const std::string &)const;
private:
std::list<VGTable*> m_tables;
std::list<ExecutItem*> m_sqls;
std::list<MySqlStruct*> m_mysqls;
std::list<VGTrigger*> m_triggers;
};
实现
#include "VGDBManager.h"
#include "DBExecItem.h"
#include <tinyxml.h>
#include <VGTrigger.h>
static const char *sDefualtDb[5] = {
"127.0.0.1",
"gsuav",
"root",
"",
"utf8"
};
//
//VGDBManager
//
class MySqlStruct
{
public:
MySqlStruct() :m_port(3306) {}
MySqlStruct(const MySqlStruct &oth) :m_port(oth.m_port)
, m_host(oth.m_host), m_database(oth.m_host)
, m_user(oth.m_host), m_pswd(oth.m_host)
, m_charSet(oth.m_charSet){ }
int m_port;
std::string m_host;
std::string m_database;
std::string m_user;
std::string m_pswd;
std::string m_charSet;
};
//
//VGDBManager
//
VGDBManager::VGDBManager()
{
}
VGDBManager::~VGDBManager()
{
for (VGTable *itr : m_tables)
{
delete itr;
}
for (ExecutItem *itr : m_sqls)
{
delete itr;
}
for (MySqlStruct *itr : m_mysqls)
{
delete itr;
}
for (VGTrigger *itr : m_triggers)
{
delete itr;
}
}
VGDBManager &VGDBManager::Instance()
{
static VGDBManager sIns;
return sIns;
}
long VGDBManager::str2int(const std::string &str, unsigned radix, bool *suc)
{
unsigned count = str.length();
bool bSuc = false;
bool bSubMin = false;
const char *c = str.c_str();
long nRet = 0;
if ((8==radix || 10==radix || 16== radix) && count >0)
{
unsigned i = 0;
while (' ' == c[i] || '\t' == c[i])++i;
if (i < count && (c[i] == '+' || c[i] == '-'))
{
if (c[i] == '-')
bSubMin = true;
++i;
}
if (i < count)
bSuc = true;
for (; i < count; ++i)
{
int nTmp = c[i] - '0';
if (nTmp > 10 && radix == 16)
nTmp = 10 + (c[i] > 'F' ? c[i] - 'a' : c[i] - 'A');
if (nTmp < 0 || nTmp >= int(radix))
{
if (' ' != c[i] && '\t' != c[i])
bSuc = false;
break;
}
nRet = nRet * radix + (bSubMin ? -nTmp : nTmp);
}
}
if (suc)
*suc = bSuc;
return bSuc ? nRet : 0;
}
list<string> VGDBManager::SplitString(const std::string &str, const std::string &sp, bool bSkipEmpty /*= true*/)
{
list<string> strLsRet;
int nSizeSp = sp.size();
if (!nSizeSp)
return strLsRet;
unsigned nPos = 0;
while (nPos < str.size())
{
int nTmp = str.find(sp, nPos);
string strTmp = str.substr(nPos, nTmp < 0 ? -1 : nTmp - nPos);
if (strTmp.size() || !bSkipEmpty)
strLsRet.push_back(strTmp);
if (nTmp < int(nPos))
break;
nPos = nTmp + nSizeSp;
}
return strLsRet;
}
string VGDBManager::Load(const TiXmlDocument &doc, StringList &tbs)
{
if (const TiXmlElement *rootElement = doc.RootElement())
{
const TiXmlNode *node = rootElement->FirstChild("Database");
if (!node)
return string();
tbs = parseTables(rootElement->FirstChild("Tables"));
parseSqls(rootElement->FirstChild("SQLs"));
parseTriggers(rootElement->FirstChild("Triggers"));
return parseDatabase(node->ToElement());
}
return string();
}
VGTable *VGDBManager::GetTableByName(const std::string &name) const
{
if (name.length() < 1)
return NULL;
for (VGTable *itr : m_tables)
{
if (itr->GetName() == name)
return itr;
}
return NULL;
}
ExecutItem *VGDBManager::GetSqlByName(const std::string &name) const
{
if (name.empty())
return NULL;
for (ExecutItem *itr : m_sqls)
{
if (itr->GetName() == name)
return itr;
}
return NULL;
}
VGTrigger *VGDBManager::GetTriggerByName(const std::string &name) const
{
if (name.empty())
return NULL;
for (VGTrigger *itr : m_triggers)
{
if (itr->GetName() == name)
return itr;
}
return NULL;
}
const char *VGDBManager::GetMysqlHost(const std::string &db) const
{
if (MySqlStruct *dbs = _getDbStruct(db))
return dbs->m_host.c_str();
return sDefualtDb[0];
}
int VGDBManager::GetMysqlPort(const std::string &db/*=""*/) const
{
if (MySqlStruct *dbs = _getDbStruct(db))
return dbs->m_port;
return 3306;
}
const char *VGDBManager::GetMysqlUser(const std::string &db/*=""*/) const
{
if (MySqlStruct *dbs = _getDbStruct(db))
return dbs->m_user.c_str();
return sDefualtDb[2];
}
const char *VGDBManager::GetMysqlPswd(const std::string &db/*=""*/) const
{
if (MySqlStruct *dbs = _getDbStruct(db))
return dbs->m_pswd.c_str();
return sDefualtDb[3];
}
const char *VGDBManager::GetMysqlCharSet(const std::string &db) const
{
if (MySqlStruct *dbs = _getDbStruct(db))
return dbs->m_charSet.c_str();
return sDefualtDb[4];;
}
list<string> VGDBManager::GetDatabases() const
{
list<string> ret;
for (MySqlStruct *itr : m_mysqls)
{
if(itr)
ret.push_back(itr->m_database);
}
return ret;
}
StringList VGDBManager::GetTriggers() const
{
StringList ret;
for (VGTrigger *itr : m_triggers)
{
ret.push_back(itr->GetName());
}
return ret;
}
StringList VGDBManager::parseTables(const TiXmlNode *node)
{
StringList ret;
if (!node)
return ret;
const TiXmlNode *table = node ? node->FirstChild("table") : NULL;
while (table)
{
if (VGTable *tb = VGTable::ParseTable(*table->ToElement()))
{
m_tables.push_back(tb);
ret.push_back(tb->GetName());
}
table = table->NextSibling("table");
}
return ret;
}
void VGDBManager::parseSqls(const TiXmlNode *node)
{
const TiXmlNode *sql = node ? node->FirstChild("SQL") : NULL;
while (sql)
{
if (ExecutItem *item = ExecutItem::parse(sql->ToElement()))
{
if (item->GetName().length() > 0 && NULL == GetTableByName(item->GetName()))
m_sqls.push_back(item);
else
delete item;
}
sql = sql->NextSibling("SQL");
}
}
void VGDBManager::parseTriggers(const TiXmlNode *node)
{
const TiXmlNode *tgNode = node ? node->FirstChild("Trigger") : NULL;
while (tgNode)
{
if (VGTrigger *trg = VGTrigger::Parse(*tgNode->ToElement()))
m_triggers.push_back(trg);
tgNode = tgNode->NextSibling("Trigger");
}
}
string VGDBManager::parseDatabase(const TiXmlElement *e)
{
if (!e)
return string();
MySqlStruct sqlSrv;
const char *tmp = e->Attribute("user");
sqlSrv.m_user = tmp ? tmp : "root";
tmp = e->Attribute("pswd");
sqlSrv.m_pswd = tmp ? tmp : "root";
tmp = e->Attribute("host");
sqlSrv.m_host = tmp ? tmp : "127.0.0.1";
tmp = e->Attribute("port");
sqlSrv.m_port = tmp ? str2int(tmp):3306;
tmp = e->Attribute("database");
if (!tmp)
return string();
if (const char *tmpS = e->Attribute("charSet"))
sqlSrv.m_charSet = tmpS;
sqlSrv.m_database = tmp;
m_mysqls.push_back(new MySqlStruct(sqlSrv));
return sqlSrv.m_database;
}
MySqlStruct *VGDBManager::_getDbStruct(const std::string &db)const
{
for (MySqlStruct *itr : m_mysqls)
{
if (itr && (db.empty() || db == itr->m_database))
return itr;
}
return NULL;
}
MySql操作
头文件
#include <string>
#include <list>
#include <map>
typedef struct st_mysql MYSQL;
typedef struct st_mysql_stmt MYSQL_STMT;
typedef struct st_mysql_bind MYSQL_BIND;
typedef struct st_mysql_res MYSQL_RES;
class FiledValueItem;
class ExecutItem;
class VGTable;
class VGTrigger;
class VGMySql
{
public:
VGMySql();
VGMySql(const char *host, int port, const char *user, const char *pswd);
virtual ~VGMySql();
bool ConnectMySql(const char *host, int port, const char *user, const char *pswd);
bool IsValid() const;
bool Execut(ExecutItem *item);
ExecutItem *GetResult();
bool EnterDatabase(const std::string &db, const char *cset=NULL);
bool ExistTable(const std::string &name);
bool CreateTable(VGTable *tb);
bool ExistTrigger(const std::string &name);
bool CreateTrigger(VGTrigger *trigger);
MYSQL_RES *Query(const std::string &sql);
protected:
bool _canOperaterDB();
bool _executChange(const std::string &sql, MYSQL_BIND *binds, FiledValueItem *i=NULL);
bool _changeItem(ExecutItem *item);
bool _selectItem(ExecutItem *item);
std::string _getTablesString(const ExecutItem &item);
MYSQL_STMT *_prepareMySql(const std::string &fmt, MYSQL_BIND *binds, int nRead=0);
void _checkPrepare();
private:
MYSQL *m_mysql;
std::string m_host;
int m_nPort;
std::string m_user;
std::string m_pswd;
bool m_bValid;
ExecutItem *m_execItem;
MYSQL_BIND *m_binds;
MYSQL_STMT *m_stmt;
};
MySql操作实现
#include "VGMysql.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#if !defined _WIN32 && !defined _WIN64
#include <mysql/mysql.h>
#else
#include <mysql.h>
#endif
#include "DBExecItem.h"
#include "VGDBManager.h"
#include "VGTrigger.h"
using namespace std;
VGMySql::VGMySql() : m_bValid(false)
, m_binds(NULL) , m_stmt(NULL)
{
m_mysql = mysql_init(NULL);
if(!m_mysql)
return;
}
VGMySql::VGMySql( const char *host, int port, const char *user, const char *pswd)
: m_bValid(false), m_binds(NULL), m_stmt(NULL)
{
m_mysql = mysql_init(NULL);
if(!m_mysql)
return;
ConnectMySql(host, port, user, pswd);
}
bool VGMySql::IsValid() const
{
return m_mysql!=NULL && m_bValid;
}
VGMySql::~VGMySql()
{
_checkPrepare();
if (m_mysql)
mysql_close(m_mysql);
}
bool VGMySql::Execut(ExecutItem *item)
{
if (!item->IsValid())
return false;
switch (item->GetType())
{
case ExecutItem::Insert:
case ExecutItem::Delete:
case ExecutItem::Update:
return _changeItem(item);
case ExecutItem::Select:
return _selectItem(item);
default:
break;
}
return false;
}
ExecutItem *VGMySql::GetResult()
{
if (!m_stmt || !m_execItem || !m_binds || mysql_stmt_fetch(m_stmt))
_checkPrepare();
return m_execItem;
}
bool VGMySql::EnterDatabase(const std::string &db, const char *cset)
{
bool ret = false;
if (db.empty())
return ret;
char tmp[128];
const char *set = cset ? cset : "utf8";
sprintf(tmp, "default character set %s collate %s_general_ci", set, set);
string sql = string("create database if not exists ") + db + " " + tmp;
if (MYSQL_RES *res = Query(sql))
{
my_ulonglong nNum = mysql_num_rows(res);
mysql_free_result(res);
ret = nNum > 0;
}
if (MYSQL_RES *res = Query(string("use ") + db))
mysql_free_result(res);
return ret;
}
bool VGMySql::ExistTable(const std::string &name)
{
if (!_canOperaterDB() || name.empty())
return false;
string sql;
sql.resize(name.length()+20);
sprintf(&sql.at(0), "show tables like \'%s\'", name.c_str());
if (MYSQL_RES *res = Query(sql.c_str()))
{
my_ulonglong nNum = mysql_num_rows(res);
mysql_free_result(res);
return nNum > 0;
}
return false;
}
bool VGMySql::CreateTable(VGTable *tb)
{
if (!tb || !_canOperaterDB())
return false;
if (MYSQL_RES *res = Query(tb->ToCreateSQL()))
mysql_free_result(res);
return ExistTable(tb->GetName());
}
bool VGMySql::ExistTrigger(const std::string &name)
{
if (!_canOperaterDB() || name.empty())
return false;
static string sTrigExFmt = "SELECT TRIGGER_NAME FROM information_schema.triggers ";
string sql= sTrigExFmt+"where TRIGGER_NAME='" + name+"'";
if (MYSQL_RES *res = Query(sql))
{
my_ulonglong nNum = mysql_num_rows(res);
mysql_free_result(res);
return nNum > 0;
}
return false;
}
bool VGMySql::CreateTrigger(VGTrigger *trigger)
{
if (!trigger || ExistTrigger(trigger->GetName()))
return false;
if (MYSQL_RES *res = Query(trigger->ToSqlString()))
{
mysql_free_result(res);
return true;
}
return false;
}
bool VGMySql::_canOperaterDB()
{
if (m_bValid && mysql_ping(m_mysql))
{
if (mysql_real_connect(m_mysql, m_host.c_str(), m_user.c_str(), m_pswd.c_str(), NULL, m_nPort, NULL, 0))
return false;
}
return m_bValid;
}
bool VGMySql::ConnectMySql( const char *host, int port, const char *user, const char *pswd)
{
if (!m_mysql)
return m_bValid = false;
if (mysql_real_connect(m_mysql, host, user, pswd, NULL, port, NULL, 0))
{
m_host = host ? host : string();
m_nPort = port;
m_user = user ? user : string();
m_pswd = pswd ? pswd : string();
return m_bValid = true;
}
fprintf(stderr, " mysql_real_connect() failed %s\n", mysql_error(m_mysql));
string strErr = mysql_error(m_mysql);
return m_bValid = false;
}
bool VGMySql::_executChange(const string &sql, MYSQL_BIND *binds, FiledValueItem *i)
{
MYSQL_STMT *stmt = _prepareMySql(sql, binds);
if (!stmt)
return false;
bool ret = true;
if (mysql_stmt_affected_rows(stmt) < 1)
ret = false;
if(i)
{
uint64_t n = mysql_insert_id(m_mysql);
i->InitBuff(sizeof(n), &n);
}
mysql_stmt_close(stmt);
return ret;
}
bool VGMySql::_changeItem(ExecutItem *item)
{
if (!item || !item->IsValid())
return false;
if (!_canOperaterDB())
return false;
MYSQL_BIND *binds = item->GetParamBinds();
string sql = item->GetSqlString(binds);
bool ret = false;
if (sql.length() > 0)
{
if (item->HasForeignRefTable())
{
if (MYSQL_RES *res = Query("SET FOREIGN_KEY_CHECKS=0"))
mysql_free_result(res);
}
ret = _executChange(sql, binds, item->GetIncrement());
if (item->HasForeignRefTable())
{
if (MYSQL_RES *res = Query("SET FOREIGN_KEY_CHECKS=1"))
mysql_free_result(res);
}
}
delete binds;
return ret;
}
bool VGMySql::_selectItem(ExecutItem *item)
{
_checkPrepare();
int count = item ? item->CountRead() : 0;
if (count < 1 || !item->IsValid() || item->GetType() != ExecutItem::Select)
return false;
if (!_canOperaterDB())
return false;
MYSQL_BIND *params = item->GetParamBinds();
string sql = item->GetSqlString(params);
MYSQL_STMT *stmt = _prepareMySql(sql, params);
if (!stmt)
{
delete params;
return false;
}
MYSQL_BIND *binds = item->TransformRead();
bool ret = false;
if (mysql_stmt_bind_result(stmt, binds))
fprintf(stderr, "mysql_stmt_bind_result() failed! %s\n", mysql_stmt_error(stmt));
else if (mysql_stmt_store_result(stmt))
fprintf(stderr, " mysql_stmt_store_result() failed %s\n", mysql_stmt_error(stmt));
else if (mysql_stmt_fetch(stmt))
fprintf(stderr, " mysql_stmt_fetch() failed %s\n", mysql_stmt_error(stmt));
else
ret = true;
delete params;
if (!ret)
{
delete binds;
if(stmt)
{
mysql_stmt_free_result(stmt);
mysql_stmt_close(stmt);
}
return false;
}
m_binds = binds;
m_stmt = stmt;
m_execItem = item;
return true;
}
MYSQL_STMT *VGMySql::_prepareMySql(const string &strFormat, MYSQL_BIND *binds, int nRead)
{
MYSQL_STMT *stmt = mysql_stmt_init(m_mysql);
if (!stmt)
{
fprintf(stderr, " mysql_stmt_init(), out of memory\n");
return NULL;
}
bool ret = false;
if (mysql_stmt_prepare(stmt, strFormat.c_str(), strFormat.length()))
fprintf(stderr, "mysql_stmt_prepare() failed! %s\n", mysql_stmt_error(stmt));
else if (binds && mysql_stmt_bind_param(stmt, binds))
fprintf(stderr, "mysql_stmt_bind_param() failed\n");
else
ret = true;
if (ret && nRead>0)
{
MYSQL_RES *preRes = mysql_stmt_result_metadata(stmt);
if (!preRes || nRead != (int)mysql_num_fields(preRes))
ret = false;
}
if (ret && mysql_stmt_execute(stmt))
{
fprintf(stderr, "mysql_stmt_execute(), %s failed\n", mysql_error(m_mysql));
ret = false;
}
if (!ret)
{
mysql_stmt_close(stmt);
stmt = NULL;
}
return stmt;
}
void VGMySql::_checkPrepare()
{
if (m_binds)
{
delete m_binds;
m_binds = NULL;
}
if (m_stmt)
{
mysql_stmt_free_result(m_stmt);
mysql_stmt_close(m_stmt);
m_stmt = NULL;
}
m_execItem = NULL;
}
std::string VGMySql::_getTablesString(const ExecutItem &item)
{
string ret;
for (const string &table : item.ExecutTables())
{
ret += " " + table;
}
return ret;
}
MYSQL_RES *VGMySql::Query(const std::string &sql)
{
if (mysql_real_query(m_mysql, sql.c_str(), sql.length()))
{
fprintf(stderr, "mysql_real_query() failed! %s\n", mysql_error(m_mysql));
return NULL;
}
return mysql_store_result(m_mysql);
}