C++数据库操作

一、前置条件:环境准备

C++操作MySQL依赖MySQL官方的C API客户端库(libmysqlclient),需先完成安装和配置:

1. Linux系统(Ubuntu/Debian)
bash 复制代码
# 安装客户端开发库(包含头文件和链接库)
sudo apt-get update
sudo apt-get install libmysqlclient-dev
# 验证安装:查看头文件和库文件
ls /usr/include/mysql/  # 头文件目录
ls /usr/lib/x86_64-linux-gnu/libmysqlclient.so  # 库文件
2. Windows系统
  1. 下载MySQL C API:从MySQL官网下载"MySQL Connector/C";
  2. 配置项目:
    • 头文件路径:添加mysql/include到项目的"附加包含目录";
    • 库文件路径:添加mysql/lib到项目的"附加库目录";
    • 链接库:添加libmysql.lib(编译时)和libmysql.dll(运行时,放到可执行文件同目录)。
3. 编译命令(Linux)

编写代码后,编译时需链接MySQL库:

bash 复制代码
g++ mysql_crud.cpp -o mysql_crud -lmysqlclient -std=c++11

二、C++操作MySQL的核心流程

所有数据库操作的基础流程是:

复制代码
初始化MySQL库 → 创建连接句柄 → 连接数据库 → 执行SQL(增删改查) → 处理结果(查询特有) → 释放资源

三、核心API讲解

先熟悉MySQL C API的关键函数(C++中直接调用这些C风格API):

函数名 作用
mysql_library_init() 初始化MySQL库(程序启动时调用)
mysql_init() 创建并初始化MySQL连接句柄(MYSQL*类型)
mysql_real_connect() 建立与MySQL服务器的实际连接(指定主机、用户名、密码、数据库等)
mysql_query() 执行SQL语句(增/删/改/查,查询需配合结果集函数)
mysql_store_result() 获取查询结果集(将结果加载到内存,适合小数据量)
mysql_fetch_row() 逐行读取结果集中的数据
mysql_num_fields() 获取结果集的列数
mysql_fetch_field() 获取列的元数据(列名、类型等)
mysql_affected_rows() 获取增/删/改操作影响的行数
mysql_free_result() 释放结果集内存(避免内存泄漏)
mysql_close() 关闭数据库连接
mysql_library_end() 释放MySQL库资源(程序结束时调用)

四、完整CRUD代码示例

下面是包含"连接数据库+增删改查"的完整可运行代码,代码中有详细注释:

cpp 复制代码
#include <iostream>
#include <cstdio>
#include <cstring>
// 引入MySQL C API头文件
#include <mysql/mysql.h>

using namespace std;

// 数据库配置(根据自己的环境修改)
const char* HOST = "localhost";    // 数据库主机(本地为localhost)
const char* USER = "root";         // 数据库用户名
const char* PASSWORD = "your_password"; // 数据库密码
const char* DB_NAME = "test_db";   // 要操作的数据库名
const unsigned int PORT = 3306;    // MySQL端口(默认3306)
const char* CHARSET = "utf8mb4";   // 字符集(支持中文)

// 封装MySQL连接类(简化操作,更符合C++风格)
class MySQLDB {
private:
    MYSQL* conn;  // MySQL连接句柄

public:
    // 构造函数:初始化连接句柄
    MySQLDB() {
        // 初始化MySQL库(0表示初始化所有核心组件)
        if (mysql_library_init(0, nullptr, nullptr) != 0) {
            cerr << "MySQL库初始化失败!" << endl;
            exit(1);
        }
        // 创建连接句柄
        conn = mysql_init(nullptr);
        if (conn == nullptr) {
            cerr << "创建连接句柄失败:" << mysql_error(conn) << endl;
            exit(1);
        }
    }

    // 连接数据库
    bool connect() {
        // 建立实际连接
        if (mysql_real_connect(conn, HOST, USER, PASSWORD, DB_NAME, PORT, nullptr, 0) == nullptr) {
            cerr << "数据库连接失败:" << mysql_error(conn) << endl;
            return false;
        }
        // 设置字符集(避免中文乱码)
        mysql_set_character_set(conn, CHARSET);
        cout << "数据库连接成功!" << endl;
        return true;
    }

    // 1. 新增操作(INSERT)
    bool insert(const string& sql) {
        if (mysql_query(conn, sql.c_str()) != 0) {
            cerr << "插入失败:" << mysql_error(conn) << endl;
            return false;
        }
        // 获取影响的行数(插入成功至少影响1行)
        int affected = mysql_affected_rows(conn);
        cout << "插入成功,影响行数:" << affected << endl;
        return true;
    }

    // 2. 删除操作(DELETE)
    bool remove(const string& sql) {
        if (mysql_query(conn, sql.c_str()) != 0) {
            cerr << "删除失败:" << mysql_error(conn) << endl;
            return false;
        }
        int affected = mysql_affected_rows(conn);
        cout << "删除成功,影响行数:" << affected << endl;
        return true;
    }

    // 3. 修改操作(UPDATE)
    bool update(const string& sql) {
        if (mysql_query(conn, sql.c_str()) != 0) {
            cerr << "修改失败:" << mysql_error(conn) << endl;
            return false;
        }
        int affected = mysql_affected_rows(conn);
        cout << "修改成功,影响行数:" << affected << endl;
        return true;
    }

    // 4. 查询操作(SELECT)
    bool query(const string& sql) {
        if (mysql_query(conn, sql.c_str()) != 0) {
            cerr << "查询失败:" << mysql_error(conn) << endl;
            return false;
        }

        // 获取结果集(加载所有数据到内存)
        MYSQL_RES* res = mysql_store_result(conn);
        if (res == nullptr) {
            cerr << "获取结果集失败:" << mysql_error(conn) << endl;
            return false;
        }

        // 获取列数和列名
        int col_num = mysql_num_fields(res);
        MYSQL_FIELD* fields = mysql_fetch_fields(res);
        // 打印列名
        for (int i = 0; i < col_num; ++i) {
            cout << fields[i].name << "\t";
        }
        cout << endl;

        // 逐行读取数据
        MYSQL_ROW row;
        while ((row = mysql_fetch_row(res)) != nullptr) {
            for (int i = 0; i < col_num; ++i) {
                // 注意:NULL值需要特殊处理(row[i]为nullptr)
                cout << (row[i] ? row[i] : "NULL") << "\t";
            }
            cout << endl;
        }

        // 释放结果集(必须!否则内存泄漏)
        mysql_free_result(res);
        return true;
    }

    // 析构函数:释放资源
    ~MySQLDB() {
        if (conn != nullptr) {
            mysql_close(conn);  // 关闭连接
        }
        mysql_library_end();    // 释放MySQL库资源
        cout << "数据库连接已关闭!" << endl;
    }
};

// 测试主函数
int main() {
    // 1. 创建数据库对象并连接
    MySQLDB db;
    if (!db.connect()) {
        return 1;
    }

    // 先创建测试表(如果不存在)
    string create_table_sql = "CREATE TABLE IF NOT EXISTS student ("
                              "id INT PRIMARY KEY AUTO_INCREMENT,"
                              "name VARCHAR(50) NOT NULL,"
                              "age INT NOT NULL,"
                              "score FLOAT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;";
    if (mysql_query(db.conn, create_table_sql.c_str()) != 0) {
        cerr << "创建表失败:" << mysql_error(db.conn) << endl;
        return 1;
    }

    // 2. 新增数据
    string insert_sql = "INSERT INTO student (name, age, score) VALUES ('张三', 18, 95.5), ('李四', 19, 88.0);";
    db.insert(insert_sql);

    // 3. 修改数据(把李四的分数改为90.0)
    string update_sql = "UPDATE student SET score = 90.0 WHERE name = '李四';";
    db.update(update_sql);

    // 4. 查询数据
    string query_sql = "SELECT * FROM student;";
    db.query(query_sql);

    // 5. 删除数据(删除年龄为18的记录)
    string delete_sql = "DELETE FROM student WHERE age = 18;";
    db.remove(delete_sql);

    // 再次查询,验证删除结果
    cout << "\n删除后的数据:" << endl;
    db.query(query_sql);

    return 0;
}

五、关键细节解释

1. 连接参数说明
  • mysql_real_connect的参数:(连接句柄, 主机, 用户名, 密码, 数据库名, 端口, unix套接字, 客户端标志)
  • 本地连接时unix套接字nullptr客户端标志0即可。
2. 中文乱码问题
  • 必须调用mysql_set_character_set(conn, "utf8mb4")设置字符集;
  • 数据库/表的字符集也需设置为utf8mb4(兼容所有Unicode字符,包括emoji)。
3. 查询结果处理
  • mysql_store_result():将查询结果全部加载到内存,适合小数据量;大数据量建议用mysql_use_result()(逐行读取,不占内存);
  • mysql_fetch_row()返回char**,每行数据按列存储,NULL值对应row[i] = nullptr,需特殊处理。
4. 错误处理
  • 每个MySQL API调用后都要检查返回值(比如mysql_query返回0表示成功,非0失败);
  • mysql_error(conn):返回最近一次操作的错误信息,调试必备。
5. 防SQL注入(重要!)

上面的示例直接拼接SQL字符串,存在SQL注入风险 (比如输入的name包含' OR 1=1 --)。生产环境需用预处理语句(Prepared Statement)

cpp 复制代码
// 预处理语句示例(插入数据)
MYSQL_STMT* stmt = mysql_stmt_init(conn);
string sql = "INSERT INTO student (name, age, score) VALUES (?, ?, ?);";
mysql_stmt_prepare(stmt, sql.c_str(), sql.length());

// 绑定参数
MYSQL_BIND params[3];
memset(params, 0, sizeof(params));
// 绑定name(字符串)
char name[] = "王五";
params[0].buffer_type = MYSQL_TYPE_STRING;
params[0].buffer = name;
params[0].buffer_length = strlen(name);
// 绑定age(int)
int age = 20;
params[1].buffer_type = MYSQL_TYPE_LONG;
params[1].buffer = &age;
// 绑定score(float)
float score = 92.5;
params[2].buffer_type = MYSQL_TYPE_FLOAT;
params[2].buffer = &score;

mysql_stmt_bind_param(stmt, params);
mysql_stmt_execute(stmt);
mysql_stmt_close(stmt);

六、总结

  1. C++操作MySQL依赖MySQL C API ,核心是通过MYSQL句柄管理连接,mysql_query执行SQL;
  2. 增删改操作直接执行SQL,通过mysql_affected_rows获取影响行数;查询操作需通过mysql_store_result获取结果集并逐行解析;
  3. 关键注意事项:
    • 必须初始化/释放MySQL库资源,避免内存泄漏;
    • 设置字符集防止中文乱码;
    • 生产环境用预处理语句防SQL注入;
    • 每个API调用后检查返回值,通过mysql_error排查错误。
相关推荐
酬勤-人间道2 小时前
CAD 曲线切割 3D 曲面:坡面 / 开挖模型的开挖 - 填埋精准计算解决方案
c++·计算机·cad·布尔计算·曲线切割·工业级解决方案·岩体
superman超哥2 小时前
Rust 日志级别与结构化日志:生产级可观测性实践
开发语言·后端·rust·可观测性·rust日志级别·rust结构化日志
咸鱼2.02 小时前
【java入门到放弃】数据结构
java·开发语言·数据结构
Access开发易登软件2 小时前
Access 数据可视化:如何制作箱形图
前端·数据库·vba·access·access开发
啊西:2 小时前
SuperMap iObjects Java地图生成栅格瓦片并保存到mongodb
java·开发语言·mongodb
醉风塘2 小时前
MongoDB内嵌文档深度解析:使用MongoTemplate进行高效操作
数据库·mongodb
电商API_180079052472 小时前
淘宝商品数据爬虫技术实践指南
大数据·数据库·人工智能·爬虫
老歌老听老掉牙2 小时前
PyQt5中RadioButton互斥选择的实现方法
开发语言·python·qt
一路往蓝-Anbo2 小时前
C语言从句柄到对象 (四) —— 接口抽象:从 Switch-Case 到通用接口
c语言·开发语言·stm32·嵌入式硬件