class CResultSet
{
public:
CResultSet(MYSQL_RES *res);
virtual ~CResultSet();
bool Next();
int GetInt(const char *key);
char *GetString(const char *key);
private:
int _GetIndex(const char *key); //获取列名对应的索引
MYSQL_RES *m_res; // 该结构代表返回行的查询结果(SELECT, SHOW, DESCRIBE, EXPLAIN)
MYSQL_ROW m_row; // 这是1行数据的“类型安全”表示。它目前是按照计数字节字符串的数组实施的。
map<string, int> m_key_map;
};
功能是用于存放MySQL查询结果。
CResultSet::CResultSet(MYSQL_RES *res)
{
m_res = res;
// map table field key to index in the result array
int num_fields = mysql_num_fields(m_res); // 返回结果集中的列数。
MYSQL_FIELD *fields = mysql_fetch_fields(m_res); // 关于结果集所有列的MYSQL_FIELD结构的数组
for (int i = 0; i < num_fields; i++)
{
// 多列
m_key_map.insert(make_pair(fields[i].name, i)); // 每个结构提供了结果集中1列的字段定义
LOG_INFO << num_fields << " - [" << i << "] - " << fields[i].name;
}
}
这个构造函数是读取出一行的查询结果,然后将每一列存储到map中
CResultSet::~CResultSet()
{
if (m_res)
{
mysql_free_result(m_res);
m_res = NULL;
}
}
mysql_free_result 释放 mysql_store_result(), mysql_use_result(), mysql_list_dbs() 为结果集分配的内存*
bool CResultSet::Next()
{
m_row = mysql_fetch_row(m_res); // 检索结果集的下一行,行内值的数目由mysql_num_fields(result)给出
if (m_row)
{
return true;
}
else
{
return false;
}
}
class CPrepareStatement
{
public:
CPrepareStatement();
virtual ~CPrepareStatement();
bool Init(MYSQL *mysql, string &sql);
void SetParam(uint32_t index, int &value);
void SetParam(uint32_t index, uint32_t &value);
void SetParam(uint32_t index, string &value);
void SetParam(uint32_t index, const string &value);
bool ExecuteUpdate();
uint32_t GetInsertId();
private:
MYSQL_STMT *m_stmt;
MYSQL_BIND *m_param_bind;
uint32_t m_param_cnt;
};
CPrepareStatement::~CPrepareStatement()
{
if (m_stmt)
{
mysql_stmt_close(m_stmt);
m_stmt = NULL;
}
if (m_param_bind)
{
delete[] m_param_bind;
m_param_bind = NULL;
}
}
bool CPrepareStatement::Init(MYSQL *mysql, string &sql)
{
mysql_ping(mysql); // 当mysql连接丢失的时候,使用mysql_ping能够自动重连数据库
// g_master_conn_fail_num ++;
m_stmt = mysql_stmt_init(mysql);//创建MYSQL_STMT句柄
if (!m_stmt)
{
LOG_ERROR << "mysql_stmt_init failed";
return false;
}
if (mysql_stmt_prepare(m_stmt, sql.c_str(), sql.size()))
{
LOG_ERROR << "mysql_stmt_prepare failed: " << mysql_stmt_error(m_stmt);
return false;
}
m_param_cnt = mysql_stmt_param_count(m_stmt);
if (m_param_cnt > 0)
{
m_param_bind = new MYSQL_BIND[m_param_cnt];
if (!m_param_bind)
{
LOG_ERROR << "new failed";
return false;
}
memset(m_param_bind, 0, sizeof(MYSQL_BIND) * m_param_cnt);
}
return true;
}
执行MySQL 的 Update操作
bool CPrepareStatement::ExecuteUpdate()
{
if (!m_stmt)
{
LOG_ERROR << "no m_stmt";
return false;
}
if (mysql_stmt_bind_param(m_stmt, m_param_bind))
{
LOG_ERROR << "mysql_stmt_bind_param failed: " << mysql_stmt_error(m_stmt);
return false;
}
if (mysql_stmt_execute(m_stmt))
{
LOG_ERROR << "mysql_stmt_execute failed: " << mysql_stmt_error(m_stmt);
return false;
}
// 返回上次执行语句更改、删除或插入的总行数。对于UPDATE、DELETE或INSERT语句,可在mysql_stmt_execute()之后立刻调用它们。
if (mysql_stmt_affected_rows(m_stmt) == 0)
{
LOG_ERROR << "ExecuteUpdate have no effect";
return false;
}
return true;
}
class CDBConn
{
public:
CDBConn(CDBPool *pDBPool);
virtual ~CDBConn();
int Init();
// 创建表
bool ExecuteCreate(const char *sql_query);
// 删除表
bool ExecuteDrop(const char *sql_query);
// 查询
CResultSet *ExecuteQuery(const char *sql_query);
bool ExecutePassQuery(const char *sql_query);
/**
* 执行DB更新,修改
*
* @param sql_query sql
* @param care_affected_rows 是否在意影响的行数,false:不在意;true:在意
*
* @return 成功返回true 失败返回false
*/
bool ExecuteUpdate(const char *sql_query, bool care_affected_rows = true);
uint32_t GetInsertId();
// 开启事务
bool StartTransaction();
// 提交事务
bool Commit();
// 回滚事务
bool Rollback();
// 获取连接池名
const char *GetPoolName();
MYSQL *GetMysql() { return m_mysql; }
int GetRowNum() { return row_num; }
private:
int row_num = 0;
CDBPool *m_pDBPool; // to get MySQL server information
MYSQL *m_mysql; // 对应一个连接
char m_escape_string[MAX_ESCAPE_STRING_LEN + 1];
};
class CDBPool
{ // 只是负责管理连接CDBConn,真正干活的是CDBConn
public:
CDBPool() {} // 如果在构造函数做一些可能失败的操作,需要抛出异常,外部要捕获异常
CDBPool(const char *pool_name, const char *db_server_ip, uint16_t db_server_port,
const char *username, const char *password, const char *db_name,
int max_conn_cnt);
virtual ~CDBPool();
int Init(); // 连接数据库,创建连接
CDBConn *GetDBConn(const int timeout_ms = 0); // 获取连接资源
void RelDBConn(CDBConn *pConn); // 归还连接资源
const char *GetPoolName() { return m_pool_name.c_str(); }
const char *GetDBServerIP() { return m_db_server_ip.c_str(); }
uint16_t GetDBServerPort() { return m_db_server_port; }
const char *GetUsername() { return m_username.c_str(); }
const char *GetPasswrod() { return m_password.c_str(); }
const char *GetDBName() { return m_db_name.c_str(); }
private:
string m_pool_name; // 连接池名称
string m_db_server_ip; // 数据库ip
uint16_t m_db_server_port; // 数据库端口
string m_username; // 用户名
string m_password; // 用户密码
string m_db_name; // db名称
int m_db_cur_conn_cnt; // 当前启用的连接数量
int m_db_max_conn_cnt; // 最大连接数量
list<CDBConn *> m_free_list; // 空闲的连接
list<CDBConn *> m_used_list; // 记录已经被请求的连接
std::mutex m_mutex;
std::condition_variable m_cond_var;
bool m_abort_request = false;
};
CDBConn *CDBPool::GetDBConn(const int timeout_ms)
{
std::unique_lock<std::mutex> lock(m_mutex);
if (m_abort_request)
{
LOG_WARN << "have aboort";
return NULL;
}
if (m_free_list.empty()) // 2 当没有连接可以用时
{
// 第一步先检测 当前连接数量是否达到最大的连接数量
if (m_db_cur_conn_cnt >= m_db_max_conn_cnt) // 等待的逻辑
{
// 如果已经到达了,看看是否需要超时等待
if (timeout_ms <= 0) // 死等,直到有连接可以用 或者 连接池要退出
{
// lambda为true才会退出 while
m_cond_var.wait(lock, [this]
{
// 当前连接数量小于最大连接数量 或者请求释放连接池时退出
return (!m_free_list.empty()) | m_abort_request;
});
}
else
{
// return如果返回 false,继续wait(或者超时), 如果返回true退出 wait
// 1.m_free_list不为空
// 2.超时退出
// 3. m_abort_request被置为true,要释放整个连接池
m_cond_var.wait_for(lock, std::chrono::milliseconds(timeout_ms), [this]
{
return (!m_free_list.empty()) | m_abort_request;
});
// 带超时功能时还要判断是否为空
if (m_free_list.empty()) // 如果连接池还是没有空闲则退出
{
return NULL;
}
}
if (m_abort_request)
{
LOG_WARN << "have abort";
return NULL;
}
}
else // 还没有到最大连接则创建连接
{
CDBConn *pDBConn = new CDBConn(this); //新建连接
int ret = pDBConn->Init();
if (ret)
{
LOG_ERROR << "Init DBConnecton failed";
delete pDBConn;
return NULL;
}
else
{
m_free_list.push_back(pDBConn);
m_db_cur_conn_cnt++;
// log_info("new db connection: %s, conn_cnt: %d\n", m_pool_name.c_str(), m_db_cur_conn_cnt);
}
}
}
CDBConn *pConn = m_free_list.front(); // 获取连接
m_free_list.pop_front(); // STL 吐出连接,从空闲队列删除
return pConn;
}
void CDBPool::RelDBConn(CDBConn *pConn)
{
std::lock_guard<std::mutex> lock(m_mutex);
list<CDBConn *>::iterator it = m_free_list.begin();
for (; it != m_free_list.end(); it++) // 避免重复归还
{
if (*it == pConn)
{
break;
}
}
if (it == m_free_list.end()) // 不在m_free_list中就表示要归还
{
// m_used_list.remove(pConn);
m_free_list.push_back(pConn); //
m_cond_var.notify_one(); // 通知取队列
}
else
{
LOG_WARN << "RelDBConn failed";// 不再次回收连接
}
}
class CDBManager
{
public:
virtual ~CDBManager();
static CDBManager *getInstance(); // mark1
int Init();
CDBConn *GetDBConn(const char *dbpool_name);
void RelDBConn(CDBConn *pConn);
private:
CDBManager();
private:
static CDBManager *s_db_manager; // 对应mark1
map<string, CDBPool *> m_dbpool_map;
};
有一个主数据连接池和一个从数据连接池
int CDBManager::Init()
{
CConfigFileReader config_file("tc_http_server.conf");
char *db_instances = config_file.GetConfigName("DBInstances");
if (!db_instances)
{
LOG_ERROR << "not configure DBInstances";
return 1;
}
char host[64];
char port[64];
char dbname[64];
char username[64];
char password[64];
char maxconncnt[64];
CStrExplode instances_name(db_instances, ',');
for (uint32_t i = 0; i < instances_name.GetItemCnt(); i++)
{
char *pool_name = instances_name.GetItem(i);
snprintf(host, 64, "%s_host", pool_name);
snprintf(port, 64, "%s_port", pool_name);
snprintf(dbname, 64, "%s_dbname", pool_name);
snprintf(username, 64, "%s_username", pool_name);
snprintf(password, 64, "%s_password", pool_name);
snprintf(maxconncnt, 64, "%s_maxconncnt", pool_name);
char *db_host = config_file.GetConfigName(host);
char *str_db_port = config_file.GetConfigName(port);
char *db_dbname = config_file.GetConfigName(dbname);
char *db_username = config_file.GetConfigName(username);
char *db_password = config_file.GetConfigName(password);
char *str_maxconncnt = config_file.GetConfigName(maxconncnt);
LOG_INFO << "db_host:" << db_host << ", db_port:" << str_db_port << ", db_dbname:" << db_dbname
<< ", db_username:" << db_username << ", db_password:" << db_password;
if (!db_host || !str_db_port || !db_dbname || !db_username || !db_password || !str_maxconncnt)
{
LOG_FATAL << "not configure db instance: " << pool_name;
return 2;
}
int db_port = atoi(str_db_port);
int db_maxconncnt = atoi(str_maxconncnt);
CDBPool *pDBPool = new CDBPool(pool_name, db_host, db_port, db_username, db_password, db_dbname, db_maxconncnt);
if (pDBPool->Init())
{
LOG_ERROR << "init db instance failed: " << pool_name;
return 3;
}
m_dbpool_map.insert(make_pair(pool_name, pDBPool));
}
return 0;
}
CDBConn *CDBManager::GetDBConn(const char *dbpool_name)
{
// 先找到对应连接池,在获取其中的一条连接
map<string, CDBPool *>::iterator it = m_dbpool_map.find(dbpool_name); // 主从
if (it == m_dbpool_map.end())
{
return NULL;
}
else
{
return it->second->GetDBConn();
}
}
class AutoRelDBCon
{
public:
AutoRelDBCon(CDBManager *manger, CDBConn *conn) : manger_(manger), conn_(conn) {}
~AutoRelDBCon() // 对象声明周期时自动调用该析构函数
{
if (manger_)
{
manger_->RelDBConn(conn_);
}
} //在析构函数规划
private:
CDBManager *manger_ = NULL;
CDBConn *conn_ = NULL;
};
// 创建数据库连接对象,使得对象销毁时能够自动析构
#define AUTO_REAL_DBCONN(m, c) AutoRelDBCon autoreldbconn(m, c)