第一部分:init初始化MySQL连接的相关API:
(1) mysql_init :
MYSQL* mysql_init(MYSQL *mysql);
返回一个MYSQL 句柄,对应这个句柄MySQL会在数据库内部分配一个对象并开辟内存,用于存储关联这个连接的处理信息。
后续所有的对这条数据库连接的操作都是通过这个句柄,类似于socket()返回的sockfd。
如果MySQL内部内存不足,则返回NULL。
(2) mysql_options :
int mysql_options(MYSQL* mysql, enum mysql_option option, const void* arg);
设置额外的连接选项,用于控制连接行为。
在 mysql_init
之后、mysql_connect
之前 进行调用。
option
参数用于指定要设置的选项类型;
arg
参数用于设置此选项的值。
常用的选项有:
MYSQL_OPT_RECONNECT
:当发现连接断开时,自动进行重连(例如超8小时无活动,连接被MySQL server断开后,再次进行访问时发现连接已断开,会自动进行重连)
MYSQL_SET_CHARSET_NAME
:设置默认的charset
(3) mysql_real_connect :
MYSQL* mysql_real_connect( MYSQL* mysql,
const char* host, const char* user, const char* passwd,
const char* db, unsigned int port, const char* unix_socket, unsigned long client_flag);
用于向MySQL server发起连接,后续所有的对数据库的操作都需要先建立连接。
>> Example 1: Init()初始化部分使用举例:
int CDBConn::Init() {
m_mysql = mysql_init(NULL);
if(!m_mysql) {
return -1;
}
my_bool reconnect = true;
mysql_options(m_mysql, MYSQL_OPT_RECONNECT, &reconnect);
mysql_options(m_mysql, MYSQL_OPT_CHARSET_NAME, "utf8mb4");
if(mysql_real_connect(m_mysql, m_pDBPool->GetDBServerIP(), m_pDBPool->GetUsername(), m_pDBPool->GetPassword(),
m_pDBPool->GetDBName(), m_pDBPool->GetDBServerPort(), NULL, 0) ) {
return -1;
}
return 0;
}
第二部分:执行“select”查询功能的API:
(4) mysql_ping :
int mysql_ping(MYSQL* mysql);
检查与MySQL的连接是否正常。如果连接已经断开,且已设置了自动重连 MYSQL_OPT_RECONNECT
选项,则发起自动重连;
如果没有开启自动重连选项,则返回error。
(5) mysql_real_query :
int mysql_real_query(MYSQL* mysql, const char* stmt_str, unsigned long length);
执行参数 stmt_str
传入的查询语句,其中不包含 “;” SQL语句结束符。
(6) mysql_store_result :
MYSQL_RES* mysql_store_result(MYSQL* msyql);
在调用 mysql_real_query
之后,必须 调用 mysql_store_result
用于存放查询结果。
且在使用完毕后,必须 调用 mysql_free_result
去释放 mysql_store_result
分配的内存(用于保存查询结果的内存)。
(7) mysql_free_result :
void mysql_free_result(MYSQL_RES *result);
用于释放前面 mysql_store_result
分配的内存。
>> Example 2: Select查询部分使用举例:
CResultSet* CDBConn::ExcuteQuery(const char* sql_query) {
mysql_ping(m_mysql);
if(mysql_real_query(m_mysql, sql_query, strlen(sql_query))) {
printf("mysql_real_query failed: %s, sql: %s\n", mysql_error(m_mysql), sql_query);
return NULL;
}
MYSQL_RES* res = mysql_store_result(m_mysql);
if(!res) {
printf("mysql_store_result failed: %s\n", mysql_error(m_mysql));
return NULL;
}
//将查询到的结果MYSQL_RES封装成CResultSet对象返回给上级调用函数,待外层函数使用完毕后再调用 mysql_free_result 去释放这块内存
CResultSet* result_set = new CResultSet(res);
return result_set;
}
(8) mysql_num_fields :
unsigned int mysql_num_fields(MYSQL_RES* result);
返回查询结果集中的列的数目。
(9) mysql_fetch_fields :
MYSQL_FIELD* mysql_fetch_fields(MYSQL_RES* result);
返回查询结果集中所有 MYSQL_FIELD 结构的数组。数组中的每个结构体表示一个列的字段定义。“对一个列的属性描述”,包括列名、列数据类型等
(10) mysql_fetch_row :
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);
检索查询结果集(MYSQL_RES) 中的下一行。
在 mysql_store_result 之后使用时,如果没有要检索的行,或者检索出错,mysql_fetch_row()返回NULL;
行内值的数据由 mysql_num_fields() 给出(即列的数目),可通过 row[column_index] 访问这些指针(从 row[0] 到 row[mysql_num_fields()-1])
>> Example 3: 解析查询MySQL查询结果举例:
//将mysql_store_result返回的MYSQL_RES查询结构进行解析、分解后,存储在CResultSet对象中:
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);
private:
MYSQL_RES* m_res;
MYSQL_ROW m_row;
map<string, int> m_key_map;
};
第三部分:执行“增删改”(insert/delete/update)操作的API:
(11) mysql_affected_rows :
my_ulonglong mysql_affected_rows(MYSQL *mysql);
返回上次UPDATE更改的行数,上次DELETE删除的行数,或者上次INSERT语句插入的行数。
对于UPDATE、DELETE、INSERT语句,可以在 msyql_query() 后立刻调用;
对于SELECT语句,mysql_affected_rows() 的工作方式与mysql_num_rows类似。
>> Example 4: 执行Update更新操作举例:
//例如 char* sql_query = "update [table_name] set [change_value] where [select_option];"
bool CDBConn::ExcuteUpdate(const char* sql_query) {
mysql_ping(m_mysql);
if(mysql_real_query(m_mysql, sql_query, strlen(sql_query))) {
printf("mysql_real_query failed: %s\n", mysql_error(m_mysql));
return false;
}
//使用msyql_affected_rows检查UPDATE等语句是否正确生效:
if(mysql_affected_rows(m_mysql) > 0) {
return true;
}
else {
return false;
}
}
第四部分: PrepareStatement 防注入:
PrepareStatement主要是使用【预编译】防止SQL注入。
预编译语句就是将这类语句中的值用“占位符”(?) 替代,可以视为将sql语句“模板化”或者“参数化”。
一次编译,多次运行,省去了解析优化等过程。
预编译 的作用:
(1)预编译阶段可以优化sql的执行;
(2)防止SQL注入:
使用预编译,而其后注入的参数将不会再进行SQL编译,也就是说其后注入进来的参数将不会认为它是一条SQL语句,
而默认其是一个参数,参数中的 or 或者 and 等就不是SQL语句保留字了。
(12) mysql_stmt_init :
创建 MYSQL_STMT 句柄
MYSQL_STMT* mysql_stmt_init(MYSQL *mysql);
描述: 创建 MYSQL_STMT 句柄。对于该句柄,应该用 mysql_stmt_close(MYSQL_STMT*); 释放。
返回值:成功时返回指向MYSQL_STMT结构的指针;失败时返回NULL。
(13) mysql_stmt_prepare :
作用相当于是注册一条带有“?”参数标记符 的SQL语句,这里应该就是【预编译】的操作
int mysql_stmt_prepare(MYSQL_STMT* stmt, const char* query, unsigned long length);
入参:
给定mysql_stmt_init返回的MYSQL_STMT 句柄,以及待执行的SQL语句(字符串、字符串长度)。
通过将问号字符 “?” 嵌入到SQl字符串的恰当位置,应用程序可包含SQL语句中的一个或多个参数标记符。
标记符仅在SQL语句中的特定位置时才是合法的。
执行语句之前,必须使用 mysql_stmt_bind_param(),将“参数标记符”(?) 与 “应用程序变量” 绑定到一起。
(mysql_stmt_bind_param 相当于是一个传参的过程,?是形参,应用程序传入的变量是实参。)
使用举例:
mysql_stmt_prepare( m_stmt, sql.c_str(), sql.size() );
string sql = "insert into IMMessage_x (related, fromId, toId, msgId, content, status, type, created, updated) values (?,?,?,?,?,?,?,?,?)";
(14) mysql_stmt_param_count :
unsigned long mysql_stmt_param_count(MYSQL_STMT* stmt);
返回预处理语句中参数标记符的数目。
(15) mysql_stmt_bind_param :
my_bool mysql_stmt_bind_param(MYSQL_STMT* stmt, MYSQL_BIND* bind);
用于为SQL语句中的 “参数标记符”(?) 绑定数据(“实参”),以传递给 mysql_stmt_prepare中的?位置。
注入此函数需要使用 MYSQL_BIND* 为元素类型的数组进行入参,所以在调用函数之前需要先 new MYSQL_BIND[param_count] 构造一个数组。
(16) mysql_stmt_execute :
int mysql_stmt_excute(MYSQL_STMT* stmt);
执行与语句相关的预处理查询。
将当前绑定的参数标记符的值发送给服务器,服务器用新提供的数据替换标记符。
如果语句是insert/update/delete(增删改),通过调用 mysql_stmt_affected_rows() 确认更改/插入/删除的总行数;
如果语句是 select,则需要调用 msyql_stmt_fetch() 来获取数据。
(17) mysql_stmt_affected_rows :
my_ulonglong mysql_stmt_affected_rows(MYSQL_STMT* stmt);
返回上次执行语句更改、删除或插入的总行数。
(18) mysql_stmt_fetch :
同mysql_fetch_row / mysql_fetch_fields 的用法。
(19) mysql_stmt_insert_id :
my_ulonglong mysql_stmt_insert_id(MYSQL_STMT* stmt);
返回预处理“INSERT”或者“UPDATE” 语句为 AUTO_INCREMENT列生成的值。
在包含
也就是说,如果一个表中有某个列的属性是AUTO_INCREMENT自增的,那么当对这个表进行一次INSERT或者UPDATE后,
调用mysql_stmt_insert_id()这个函数就可以得到INSERT操作新增的行元素的自增一列的值。
>> Example 5: 使用预编译防止SQL注入举例:
待补充。。。。。。
MYSQL_STMT* m_stmt = mysql_stmt_init(MYSQL* m_mysql);
mysql_stmt_prepare(m_stmt, sql.c_str(), sql.size()); //sql="insert into ... values (???)"
mysql_stmt_param_count(m_stmt); //只是一个安全校验
MYSQL_BIND* m_param_bind = new MYSQL_BIND[m_param_cnt]; //new一块堆内存
m_param_bind[index].buffer_type = MYSQL_TYPE_STRING;
m_param_bind[index].buffer = (char*)value.c_str();
m_param_bind[index].buffer_length = value.size();
/*
m_param_bind[index].buffer_type = MYSQL_TYPE_LONG;
m_param_bind[index].buffer = &value;
*/
mysql_stmt_bind_param(m_stmt, m_param_bind);
mysql_stmt_excute(m_stmt);
mysql_stmt_affected_rows(); / mysql_stmt_fetch_fields();