C++下,MySql预处理封装
为什么要使用预处理
1、即时 SQL
一条 SQL 在 DB 接收到最终执行完毕返回,大致的过程如下:
1. 词法和语义解析;
2. 优化 SQL 语句,制定执行计划;
3. 执行并返回结果;
如上,一条 SQL 直接是走流程处理,一次编译,单次运行,此类普通语句被称作Immediate Statements (即时 SQL)。2、预处理 SQL
但是,绝大多数情况下,某需求某一条 SQL 语句可能会被反复调用执行,或>者每次执行的时候只有个别的值不同(比如 select 的 where 子句值不同,update 的 set 子句值不同,insert 的 values 值不同)。如果每次都需要经过上面的词法语义解析、语句优化、制定执行计划等,则效率就明显不行了。
所谓预编译语句就是将此类 SQL 语句中的值用占位符替代,可以视为将 SQL 语句模板化或者说参数化,一般称这类语句叫Prepared Statements。
预编译语句的优势在于归纳为:一次编译、多次运行,省去了解析优化等过程;此外预编译语句能防止 SQL 注入。
SqlStmt类
上面那段抄的,主要看下面这里。
鉴于预编译使用的繁琐,我将将其做了封装,由于是我个人项目用的,封装具有局限性,使用此类需要注意以下几点:
1. 此类 get 数据 仅返回一条,需要做返回全部数据的请自行修改。
2. 带入和返回的参数只能是 std::string 类型,需要其他类型请自行封装,注意写入参数时,传入的是地址,例如 int 类型用 &int。
3. results 中返回的字符串类型需要用 .c_str() 方法 才能获取正确的结果。
4. 我这个类封装的没有实现效率提升,需要的话可以把语句做成常量,然后在初始化的时候上传到服务器,然后保存句柄,用到时候直接调用就行。
// SqlStmt.h
#pragma once
#include <iostream>
#include <mysql.h>
class SqlStmt
{
private:
MYSQL_STMT* stmt;
public:
int connect(MYSQL& mysql, std::string host, std::string user, std::string passwd, std::string db, unsigned int port, std::string unix_socket, unsigned long clientflag);
int close(MYSQL& conn);
/*
* 插入set的setData数据进table表
*/
bool insert_table_info(MYSQL& conn, std::string table,
std::string set, std::string setData);
/*
* 插入set1的setData1和set2的setData2数据进table表
*/
bool insert_table_info(MYSQL& conn, std::string table,
std::string set1, std::string setData1,
std::string set2, std::string setData2);
/*
* 获取table表来自where的whereData数据对应的from的数据
*/
std::string get_table_info(MYSQL& conn, std::string table,
std::string from,
std::string where, std::string whereData);
/*
* 设置table表来自where的whereData数据对应的set数据为setData
*/
bool set_table_info(MYSQL& conn, std::string table,
std::string set, std::string setData,
std::string where, std::string whereData);
/*
* 删除table表来自where的whereData数据对应的记录
*/
bool delete_table_info(MYSQL& conn, std::string table,
std::string where, std::string whereData);
};
// SqlStmt.cpp
#include "SqlStmt.h"
int SqlStmt::connect(MYSQL& conn, std::string host, std::string user, std::string passwd, std::string db, unsigned int port, std::string unix_socket = NULL, unsigned long clientflag = 0)
{
// 初始化句柄
mysql_init(&conn);
// 连接的数据库(句柄、主机名、用户名、密码、数据库名、端口号、socket指针、标记)
if (!mysql_real_connect(&conn, host.c_str(), user.c_str(), passwd.c_str(), db.c_str(), port, nullptr, 0))
{
return -1;
}
else {
return 0;
}
}
int SqlStmt::close(MYSQL& conn)
{
if (!mysql_ping(&conn)) // 连接存活
{
mysql_close(&conn);
return 0;
}
else {
return -1;
}
}
bool SqlStmt::insert_table_info(MYSQL& conn, std::string table, std::string set, std::string setData)
{
mysql_query(&conn, "SET NAMES GB2312"); // 适配中文
bool flag = false;
stmt = mysql_stmt_init(&conn); //创建MYSQL_STMT句柄
if (!stmt) {
return false;
}
// 构建语句
std::string query = "INSERT INTO " + table + " (" + set + ") VALUES (?);";
// 发送到服务器
if (mysql_stmt_prepare(stmt, query.c_str(), query.size()))
{
return false;
}
MYSQL_BIND params[1]; // 参数
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)setData.c_str();
params[0].buffer_length = setData.size();
// 绑定 获取结果的绑定见get_table_info()方法
mysql_stmt_bind_param(stmt, params);
if (!mysql_stmt_execute(stmt)) {
flag = true;
}
else {
flag = false;
}
mysql_stmt_close(stmt);
return flag;
}
bool SqlStmt::insert_table_info(MYSQL& conn, std::string table, std::string set1, std::string setData1, std::string set2, std::string setData2)
{
mysql_query(&conn, "SET NAMES GB2312");
bool flag = false;
stmt = mysql_stmt_init(&conn);
if (!stmt) {
return false;
}
std::string query = "INSERT INTO " + table + " (" + set1 + "," + set2 + ") VALUES (?,?);";
if (mysql_stmt_prepare(stmt, query.c_str(), query.size()))
{
return false;
}
MYSQL_BIND params[2];
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)setData1.c_str();
params[0].buffer_length = setData1.size();
params[1].buffer_type = MYSQL_TYPE_STRING;
params[1].buffer = (char*)setData2.c_str();
params[1].buffer_length = setData2.size();
mysql_stmt_bind_param(stmt, params);
if (!mysql_stmt_execute(stmt)) {
flag = true;
}
else {
flag = false;
}
mysql_stmt_close(stmt);
return flag;
}
std::string SqlStmt::get_table_info(MYSQL& conn, std::string table, std::string from, std::string where, std::string whereData)
{
mysql_query(&conn, "SET NAMES GB2312");
std::string result = "";
stmt = mysql_stmt_init(&conn);
if (!stmt) {
return NULL;
}
std::string query = "SELECT " + from + " FROM " + table + " WHERE " + where + " = ?;";
if (mysql_stmt_prepare(stmt, query.c_str(), query.size()))
{
return NULL;
}
MYSQL_BIND params[1];
MYSQL_BIND results[1]; // 结果
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)whereData.c_str();
params[0].buffer_length = whereData.size();
memset(results, 0, sizeof(results));
results[0].buffer_type = MYSQL_TYPE_STRING;
results[0].buffer = (char*)result.c_str();
results[0].buffer_length = MYSQL_TYPE_STRING;
mysql_stmt_bind_param(stmt, params);
mysql_stmt_bind_result(stmt, results);
mysql_stmt_execute(stmt);
mysql_stmt_store_result(stmt);
if (mysql_stmt_fetch(stmt) != 0)
return NULL;
mysql_stmt_close(stmt);
return result.c_str();
}
bool SqlStmt::set_table_info(MYSQL& conn, std::string table, std::string set, std::string setData, std::string where, std::string whereData)
{
mysql_query(&conn, "SET NAMES GB2312");
bool flag = false;
stmt = mysql_stmt_init(&conn);
if (!stmt) {
return false;
}
std::string query = "UPDATE " + table + " SET " + set + " = ? WHERE " + where + " = ?;";
if (mysql_stmt_prepare(stmt, query.c_str(), query.size()))
{
return false;
}
MYSQL_BIND params[2];
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)setData.c_str();
params[0].buffer_length = setData.size();
params[1].buffer_type = MYSQL_TYPE_STRING;
params[1].buffer = (char*)whereData.c_str();
params[1].buffer_length = whereData.size();
mysql_stmt_bind_param(stmt, params);
if (!mysql_stmt_execute(stmt)) {
flag = true;
}
else {
flag = false;
}
mysql_stmt_close(stmt);
return flag;
}
bool SqlStmt::delete_table_info(MYSQL& conn, std::string table, std::string where, std::string whereData)
{
mysql_query(&conn, "SET NAMES GB2312");
bool flag = false;
stmt = mysql_stmt_init(&conn);
if (!stmt) {
return false;
}
std::string query = "DELETE FROM " + table + " WHERE " + where + " = ?;";
if (mysql_stmt_prepare(stmt, query.c_str(), query.size()))
{
return flag;
}
MYSQL_BIND params[1];
memset(params, 0, sizeof(params));
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = (char*)whereData.c_str();
params[0].buffer_length = whereData.size();
mysql_stmt_bind_param(stmt, params);
mysql_stmt_execute(stmt);
if (mysql_stmt_store_result(stmt) == 0) {
flag = true;
}
else {
flag = false;
}
mysql_stmt_close(stmt);
return flag;
}
// 示例
#include "SqlStmt.h"
#include "mysql.h"
int main(void) {
SqlStmt mysqlstmt;
MYSQL conn;
// 连接数据库
mysqlstmt.connect(conn, "localhost", "root", "123456", "db", 3306);
// 插入,王二狗诞生
mysqlstmt.insert_table_info(conn, "user_info", "name", "王二狗", "sex", "男");
// 更新,王二狗变性后,重新更新他的性别。
mysqlstmt.set_table_info(conn, "user_info", "sex", "女", "name", "王二狗");
// 获取,得到王二狗的性别
std::string sex = mysqlstmt.get_table_info(conn, "user_info", "sex", "name", "王二狗");
// 删除,王二狗没了
mysqlstmt.delete_table_info(conn, "user_info", "name", "王二狗");
// 关闭数据库
mysqlstmt.close(conn);
return 0;
}