• #MySQL在C++中的基本`api`讲解



    ​ 在上篇文章中我介绍了MySQL在C语言中的基本 api,虽然只是基本的接口,但是我们依旧可以发现有这许多问题,比如,创建对象后必须手动释放,查询结果后必须手动释放否则就会有大量的内存泄漏问题出现,当然在C语言中对于MySQL多线程的把握,需要大量的锁去实现,这不仅提高代码的复杂程度,更是进一步的把后续的维护成本大大提升。

    而回看C++的三大特性,封装、继承、多态,无论是其中蕴含的RAII,对于锁的更加灵1活的使用,还是衍生出来的设计模式(如:单例模式)和池化技术,以及后对于异常的处理的都简化了代码的编写。

    本文将提供一个简单的demo代码,并逐步解释其中的含义,带你快速上手基本的api

    首先,确保你已经安装了MySQL Connector/C++库。可以从MySQL官网下载安装。

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main() {
        try {
            // 创建驱动程序实例
            sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance();
    
            // 通过驱动程序创建连接
            std::unique_ptr conn(driver->connect("tcp://127.0.0.1:3306", "username", "password"));
    
            // 连接到具体的数据库
            conn->setSchema("test_db");
    
            // 创建语句对象
            std::unique_ptr stmt(conn->createStatement());
    
            // 执行查询并获取结果集
            std::unique_ptr res(stmt->executeQuery("SELECT id, name FROM test_table"));
    
            // 遍历结果集并输出结果
            while (res->next()) {
                std::cout << "ID: " << res->getInt("id");
                std::cout << ", Name: " << res->getString("name") << std::endl;
            }
        } catch (sql::SQLException& e) {
            std::cerr << "SQLException: " << e.what() << std::endl;
            std::cerr << "SQLState: " << e.getSQLState() << std::endl;
        }
    
        return 0;
    }
    
    

    一、创建驱动程序实例

    创建驱动程序实例是使用MySQL Connector/C++库与MySQL数据库进行交互的第一步。这一步骤是通过调用get_mysql_driver_instance方法来实现的。其本质是用于获取MySQL_Driver类的单例实例。这个方法确保在整个程序中只存在一个驱动程序实例。

    sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance();`
    

    其中1、MySQL Connector/C++库使用了一些命名空间来组织其类和函数。sql::mysql命名空间包含了专门用于MySQL数据库的类和函数。

    2、MySQL_Driver类是MySQL Connector/C++库的一个核心类,它实现了与MySQL数据库的连接管理。这个类的实例负责创建和管理与MySQL服务器的连接。

    执行过程

    1. 调用get_mysql_driver_instance:
    • 当你调用sql::mysql::get_mysql_driver_instance()时,该方法会检查是否已经存在一个MySQL_Driver实例。
    • 如果不存在,它会创建一个新的实例。
    • 如果已经存在,它会返回现有的实例。
    1. 返回驱动程序实例:
    • 该方法返回一个指向MySQL_Driver实例的指针。

    为什么需要驱动程序实例

    驱动程序实例是与MySQL数据库通信的核心组件。通过这个实例,你可以:

    • 创建与数据库服务器的连接。
    • 执行SQL查询和命令。
    • 管理连接池和其他底层细节。

    二、连接服务器

    std::unique_ptr conn(driver->connect("tcp://127.0.0.1:3306", "username", "password"));
    conn1->setSchema("test_db1");
    

    这里我主要要讲一下这里的第一个参数,这个字符串由三部分组成:

    • protocol:通信协议。对于MySQL数据库,通常使用tcpsocket
    • host:数据库服务器的主机名或IP地址。
    • port:数据库服务器监听的端口号。

    在这个例子中:

    • tcp:表示使用TCP/IP协议进行连接。
    • 127.0.0.1:表示连接到本地主机(localhost)。
    • 3306:MySQL数据库默认的端口号。
    • “username”:数据库的用户名。
    • “password”:数据库的密码。

    为什么使用tcp://

    1. 明确通信协议:通过指定tcp://,明确告知驱动程序使用TCP/IP协议进行连接。这在需要明确区分连接方式时非常有用。例如,如果数据库服务器在本地,并且你想通过Unix域套接字(socket)连接而不是TCP/IP,可以使用socket://
    2. 灵活性和兼容性:使用标准的URL格式,可以灵活地切换不同的协议和地址,适应不同的部署环境和需求。

    不使用tcp://会怎样?

    如果你省略tcp://,通常默认会使用TCP/IP协议,但明确指定协议更为严谨,特别是在配置和调试数据库连接时。某些驱动程序和配置环境可能要求明确指定协议,以避免歧义或连接错误。

    其他协议示例

    • socket://:用于通过Unix域套接字连接到MySQL数据库(仅适用于Unix/Linux系统)。
    std::unique_ptr conn(driver->connect("socket:///path/to/socket", "username", "password"));
    
    • 普通连接(不指定协议):有些情况下可以省略协议前缀,依赖默认设置。
    std::unique_ptr conn(driver->connect("127.0.0.1:3306", "username", "password"));
    

    省略协议前缀通常也会使用TCP/IP协议,但明确指定协议更加严谨和可读。

    连接到具体的数据库

    使用创建的连接对象的 setSchema 方法选择具体的数据库。

    conn1->setSchema("test_db1");
    

    注意每个连接都是独立的,可以连接到不同的数据库实例或同一数据库实例下的不同数据库。

    创建SQL语句

    在C++的apisql语句分为PreparedStatement和不带参数的Statement,他们两者是有一定差别的

    Statement

    Statement 对象主要用于执行静态的、不带参数的 SQL 语句,例如 SELECTINSERTUPDATEDELETE。它适合用来执行那些不需要动态参数的简单 SQL 语句,其中的值是固定的,不会根据不同的输入而改变。Statement 对象的使用可以简化代码,但它不如 PreparedStatement 安全,因为不提供防止 SQL 注入的保护。

    std::unique_ptr stmt(conn->createStatement());
    std::unique_ptr res(stmt->executeQuery("SELECT id, name FROM test_table"));
    

    在上面的demo中我们发现使用 Statement 对象时,执行 SQL 查询和获取结果是一步完成的。你需要在调用 executeQueryexecuteUpdate 等方法时传入 SQL 语句,并且方法会立即执行该语句并返回结果。

    PreparedStatement

    PreparedStatement主要用于参数化查询重复执行相同查询执行批量操作 等场景

    // 创建 PreparedStatement 对象,并绑定 SQL 语句
    std::unique_ptr pstmt(conn->prepareStatement("SELECT id, name FROM test_table WHERE id = ?"));
    
    // 第一次设置参数并执行查询
    pstmt->setInt(1, 1); // 第一个参数位置,值为1
    std::unique_ptr res1(pstmt->executeQuery());
    

    PreparedStatement给人的感觉是像是封装了一个函数然后通过用一些set…函数经行‘传参’改变这个语句中的占位符中的字母,实现多种查询,每次查询是将占位符经行改变,而不是重新输入一个SQL语句。这样的函数有

    setInt(n, 1):
    设置第n个占位符(?)为整数值1。
    setString(n, "Alice"):
    设置第n个占位符(?)为字符串值"Alice"。
    setInt(n, 25):
    设置第n个占位符(?)为整数值25。
    setDouble(n, 50000.50):
    设置第n个占位符(?)为双精度浮点数值50000.50。
    setBoolean(n, true):
    设置第n个占位符(?)为布尔值true。
    

    执行时机

    • 当调用 executeQueryexecuteUpdateexecute 方法时,SQL 语句被发送到数据库服务器并实际执行。
    • executeQuery 用于 SELECT 语句,返回一个 ResultSet 对象用于遍历查询结果。
    • executeUpdate 用于 INSERTUPDATEDELETE 等语句,返回受影响的行数。
    • execute 是一个通用方法,可以执行任何 SQL 语句,并需要根据返回结果进一步处理。

    处理结果

    上面我们提到在执行sql语句时会用sql::ResultSet 类型将结果封存,所以处理结果的过程,就是遍历sql::ResultSet获取值的过程。

    以下是一些处理结果集的基本操作:

    1. 遍历结果集

    通过 next() 方法遍历结果集中的每一行:

    while (res->next()) {
        int id = res->getInt("id");
        std::string name = res->getString("name");
        std::cout << "ID: " << id << ", Name: " << name << std::endl;
    }
    

    可以看到->next()在单个方法调用中合并了“移动到下一个元素”和“检查是否存在更多元素”这两个操作。这种设计使得遍历结果集变得简单和高效。

    2. 获取列值

    通过列名或列索引来获取列值:

    int id = res->getInt("id"); // 使用列名
    std::string name = res->getString("name");
    
    int id = res->getInt(1); // 使用列索引(从 1 开始)
    std::string name = res->getString(2);
    
    3. 检查结果集是否为空

    在遍历之前可以检查结果集是否为空:

    if (!res->next()) {
        std::cout << "No data found." << std::endl;
    } else {
        // 重置游标到第一行
        res->beforeFirst();
        while (res->next()) {
            int id = res->getInt("id");
            std::string name = res->getString("name");
            std::cout << "ID: " << id << ", Name: " << name << std::endl;
        }
    }
    
  • 相关阅读:
    掌握 Scikit-Learn: Python 中的机器学习库入门
    单片机开发——宠物自动饮水器方案
    WPS/word 表格跨行如何续表、和表的名称
    [附源码]计算机毕业设计springboot基于Vuejs的中国名茶销售平台
    Flask使用Nacos作为服务的配置中心
    AutoAnimate - 无需任何配置,一行代码自动为元素添加优雅的过渡动画,可以搭配 Vue / React 和 Sevelt 使用
    C++使用PIMPL机制优化代码结构,降低耦合,提高编译速度
    电气工程的标准是什么
    SpringBoot 整合Mybatis
    Auto-GPT测评:自信、努力、不合格
  • 原文地址:https://blog.csdn.net/m0_73495584/article/details/139291318