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

    • 一、创建驱动程序实例
    • 二、连接服务器
      • 为什么使用`tcp://`
      • 不使用`tcp://`会怎样?
      • 其他协议示例
      • 连接到具体的数据库
    • 创建SQL语句
      • Statement
      • `PreparedStatement`
      • 执行时机
    • 处理结果
        • 1. 遍历结果集
        • 2. 获取列值
        • 3. 检查结果集是否为空

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

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

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

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

#include <mysql_driver.h>
#include <mysql_connection.h>
#include <cppconn/statement.h>
#include <cppconn/resultset.h>
#include <cppconn/exception.h>
#include <iostream>

int main() {
    try {
        // 创建驱动程序实例
        sql::mysql::MySQL_Driver* driver = sql::mysql::get_mysql_driver_instance();

        // 通过驱动程序创建连接
        std::unique_ptr<sql::Connection> conn(driver->connect("tcp://127.0.0.1:3306", "username", "password"));

        // 连接到具体的数据库
        conn->setSchema("test_db");

        // 创建语句对象
        std::unique_ptr<sql::Statement> stmt(conn->createStatement());

        // 执行查询并获取结果集
        std::unique_ptr<sql::ResultSet> 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<sql::Connection> 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<sql::Connection> conn(driver->connect("socket:///path/to/socket", "username", "password"));
  • 普通连接(不指定协议):有些情况下可以省略协议前缀,依赖默认设置。
std::unique_ptr<sql::Connection> 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<sql::Statement> stmt(conn->createStatement());
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery("SELECT id, name FROM test_table"));

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

PreparedStatement

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

// 创建 PreparedStatement 对象,并绑定 SQL 语句
std::unique_ptr<sql::PreparedStatement> pstmt(conn->prepareStatement("SELECT id, name FROM test_table WHERE id = ?"));

// 第一次设置参数并执行查询
pstmt->setInt(1, 1); // 第一个参数位置,值为1
std::unique_ptr<sql::ResultSet> 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;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/659180.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode热题100 Day1——双指针

双指针 移动零11. 盛最多水的容器 移动零 思路&#xff1a; 双指针i&#xff0c;j&#xff0c;j指针遍历数组&#xff0c;i指针存放非0元素。遍历结束后&#xff0c;i指针及其后面的一定是0,就再将空出来的位置设置为0 移动零 class Solution {public void moveZeroes(int[] …

PPT忘记保存?教你如何轻松恢复

在日常办公中PPT文件作为主流文档格式&#xff0c;承载着我们大量的工作成果。然而当不小心误点了“不保存”按钮&#xff0c;或是遭遇软件崩溃等意外情况导致文档丢失时&#xff0c;文件内容是否还能够能恢复&#xff0c;往往成为我们最关心的问题。本文将为您提供五大免费且实…

Centos安装nvm管理node版本(让安装变得可控又快捷)

Centos安装nvm管理node版本&#xff08;让安装变得可控又快捷&#xff09; 没有找到一个对的人&#xff0c;有可能是你没有走对那条路&#xff0c;也有可能是你没有睁开眼睛&#xff0c;而更大的可能是&#xff0c;你根本就没有出发去找。 安装说明 nvm安装目录&#xff1a;/us…

vue项目出现多次ElMessage

问题&#xff1a; 解决方法&#xff1a; let message null if (message null) { message ElMessage.error(“登录过期,请重新登录”); } 最终效果&#xff1a;只出现一个弹框

N进制计数器【02】

大容量N进制计数器 集成计数器容量的扩展 集成计数器级联扩展容量 【例1】由两片 74LS161 级联组成 256 进制&#xff08;8位二进制&#xff09;同步加法计数器 【解】级联时&#xff0c;外加时钟信号同时接到各片计数器的时钟输入端&#xff0c;用前级计数器的进位输出 C…

蓝硕硬盘数据恢复方法详解:安全、高效的解决方案

在数字化时代&#xff0c;硬盘作为我们存储和传输数据的重要载体&#xff0c;其数据安全性显得尤为重要。然而&#xff0c;无论是由于误操作、硬件故障还是病毒攻击&#xff0c;硬盘数据丢失的风险始终存在。蓝硕硬盘作为市场上的一款知名产品&#xff0c;其数据恢复同样是我们…

突破空间限制,这些远程控制软件为父母送上“手把手”的教导,解决异地办公难题,出差无忧

在现代社会&#xff0c;科技的飞速发展带来了诸多便利&#xff0c;但同时也给一些年长的父母们带来了困扰。 当父母们面对智能手机电脑等高科技产品时&#xff0c;他们往往感到无所适从&#xff0c;而子女们忙于工作、学习或其他原因&#xff0c;常常无法时刻陪伴在父母身边&a…

【Unity iOS打包】Library not loaded: ‘@rpath/AdjustSdk.framework/AdjustSdk‘

Unity打包iOS&#xff0c;XCode运行App黑屏卡死&#xff0c;报错&#xff1a; dyld[8412]: Library not loaded: rpath/AdjustSdk.framework/AdjustSdkReferenced from: /private/var/containers/Bundle/Application/C019F943-138F-4B33-AAC1-F18453F942D9/AnimalsBAMBAM.app/…

手写promise、call、apply、debounce、throttle 等

1、promise 1.1 实现resolve和reject class MyPromise {// 构造方法constructor(executor) {// 初始化值this.initValue()// 初始化this指向this.initBind()try {// 执行传进来的函数executor(this.resolve, this.reject)} catch (e) {// 捕捉到错误直接执行rejectthis.rejec…

Gartner发布评估威胁情报计划有效性指南:评估威胁情报有效性的四个步骤

许多组织都在努力实施 TI 并评估其价值。安全和风险管理领导者必须使用优先情报要求来评估其 TI 计划的有效性&#xff0c;并根据其组织战略完善该计划。 主要发现 尽管许多组织已将威胁情报 (TI) 纳入其安全计划&#xff0c;但他们很难评估其性能、成熟度以及在相关产品和服务…

再次疾呼:一稿多投是作者的合法权利!

近日&#xff0c;有作者反应在收到拒稿邮件之后另投他刊&#xff0c;竟然被判定一稿多投。 出版社解释说现在大多数期刊都是使用投稿系统收稿&#xff0c;如果被退稿后马上又投稿其他期刊&#xff0c;由于时间距离太近&#xff0c;仍然会被系统判定为一稿多投的。核心期刊编辑…

深入探索C++继承机制:从概念到实践的全面指南

目录 继承的概念及定义 继承的概念 继承的定义 定义格式 继承方式和访问限定符 继承基类成员访问方式的变化 默认继承方式 基类和派生类对象赋值转换 继承中的作用域 派生类的默认成员函数 继承与友元 继承与静态成员 继承的方式 菱形虚拟继承 菱形虚拟继承原理 继承…

「盘点」JetBrains IDEs v2024.1新功能一览,更智能的开发体验!

JetBrains IDEs日前正式发布了v2024.1版本&#xff0c;此版本中最大的亮点就是带来了AI赋能的全行代码补全&#xff0c;同时在最新的IDEs中重做了终端、拥有更强大的代码编辑和导航功能、更智能的代码分析和提示、更优化的性能、更丰富的插件和集成等。总的来说&#xff0c;Jet…

淘宝API探秘:一键获取店铺所有商品的魔法之旅

在数字时代的今天&#xff0c;数据已经成为了商业世界中的魔法石。而对于淘宝店主或者那些想要深入探索淘宝数据的人来说&#xff0c;淘宝API就像是打开阿里巴巴宝藏库的钥匙。今天&#xff0c;我们就来一起探索如何使用淘宝API&#xff0c;特别是如何获取店铺所有商品的接口&a…

为WPF的Grid添加网格边框线

在WPF中使用Grid绘制表格的时候&#xff0c;如果元素较多、排列复杂的话&#xff0c;界面会看起来很糟糕&#xff0c;没有层次&#xff0c;这时用网格或边框线分割各元素&#xff08;标签或单元格&#xff09;将会是页面看起来整齐有条理。 默认没有边框线的如下图所示&#xf…

Java 循环嵌套深度揭秘:挑战极限与性能优化

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 探索Java的调用栈极限 在Java中&#xff0c;方法调用是通过栈&#xff08;Stack&#xff09;这种数据结构来实现的。每当一个方法被调用时&#xff0c;一个新的栈帧&#xff08;Stack Frame&#xff09;会被创建并压…

React 中的 Fiber 架构

React Fiber 介绍 React Fiber 是 React 的一种重写和改进的核心算法&#xff0c;用于实现更细粒度的更新和高效的调度。它是 React 16 版本中的一个重要更新&#xff0c;使得 React 能够更好地处理复杂和高频的用户交互。以下是对 React Fiber 的详细介绍&#xff1a; 为什么…

便民社区信息小程序源码系统 功能强大 带生活电商+求职招聘功能 带完整的安装代码包以及搭建教程

系统概述 便民社区信息小程序源码系统是一款集多种功能于一身的综合性平台。它旨在为用户提供便捷的生活服务&#xff0c;满足社区居民的各种需求。无论是购物、求职还是获取社区信息&#xff0c;都能在这个平台上得到满足。该系统采用先进的技术架构&#xff0c;确保系统的稳…

【python 进阶】 绘图

1. 将多个柱状绘制在一个图中 import seaborn as sns import matplotlib.pyplot as plt import numpy as np import pandas as pd# 创建示例数据 categories [A, B, C, D, E] values1 np.random.randint(1, 10, sizelen(categories)) values2 np.random.randint(1, 10, siz…

揭秘!编写高质量代码的关键:码农必知的黄金法则!

文章目录 一、保持代码的简洁与清晰二、遵循良好的命名规范三、注重代码的可读性四、利用抽象与封装五、遵循SOLID原则六、关注代码性能七、确保代码安全性《码农修行&#xff1a;编写优雅代码的32条法则》编辑推荐内容简介目录前言/序言 在编程的世界里&#xff0c;每一位码农…