19 C、C++ 操作 MySQL

🦄 个人主页 : 小米里的大麦-CSDN博客

🎏 所属专栏 : MySQL_小米里的大麦的博客-CSDN博客

🎁 GitHub主页 : 小米里的大麦的 GitHub

文章目录

  • [C/C++ 操作 MySQL](#C/C++ 操作 MySQL)
    • [1. 准备工作](#1. 准备工作)
      • [1. 环境准备](#1. 环境准备)
      • [2. C 和 C++ 接口初识](#2. C 和 C++ 接口初识)
    • [2. 在 Linux 中用 C/C++操作 MySQL](#2. 在 Linux 中用 C/C++操作 MySQL)
      • [1. 准备工作](#1. 准备工作)
      • [2. C 代码操作 MySQL](#2. C 代码操作 MySQL)
      • [3. C++ 操作 MySQL](#3. C++ 操作 MySQL)
    • [3. Windows 中使用 C/C++ 操作 MySQL](#3. Windows 中使用 C/C++ 操作 MySQL)
      • [1. 准备工作:确保 Linux 上的 MySQL 允许远程连接](#1. 准备工作:确保 Linux 上的 MySQL 允许远程连接)
      • [2. 在 Windows 上安装 MySQL](#2. 在 Windows 上安装 MySQL)
        • [1. 验证 MySQL Server 是否已包含 C API 库](#1. 验证 MySQL Server 是否已包含 C API 库)
        • [2. 如果缺少开发组件怎么办?](#2. 如果缺少开发组件怎么办?)
      • [3. 在 Visual Studio 中配置 C/C++项目](#3. 在 Visual Studio 中配置 C/C++项目)
        • [1. 创建项目并配置属性](#1. 创建项目并配置属性)
        • [2. 放置运行时 DLL](#2. 放置运行时 DLL)
        • [3. 测试代码](#3. 测试代码)
      • [4. 在 VS Code 中配置 C/C++项目(使用 CMake)](#4. 在 VS Code 中配置 C/C++项目(使用 CMake))
        • [1. 安装必要插件](#1. 安装必要插件)
        • [2. 项目结构示例](#2. 项目结构示例)
        • [3. 编写 CMakeLists.txt](#3. 编写 CMakeLists.txt)
        • [4. 配置 CMake Tools](#4. 配置 CMake Tools)
        • [5. 关于 libmysql.dll](#5. 关于 libmysql.dll)
    • [4. 关于 MySQL Connector/C++ 的讨论](#4. 关于 MySQL Connector/C++ 的讨论)
    • 共勉

C/C++ 操作 MySQL

1. 准备工作

1. 环境准备

安装 MySQL 的 C 语言客户端开发库(包含头文件和链接库):

bash 复制代码
sudo apt update
sudo apt install libmysqlclient-dev

另一个命令(可选):

bash 复制代码
sudo apt install -y build-essential pkg-config default-libmysqlclient-dev

这条命令会安装:

  • build-essential:包含 gcc、g++、make 等编译工具链。
  • pkg-config:用于管理库的编译和链接参数,方便 Makefile 或构建系统自动获取头文件路径和库名称。
  • default-libmysqlclient-dev :这是一个"元包",在 Ubuntu 上通常依赖于具体的 MySQL 客户端开发库(如 libmysqlclient-dev),相当于自动选择并安装当前发行版推荐的 MySQL 客户端库。

如果希望在 C++ 中使用更"面向对象"的接口,可以考虑安装 MySQL Connector/C++:

bash 复制代码
sudo apt install libmysqlcppconn-dev   # Ubuntu 某些版本可能不包,需从源码或第三方源安装

2. C 和 C++ 接口初识

  • MySQL C API :这是 MySQL 官方提供的原生 C 语言接口,头文件为 <mysql/mysql.h>,所有函数均以 C 语言风格导出(如 mysql_init()、mysql_query() 等)。C++ 程序完全可以直接包含此头文件并调用这些函数,因为 C++兼容 C 语言。因此,如果使用 C API,那么头文件和函数接口在 C 和 C++中是完全相同的。

  • MySQL Connector/C++ :这是 MySQL 官方提供的面向 C++ 的专用驱动,使用面向对象的风格(如 sql::Connection、sql::Statement 等),需要安装额外的开发库(如 libmysqlcppconn-dev)。它与 C API 不同,头文件也不一样(如 <mysql_connection.h>)。这类接口是 C++特有的,C 语言无法直接使用。

2. 在 Linux 中用 C/C++操作 MySQL

1. 准备工作

先在 MySQL 中创建对应的用户并赋予权限等信息:

sql 复制代码
# 创建用户:仅允许本机登录,密码为 123456
mysql> create user 'local_minbit'@'localhost' identified by '123456';
Query OK, 0 rows affected (0.02 sec)

# 创建数据库:若不存在则创建。注意库名含横杠,必须用反引号 `` 包裹
mysql> create database if not exists `local-C-test`;
Query OK, 1 row affected (0.01 sec)

# 授权业务库:给该用户在 `local-C-test` 库下的所有权限
mysql> grant all privileges on `local-C-test`.* to 'local_minbit'@'localhost';
Query OK, 0 rows affected (0.01 sec)
# 授权系统表:允许该用户查看 mysql.user 表(用于查询用户列表)
mysql> grant select on mysql.user to 'local_minbit'@'localhost';
Query OK, 0 rows affected (0.02 sec)

# 切换数据库
mysql> use local-C-test;
mysql> CREATE TABLE IF NOT EXISTS student_score (
    ->     id INT AUTO_INCREMENT PRIMARY KEY,		# 自增主键
    ->     name VARCHAR(100) NOT NULL,				# 姓名,非空
    ->     subject VARCHAR(100) NOT NULL,			# 科目,非空
    ->     score DECIMAL(5,2) NOT NULL				# 分数,定点数(共5位,小数2位),防精度丢失
    -> );

2. C 代码操作 MySQL

cpp 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mysql/mysql.h> // MySQL C API 头文件

// 数据库连接参数宏定义,方便后续修改
#define DB_HOST "localhost"    // 数据库地址,localhost表示本机
#define DB_USER "local_minbit" // 数据库用户名
#define DB_PASS "123456"       // 数据库密码
#define DB_NAME "local-C-test" // 数据库名称


// 错误处理封装函数,当发生数据库错误时,打印错误信息并安全退出
void finish_with_error(MYSQL *con)
{
    // mysql_error(con): 返回最近一次调用MySQL函数失败的错误描述字符串
    fprintf(stderr, "%s\n", mysql_error(con));

    // mysql_close(con): 关闭数据库连接,释放由con指向的结构体
    // 注意:即使连接中间出错了,也需要调用此函数防止内存泄漏
    mysql_close(con);
    exit(1);
}

int main()
{
    // 1. 初始化 MySQL 对象
    // mysql_init(NULL): 初始化一个MYSQL结构体,分配并初始化内存
    // 参数:NULL表示由库自动分配内存
    // 返回值:成功返回新分配的句柄指针,失败返回NULL
    MYSQL *con = mysql_init(NULL);
    if (con == NULL)
    {
        fprintf(stderr, "mysql_init() failed\n");
        exit(1);
    }

    // 2. 连接数据库
    // mysql_real_connect(): 建立与MySQL服务器的物理连接
    // 参数说明:mysql_init返回的句柄, 主机名或IP地址, 用户名, 密码, 数据库名, 端口(0表示默认), socket文件名, 客户端标志
    // 注意:要连接的数据库名如果为NULL,连接后需手动选择库;端口号:0表示使用默认端口3306;
    // Unix Socket文件名或命名管道名,通常设为NULL让系统自动选择;客户端标志位通常设为0
    // 返回值:成功返回con指针,失败返回NULL
    if (mysql_real_connect(con, DB_HOST, DB_USER, DB_PASS, DB_NAME, 0, NULL, 0) == NULL)
    {
        finish_with_error(con);
    }

    // 3. 执行插入操作
    // mysql_query(): 执行由字符串表示的SQL语句
    // 参数:连接句柄,SQL语句字符串
    // 返回值:成功返回0,失败返回非0
    // 注意:该函数会自动检测语句是否需要分号结尾(通常不需要)
    if (mysql_query(con, "INSERT INTO student_score (name, subject, score) VALUES ('张三', '数学', 88.5), ('李四', '英语', 92.0)"))
    {
        finish_with_error(con);
    }

    // mysql_affected_rows(): 获取最近一次INSERT/UPDATE/DELETE操作影响的行数
    // 返回值:my_ulonglong类型(64位无符号整数),如果没有匹配行则返回0
    printf("插入成功,影响行数: %lu\n", (unsigned long)mysql_affected_rows(con));


    // 4. 查询操作
    if (mysql_query(con, "SELECT id, name, subject, score FROM student_score"))
    {
        finish_with_error(con);
    }

    // mysql_store_result(): 将查询的结果从服务器一次性取回到客户端,结果存储在内存中,必须在使用完后手动释放!
    // 返回值:成功返回MYSQL_RES结构体指针,失败或无结果集返回NULL
    MYSQL_RES *result = mysql_store_result(con);
    if (result == NULL)
    {
        finish_with_error(con);
    }

    // mysql_num_fields(): 获取结果中字段(列)的数量
    // 这对于遍历打印数据至关重要,因为我们不知道查回来有几列!
    int num_fields = mysql_num_fields(result);
    MYSQL_ROW row; // MYSQL_ROW 实际上是 char** 类型,代表一行数据的字符串数组

    printf("\n当前成绩表数据:\n");

    // mysql_fetch_row(): 从结果中获取下一行数据,工作原理类似于迭代器,每次调用自动移动到下一行
    // 返回值:成功返回MYSQL_ROW(字符串数组),如果没有更多行则返回NULL
    while ((row = mysql_fetch_row(result)))
    {
        for (int i = 0; i < num_fields; i++)
        {
            // row[i] 访问当前行的第 i 列数据
            // 注意:如果数据库中该字段值为 NULL,row[i] 会是 NULL指针!
            // 这里使用三元运算符防止空指针导致的段错误,如果是NULL则打印 "NULL"
            printf("%s ", row[i] ? row[i] : "NULL");
        }
        printf("\n");
    }

    // mysql_free_result(): 释放结果集占用的内存
    // 防止内存泄漏,非常重要。
    mysql_free_result(result);


    // 5. 更新操作:将张三的数学成绩改为95.0
    if (mysql_query(con, "UPDATE student_score SET score = 95.0 WHERE name = '张三' AND subject = '数学'"))
    {
        finish_with_error(con);
    }
    printf("\n更新成功,影响行数: %lu\n", (unsigned long)mysql_affected_rows(con));


    // 6. 删除操作:删除李四的英语记录
    if (mysql_query(con, "DELETE FROM student_score WHERE name = '李四' AND subject = '英语'"))
    {
        finish_with_error(con);
    }
    printf("删除成功,影响行数: %lu\n", (unsigned long)mysql_affected_rows(con));


    // 7. 再次查询,验证结果
    if (mysql_query(con, "SELECT id, name, subject, score FROM student_score"))
    {
        finish_with_error(con);
    }
    result = mysql_store_result(con);
    if (result == NULL)
    {
        finish_with_error(con);
    }
    printf("\n操作后的数据:\n");

    // 复用之前的遍历逻辑
    while ((row = mysql_fetch_row(result)))
    {
        for (int i = 0; i < num_fields; i++)
        {
            printf("%s ", row[i] ? row[i] : "NULL");
        }
        printf("\n");
    }
    mysql_free_result(result);


    // 8. 关闭连接,mysql_close(): 断开与服务器的连接,并释放连接句柄占用的内存
    // 这是最后一步清理工作!
    mysql_close(con);

    return 0;
}

makefile 文件:

makefile 复制代码
C_conn_mysql: C_conn_mysql.c
	gcc -o $@ $^ -I/usr/include/mysql -L/usr/lib/mysql -lmysqlclient

.PHONY: clean
clean:
	rm -f C_conn_mysql
  • -I:用于指明头文件的搜索路径。
  • -L:用于指明库文件的搜索路径。
  • -l:用于指明需要连接库文件路径下的哪一个库。

输出:

text 复制代码
插入成功,影响行数: 2

当前成绩表数据:
1 张三 数学 88.50 
2 李四 英语 92.00 

更新成功,影响行数: 1
删除成功,影响行数: 1

操作后的数据:
1 张三 数学 95.00

3. C++ 操作 MySQL

cpp 复制代码
#include <iostream>
#include <memory>
#include <mysql/mysql.h> // MySQL C API 头文件,C++ 可以直接调用 C 接口

class StudentDB
{
public:
    StudentDB(const std::string &host, const std::string &user,
              const std::string &pass, const std::string &db)
    {
        // 1. 初始化连接句柄
        // mysql_init(nullptr): 参数为空表示让库自动分配 MYSQL 结构体内存
        conn = mysql_init(nullptr);
        if (conn == nullptr)
        {
            std::cerr << "mysql_init() 失败!" << std::endl;
            exit(1);
        }

        // 2. 建立连接
        // 参数说明:mysql_init返回的句柄, 主机名或IP地址, 用户名, 密码, 数据库名, 端口(0表示默认), socket文件名, 客户端标志
        if (mysql_real_connect(conn, host.c_str(), user.c_str(),
                               pass.c_str(), db.c_str(), 0, nullptr, 0) == nullptr)
        {
            finish_with_error(); // 连接失败,打印错误并退出
        }
    }

    ~StudentDB()
    {
        if (conn)
        {
            mysql_close(conn); // 关闭连接并释放句柄内存
        }
    }

    // 插入操作
    bool insert(const std::string &name, const std::string &subject, double score)
    {
        char query[256]; // 缓冲区,用于存储拼接好的 SQL 语句

        // snprintf: 安全的格式化字符串函数,防止缓冲区溢出
        // 注意:实际更推荐使用 mysql_real_escape_string 处理特殊字符防止 SQL 注入
        snprintf(query, sizeof(query),
                 "INSERT INTO student_score (name, subject, score) VALUES ('%s', '%s', %.2f)",
                 name.c_str(), subject.c_str(), score);

        // mysql_query: 执行 SQL 语句
        // 返回值:非 0 表示执行失败
        if (mysql_query(conn, query))
        {
            std::cerr << "INSERT失败: " << mysql_error(conn) << std::endl;
            return false;
        }

        // mysql_affected_rows: 获取受影响的行数,通常用于验证插入是否真的发生
        std::cout << "插入成功,影响行数: " << mysql_affected_rows(conn) << std::endl;
        return true;
    }

    // 查询并打印所有记录:流程:执行 SQL -> 获取结果集 -> 遍历行 -> 释放结果集
    void queryAll()
    {
        // 执行查询语句
        if (mysql_query(conn, "SELECT id, name, subject, score FROM student_score"))
        {
            std::cerr << "SELECT失败: " << mysql_error(conn) << std::endl;
            return;
        }

        // mysql_store_result: 将查询结果从服务器全部取回并存储在本地内存
        // 返回值:MYSQL_RES 指针,失败返回 NULL
        MYSQL_RES *result = mysql_store_result(conn);
        if (result == nullptr)
        {
            std::cerr << "mysql_store_result()失败: " << mysql_error(conn) << std::endl;
            return;
        }

        // mysql_num_fields: 获取结果集中的列数(字段数量)
        int num_fields = mysql_num_fields(result);
        MYSQL_ROW row; // MYSQL_ROW 实际上是 char** 类型,代表一行数据

        std::cout << "-----------------------------------" << std::endl;

        // mysql_fetch_row: 从结果集中逐行获取数据
        // 返回值:下一行的数据,如果没有更多行则返回 NULL
        while ((row = mysql_fetch_row(result)))
        {
            for (int i = 0; i < num_fields; i++)
            {
                // row[i]: 访问当前行的第 i 列
                // 注意:如果数据库字段值为 NULL,row[i] 指针也是 NULL
                // 这里使用三元运算符进行 NULL 指针保护
                std::cout << (row[i] ? row[i] : "NULL") << "\t";
            }
            std::cout << std::endl;
        }
        std::cout << "-----------------------------------" << std::endl;

        // mysql_free_result: 释放 result 占用的内存
        // 非常重要:每次查询后必须释放,否则会造成严重的内存泄漏
        mysql_free_result(result);
    }

    // 更新数据
    bool update(const std::string &name, const std::string &subject, double newScore)
    {
        char query[256];
        snprintf(query, sizeof(query),
                 "UPDATE student_score SET score = %.2f WHERE name = '%s' AND subject = '%s'",
                 newScore, name.c_str(), subject.c_str());

        if (mysql_query(conn, query))
        {
            std::cerr << "UPDATE失败: " << mysql_error(conn) << std::endl;
            return false;
        }
        std::cout << "更新成功,影响行数: " << mysql_affected_rows(conn) << std::endl;
        return true;
    }

    // 删除数据
    bool remove(const std::string &name, const std::string &subject)
    {
        char query[256];
        snprintf(query, sizeof(query),
                 "DELETE FROM student_score WHERE name = '%s' AND subject = '%s'",
                 name.c_str(), subject.c_str());

        if (mysql_query(conn, query))
        {
            std::cerr << "DELETE失败: " << mysql_error(conn) << std::endl;
            return false;
        }
        std::cout << "删除成功,影响行数: " << mysql_affected_rows(conn) << std::endl;
        return true;
    }

private:
    MYSQL *conn; // MySQL 连接句柄指针,操作数据库的核心对象

    // 内部错误处理函数,打印错误信息、关闭连接并退出程序
    // 注意:在构造函数失败时调用,因为对象可能未完全构造,无法依赖析构函数
    void finish_with_error()
    {
        std::cerr << mysql_error(conn) << std::endl;
        mysql_close(conn); // 发生错误时手动关闭连接,防止资源泄漏
        exit(1);
    }
};

int main()
{
    // 1. 实例化数据库对象
    // 构造函数会自动执行 mysql_init 和 mysql_real_connect
    StudentDB db("localhost", "local_minbit", "123456", "local-C-test");

    // 2. 插入测试数据
    db.insert("张三", "数学", 88.5);
    db.insert("李四", "英语", 92.0);

    std::cout << "\n初始数据:" << std::endl;
    db.queryAll();

    // 3. 更新数据:将张三数学成绩修改为 95.0
    db.update("张三", "数学", 95.0);

    // 4. 删除数据:删除李四的英语成绩
    db.remove("李四", "英语");

    std::cout << "\n操作后数据:" << std::endl;
    db.queryAll();

    // 5. 程序结束
    // main 函数结束时,局部变量 db 会被销毁,自动调用析构函数 ~StudentDB()
    // 析构函数会执行 mysql_close(conn),确保连接安全关闭
    return 0;
}

运行输出结果:

text 复制代码
插入成功,影响行数: 1
插入成功,影响行数: 1

初始数据:
-----------------------------------
1       张三    数学    95.00
3       张三    数学    88.50
4       李四    英语    92.00
-----------------------------------
更新成功,影响行数: 1
删除成功,影响行数: 1

操作后数据:
-----------------------------------
1       张三    数学    95.00
3       张三    数学    95.00
-----------------------------------

3. Windows 中使用 C/C++ 操作 MySQL

1. 准备工作:确保 Linux 上的 MySQL 允许远程连接

  1. 修改 MySQL 配置文件: 更改或新增 bind-address 行为 0.0.0.0

    bash 复制代码
    vim /etc/mysql/mysql.conf.d/mysqld.cnf
    sql 复制代码
    #
    # The MySQL database server configuration file.
    #
    # One can use all long options that the program supports.
    # Run program with --help to get a list of available options and with
    # --print-defaults to see which it would actually understand and use.
    #
    # For explanations see
    # http://dev.mysql.com/doc/mysql/en/server-system-variables.html
    
    # Here is entries for some specific programs
    # The following values assume you have at least 32M ram
    
    [mysqld]
    #
    # * Basic Settings
    #
    user		= mysql
    # pid-file	= /var/run/mysqld/mysqld.pid
    # socket	= /var/run/mysqld/mysqld.sock
    # port		= 3306
    # datadir	= /var/lib/mysql
    
    
    # If MySQL is running as a replication slave, this should be
    # changed. Ref https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_tmpdir
    # tmpdir		= /tmp
    #
    # Instead of skip-networking the default is now to listen only on
    # localhost which is more compatible and is not less secure.
    bind-address		= 127.0.0.1
    mysqlx-bind-address	= 127.0.0.1
    
    # ===== 自定义扩展配置 =====
    # 设置默认字符集为 utf8mb4,防止中文和 emoji 乱码
    character-set-server = utf8mb4
    collation-server     = utf8mb4_general_ci
    
    # 启用远程访问(默认仅本机),保持仅本地访问(通过 Xshell 登录服务器再操作)可将 0.0.0.0 设置为 127.0.0.1
    bind-address         = 0.0.0.0
    mysqlx-bind-address  = 0.0.0.0
    
    # 使用传统认证插件,兼容 Navicat、旧版客户端
    default-authentication-plugin = mysql_native_password
    
    # 可选:增加连接稳定性
    # max_connections = 200
    # wait_timeout = 600
    # interactive_timeout = 600
    
    #
    # * Fine Tuning
    #
    key_buffer_size		= 16M
    # max_allowed_packet	= 64M
    # thread_stack		= 256K
    
    # thread_cache_size       = -1
    
    # This replaces the startup script and checks MyISAM tables if needed
    # the first time they are touched
    myisam-recover-options  = BACKUP
    
    # max_connections        = 151
    
    # table_open_cache       = 4000
    
    #
    # * Logging and Replication
    #
    # Both location gets rotated by the cronjob.
    #
    # Log all queries
    # Be aware that this log type is a performance killer.
    # general_log_file        = /var/log/mysql/query.log
    # general_log             = 1
    #
    # Error log - should be very few entries.
    #
    log_error = /var/log/mysql/error.log
    #
    # Here you can see queries with especially long duration
    # slow_query_log		= 1
    # slow_query_log_file	= /var/log/mysql/mysql-slow.log
    # long_query_time = 2
    # log-queries-not-using-indexes
    #
    # The following can be used as easy to replay backup logs or for replication.
    # note: if you are setting up a replication slave, see README.Debian about
    #       other settings you may need to change.
    # server-id		= 1
    # log_bin			= /var/log/mysql/mysql-bin.log
    # binlog_expire_logs_seconds	= 2592000
    max_binlog_size   = 100M
    # binlog_do_db		= include_database_name
    # binlog_ignore_db	= include_database_name

    然后重启 MySQL:

    bash 复制代码
    sudo systemctl restart mysql
  2. 创建允许从 Windows IP 连接的数据库用户(或修改现有用户)

    sql 复制代码
    mysql> create user 'win_test'@'%' identified by '123456';
    Query OK, 0 rows affected (0.01 sec)
    
    mysql> grant all privileges on *.* to 'win_test'@'%';
    Query OK, 0 rows affected (0.01 sec)
  3. 检查防火墙:确保 Linux 防火墙(如 ufw)开放 MySQL 端口(默认 3306):

    bash 复制代码
    sudo ufw allow 3306/tcp

2. 在 Windows 上安装 MySQL

  • 下载 :访问 MySQL Community Downloads,选择 Windows (x86, 64-bit), ZIP Archive 或 MSI Installer。

  • 安装 :如果下载 ZIP,解压后将其 C:\Program Files\MySQL\MySQL Server 8.4\bin 目录添加到系统 PATH;如果使用 MSI,安装过程中仅选择 "Client Programs" 即可。

  • 使用:打开命令提示符,执行:

    sql 复制代码
    mysql -h 12x.5x.16x.x -P 3306 -u win_test -p	# 用自己公网 IP

在 Windows 上通过 MySQL Installer 安装 MySQL Server 时,通常就会包含 C API 客户端库(libmysql),但需要注意安装选项。

1. 验证 MySQL Server 是否已包含 C API 库

检查安装目录: 打开 MySQL 的安装路径(C:\Program Files\MySQL\MySQL Server 8.4),查看是否存在以下文件夹和文件:

  • include 文件夹:里面应该有 mysql.h、mysql_com.h 等头文件。
  • lib 文件夹:里面应该有 libmysql.lib(静态库/导入库)和 libmysql.dll(动态链接库)。
  • 如果能找到路径(如 C:\Program Files\MySQL\MySQL Server 8.4\lib\libmysql.dll),也说明已安装。

如果这些文件存在,说明 C API 库已随服务器安装。若缺少,可能是安装时未选择开发组件。

2. 如果缺少开发组件怎么办?
  • 重新运行 MySQL Installer,选择 "Modify" ,然后勾选 "Development Components" 或 "C Connector",安装即可。
  • 或者直接下载独立的 Connector/C 并解压到任意目录,后续配置时指向该目录。

3. 在 Visual Studio 中配置 C/C++项目

假设 MySQL 安装在 C:\Program Files\MySQL\MySQL Server 8.4(或 Connector/C 解压目录)。视频可参考:【C/C++服务器开发】C 语言/C++连接 mysql | B 站。

1. 创建项目并配置属性
  • 打开 Visual Studio,创建新的控制台应用(C++)。
  • 点击 项目 → 属性(注意配置选择"所有配置",平台选择"x64"或"x86"匹配 MySQL 库版本)。
  • 在 VC++ 目录 中:
    • 包含目录 :添加 C:\Program Files\MySQL\MySQL Server 8.4\include
    • 库目录 :添加 C:\Program Files\MySQL\MySQL Server 8.4\lib
  • 在 链接器 → 输入 → 附加依赖项 中添加 libmysql.lib。
2. 放置运行时 DLL

将 libmysql.dll 从 lib 文件夹复制到可执行文件(.exe)输出目录(例如 Debug 或 Release),可以参考我的路径:D:\Coding\Visual Studio 2022\pro\x64\Debug。

3. 测试代码
cpp 复制代码
#include <iostream>
#include <mysql.h>   // 注意头文件名为 mysql.h

int main()
{
    MYSQL* conn = mysql_init(nullptr);
    if (conn == nullptr)
    {
        std::cerr << "mysql_init failed" << std::endl;
        return 1;
    }

    // 尝试连接远程Linux MySQL(替换为实际IP、用户、密码)
    if (mysql_real_connect(conn, "122.51.165.8", "win_test", "123456", "local-C-test", 3306, nullptr, 0) == nullptr)
    {
        std::cerr << "Connection error: " << mysql_error(conn) << std::endl;
        mysql_close(conn);
        return 1;
    }

    std::cout << "Connected successfully!" << std::endl;

    mysql_query(conn, "SELECT VERSION()");
    MYSQL_RES* res = mysql_store_result(conn);
    MYSQL_ROW row = mysql_fetch_row(res);
    std::cout << "MySQL version: " << row[0] << std::endl;
    mysql_free_result(res);

    mysql_close(conn);
    return 0;
}

点击运行,会发现报错了,说找不到 xxx.dll(具体名字忘记了),再网络上找了一大圈,找到方案:将路径 C:\Program Files\MySQL\MySQL Server 8.4\lib 中的libmysql.dll和libmysql.lib一并放到D:\Coding\Visual Studio 2022\pro\x64\Debug里面。激动的心颤抖的手点击运行,再次报错:系统错误,由于找不到 libssl-3-x64.dll,无法继续执行代码。重新安装程序可能会解决此问题。真是天塌了,于是苦苦再次寻觅互联网,才找到解决办法: 解决 libssl-3-x64.dll 问题的办法、Punishing gray raven dll 下载 。说实话,我也并不清楚原因,只是将下载下来的 2 个动态共享库文件一并放入到了 .exe同级目录下(前前后后总共放了 4 个文件),再次运行成功得到输出:

我自己也不清楚,具体原因和解决原理,所以仅仅是将我的经验供大家参考! Google 搜索多次救我于水火,如果期间有其他报错,不妨试试 Google 搜索看看!

4. 在 VS Code 中配置 C/C++项目(使用 CMake)

在 VS Code 上配置就比较灵活了,方式比较多样,我们主要使用 VS Code 配合 CMake 工具链(CMake Tools 插件),因为配置比较灵活。其他方法自行 B 站/Google 了,另外,推荐文章:VSCode 环境下连接 MySQL 8.0 数据库 (C++)。

1. 安装必要插件
  • C/C++ 插件(Microsoft)
  • CMake Tools 插件
2. 项目结构示例
text 复制代码
test/
├── .vscode/
│   ├── settings.json  (可选)
│   └── tasks.json     (可选)
├── CMakeLists.txt
└── main.cpp
3. 编写 CMakeLists.txt
cmake 复制代码
cmake_minimum_required(VERSION 3.10)
project(MySQLTest)

# 指定MySQL头文件和库路径(根据实际安装位置修改)
set(MYSQL_INCLUDE_DIR "C:/Program Files/MySQL/MySQL Server 8.4/include")
set(MYSQL_LIB_DIR "C:/Program Files/MySQL/MySQL Server 8.4/lib")

# 添加头文件搜索路径
include_directories(${MYSQL_INCLUDE_DIR})

# 添加可执行文件
add_executable(${PROJECT_NAME} main.cpp)

# 链接MySQL库
target_link_libraries(${PROJECT_NAME} ${MYSQL_LIB_DIR}/libmysql.lib)

# 注意:运行时需要将 libmysql.dll 放在可执行文件同级目录,或加入PATH
4. 配置 CMake Tools
  • 打开项目,CMake Tools 会自动检测工具链。
  • 选择配置(如 Visual Studio 2022 Release - x64)。
  • 构建(F7),然后运行。

如果不想使用 CMake,也可以手动编写 tasks.json 和 launch.json 配置编译命令,但 CMake 更推荐。

5. 关于 libmysql.dll

无论使用哪种方式,都需要确保 libmysql.dll 在运行时可被找到!

  • 将 DLL 复制到生成的可执行文件目录(例如 build/Debug/)。
  • 或将 MySQL 的 lib 目录添加到系统 PATH。

4. 关于 MySQL Connector/C++ 的讨论

特性 MySQL C API MySQL Connector/C++
底层 C 语言接口 C++面向对象封装
性能 极高 接近 C API,略有损耗
易用性 较繁琐 简洁,支持异常
预处理语句 支持但较复杂 原生友好支持
稳定性 非常成熟 8.x 后稳定,早期有历史问题
依赖 仅 libmysqlclient 需安装专用库
流行度 非常广泛(尤其是老项目) 较新,逐渐增加

实际 C++ 后端中,业务代码一般不会直接大量使用裸的 MySQL C API,而是会在底层基于 C API 封装一层数据库访问模块(如连接管理、SQL 执行、事务、结果集解析、连接池等)。MySQL Connector/C++ 虽然能用,但使用较少;更常见的是采用基于 C API 的第三方轻量库或公司内部封装,因为它们更稳定、更通用,也更方便控制性能和部署。

所以,大多数情况,我们只需要掌握 MySQL C API 的使用就行了,进阶基本上都是使用第三方库、个人/公司封装的版本、其他更便捷的方式。当然连接方式也会多样,也可以尝试一系列的图形化工具......

共勉

相关推荐
shuoshuohaohao2 小时前
Spring-boot-3 -注解 yaml配置 -日志
java·数据库·mysql
有毒的教程2 小时前
Dev-C++ 使用扩展库两种方式:DevPak一键安装 / 手动配置(重点)
android·c++·adb
Logic1012 小时前
C语言/数据结构位运算题解:异或XOR找出套碗游戏中的“幸运碗“——只出现一次的数字
c语言·数据结构·算法·数组·位运算·时间复杂度·空间复杂度
纪念 2292 小时前
c++ string(二)
开发语言·c++
番茄灭世神2 小时前
PN学堂GD32教程第12篇——SPI与Nor Flash
c语言·stm32·单片机·嵌入式·gd32·总线协议
sanqima2 小时前
MFC里的线程本地存储(TLS)与模块状态(Module State)
c++·mfc·tls·module state
见叶之秋2 小时前
【C++】从基类到派生类:关于C++继承的务实梳理
开发语言·c++
无毁的湖光-Al3 小时前
解Bug之路-隔离故障点后业务不恢复?-热点Key处理与亚稳态故障
java·数据库·mysql
kyle~3 小时前
C++ 智能指针:RAII、所有权与对象生命周期
开发语言·c++