CHAPTER 17
DBConnector 函数手册
依据源码:include/DBConnector.h、src/DBConnector.cpp。
0. 全局静态成员与总体职责
静态成员
DBConnector* DBConnector::instance_ = nullptr;std::mutex DBConnector::mutex_;
模块职责
- 管理 MySQL 连接句柄
MYSQL* db_ - 提供查询与执行接口
- 提供单例初始化/获取/销毁
- 提供最后错误信息
last_error_ - 提供 SQL 字面量转义
escapeSqlLiteral
1. 构造与析构
1.1 DBConnector::DBConnector(std::string host, std::string user, std::string passwd, std::string dbname, unsigned int port = 3306)(私有)
- 位置:
src/DBConnector.cpp - 作用:保存连接参数并初始化 MySQL 句柄。
执行步骤
- 成员初始化:
db_ = nullptrhost_/user_/passwd_/dbname_/port_通过std::move接管
db_ = mysql_init(nullptr)- 若
db_ == nullptr:last_error_ = "MySQL句柄初始化失败"- 抛
std::runtime_error(last_error_)
关键点
- 构造阶段只初始化句柄,不建立 TCP 连接。
- 单例未初始化前不能外部直接构造(构造函数私有)。
1.2 DBConnector::~DBConnector()
- 位置:
src/DBConnector.cpp - 作用:关闭连接句柄并清理错误状态。
执行步骤
if (db_ != nullptr) mysql_close(db_);db_ = nullptrlast_error_.clear()
2. 内部工具函数
2.1 static void DBConnector::freeResult(MYSQL_RES* res)(私有)
- 位置:
src/DBConnector.cpp - 作用:安全释放结果集。
逻辑
if (res != nullptr) mysql_free_result(res);
3. 连接与执行接口
3.1 bool DBConnector::connect()
- 作用:根据构造保存的参数建立 MySQL 连接。
执行步骤
- 若
db_ == nullptr:last_error_ = "MySQL句柄未初始化"- 返回
false
mysql_options(db_, MYSQL_SET_CHARSET_NAME, "utf8mb4")- 调用
mysql_real_connect(...) - 若失败:
last_error_ = "连接失败: " + mysql_error(db_)- 返回
false
- 成功:
last_error_.clear()- 返回
true
面试点
- 这里的
connect可重复调用,但未显式处理“已连接再连接”的状态迁移。 - 字符集固定
utf8mb4,这是正确默认选择。
3.2 std::vector<std::map<std::string, std::string>> DBConnector::query(const std::string& sql)
- 作用:执行 SELECT 类查询并返回二维字符串映射结果。
返回结构
- 外层
vector:每一行 - 内层
map:字段名 -> 字符串值
执行步骤
result初始化为空。- 若
db_ == nullptr:last_error_ = "未连接数据库"- 返回空
result
mysql_query(db_, sql.c_str())- 失败:
last_error_ = "SQL执行失败: " + mysql_error(db_),返回空
- 失败:
MYSQL_RES* res = mysql_store_result(db_)- 若
nullptr:last_error_ = "获取结果集失败: " + mysql_error(db_),返回空
- 若
- 获取字段元信息:
int field_count = mysql_num_fields(res);MYSQL_FIELD* fields = mysql_fetch_fields(res);- 若
fields == nullptr || field_count <= 0:freeResult(res)last_error_ = "无字段信息"- 返回空
- 行遍历:
while ((row = mysql_fetch_row(res)) != nullptr)- 对每列:
field_name = fields[i].namefield_value = row[i] ? row[i] : ""(NULL 转空串)
result.push_back(row_map)
freeResult(res),last_error_.clear(),返回result
边界与语义
- 返回空
vector并不一定是“查询无结果”,也可能是“执行失败”。 - 调用方应配合
getError()判断。
3.3 int DBConnector::execute(const std::string& sql)
- 作用:执行非查询 SQL(INSERT/UPDATE/DELETE)。
执行步骤
- 若
db_ == nullptr:last_error_ = "未连接数据库"- 返回
-1
mysql_query(db_, sql.c_str())- 失败:
last_error_ = "SQL执行失败: " + mysql_error(db_),返回-1
- 失败:
affected_rows = mysql_affected_rows(db_)last_error_.clear(),返回affected_rows
语义
- 成功返回影响行数(可能为 0)
- 失败返回 -1
3.4 std::string DBConnector::getError()
- 作用:返回
last_error_的当前值(按值返回副本)。 - 注意:该函数本身未加锁。
4. 单例生命周期管理
4.1 static void DBConnector::initInstance(...)
- 作用:初始化单例(若尚未创建)。
执行步骤
std::lock_guard<std::mutex> lock(mutex_)- 若
instance_ == nullptr:instance_ = new DBConnector(...)- 若构造抛异常:打印
cerr,保持instance_ = nullptr
- 若
instance_ != nullptr:静默忽略(不重置)
风险点
- 重复 init 时参数不会更新,可能与调用者预期不一致。
4.2 static DBConnector* DBConnector::getInstance()
- 作用:获取单例指针。
执行步骤
- 加锁
mutex_ - 若
instance_ == nullptr:- 抛
std::runtime_error("单例未初始化,请先调用initInstance")
- 抛
- 返回
instance_
4.3 static void DBConnector::destroyInstance()
- 作用:销毁单例。
执行步骤
- 加锁
mutex_ - 若
instance_ != nullptr:delete instance_instance_ = nullptr
注意
- 若其他线程仍持有旧指针会悬空,需外部保证生命周期同步。
5. SQL 字符串转义
5.1 static std::string DBConnector::escapeSqlLiteral(const std::string& value)
- 作用:将字符串字面量中的特殊字符转义,降低拼接 SQL 的注入风险。
处理规则
\\->\\\\'->''\0->\\0\b->\\b\n->\\n\r->\\r\t->\\t\x1A->\\Z- 其他字符原样追加
关键说明
- 这是字符串层面的最小防护,不等价于预编译参数绑定。
- 生产建议优先 prepared statements。
6. 线程安全与并发语义
已加锁部分
initInstancegetInstancedestroyInstance
未加锁部分
connect/query/execute/getError- 对同一个
MYSQL*的并发访问未在该类内部同步
面试回答建议
“当前实现将单例创建做了线程安全,但数据库操作层并没有连接池和调用级互斥;高并发场景应改为连接池 + 每连接独占线程或租借模型。”
7. 常见调用链(项目内)
DBConnector::initInstance(...)DBConnector* db = DBConnector::getInstance()db->connect()- 读:
db->query(sql)/ 写:db->execute(sql) - 失败时:
db->getError()
8. 已知限制与改进方向
- 单连接模型,不适合高并发
- 返回数据全字符串化,类型信息丢失
- 错误体系是“返回值 + last_error_”,缺统一错误码
- 无事务封装、无 prepared statement、无超时重连治理
可演进方向:
- 连接池(min/max/idle/lifetime)
- prepared statement 与参数绑定
- 超时、重试、断线重连
- 统一错误码与可观测指标(慢 SQL、失败率)
下一模块(顺序)
EmailSender → docs/modules/detail/EmailSender_FUNCTIONS.md