什么是 Connector/C++

MySQL Connector/C++ 是 MySQL 官方提供的 C++ 客户端库。

它支持两套主要 API:

X DevAPI

  • 面向文档数据库和关系表操作的现代 API
  • 基于 MySQL X Protocol
  • 支持:
    • Schema / Collection / Table
    • 文档 CRUD
    • SQL 执行
    • 事务
    • 预处理风格参数绑定
  • 推荐用于 MySQL 8.x 和 X Plugin 环境

头文件常见:

1
#include <mysqlx/xdevapi.h>

经典 JDBC/SQL 风格 API(Legacy API)

更接近传统 SQL 驱动使用方式

安装 Connector/C++

Linux

以通过:

  • 官方 MySQL 安装包
  • 系统包管理器
  • 手动下载安装

有时仓库包较旧:

1
2
sudo apt update
sudo apt install libmysqlcppconn-dev

注意:有些发行版仓库里的版本可能不是最新的 8.x

macOS

如果 Homebrew 提供可用包,可尝试:

1
brew install mysql-connector-c++

如果没有或版本不合适,通常去 MySQL 官方下载更稳。

Windows

通常从 MySQL 官方下载安装包:

  • 安装后会得到:
  • 头文件
  • .lib
  • .dll

然后在 Visual Studio 工程里配置:

  • Include 目录
  • Library 目录
  • 链接库

基本开发流程

使用 Connector/C++ 的典型流程:

  1. 加载驱动
  2. 建立连接
  3. 创建语句对象
  4. 执行 SQL
  5. 读取结果集
  6. 关闭对象 / 自动释放
  7. 处理异常

环境要求

服务端要求

要使用 X DevAPI,你的 MySQL 服务端需要支持 X Protocol,通常意味着:

  • MySQL 8.0+
  • 启用了 X Plugin
  • 默认端口通常是:
    • 经典协议:3306
    • X Protocol:33060

可在 MySQL 中检查:

1
SHOW VARIABLES LIKE 'mysqlx%';

如果能看到 mysqlx_port 等配置,说明 X Plugin 通常已启用。

客户端要求

需要安装:

  • MySQL Connector/C++ 8.x
  • 头文件
  • 动态库 / 静态库

常用头文件通常是:

1
#include <mysqlx/xdevapi.h>

基本概念

X DevAPI 里最重要的对象层次通常是:

  • Session:与 MySQL 服务器建立 X 协议连接
  • Schema:对应数据库
  • Collection:文档集合,类似 NoSQL collection
  • Table:关系表
  • Result / RowResult / DocResult:查询结果集
  • SqlStatement:执行原生 SQL

命名空间:

1
mysqlx::

链接数据库

最简单的链接方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <mysqlx/xdevapi.h>

int main() {
try {
mysqlx::Session sess("localhost", 33060, "root", "password");
std::cout << "Connected successfully!" << std::endl;
}
catch (const mysqlx::Error &err) {
std::cerr << "Error: " << err << std::endl;
}
catch (std::exception &ex) {
std::cerr << "STD Exception: " << ex.what() << std::endl;
}
}

使用URL链接

1
mysqlx::Session sess("mysqlx://root:password@localhost:33060");

也可以指定schema

1
mysqlx::Session sess("mysqlx://root:password@localhost:33060/test");

链接选项方式

1
2
3
4
5
6
mysqlx::SessionSettings settings(
"localhost", 33060,
"root", "password"
);

mysqlx::Session sess(settings);

更完整的:

1
2
3
4
5
6
7
8
mysqlx::SessionSettings settings(
mysqlx::SessionOption::HOST, "localhost",
mysqlx::SessionOption::PORT, 33060,
mysqlx::SessionOption::USER, "root",
mysqlx::SessionOption::PWD, "password"
);

mysqlx::Session sess(settings);

获取schema

Schema 对应 MySQL 中的数据库。

1
2
mysqlx::Session sess("localhost", 33060, "root", "password");
mysqlx::Schema db = sess.getSchema("test");

如果schema不存在:

1
mysqlx::Schema db = sess.createSchema("test_db", true);

第二个参数为 true 时,一般表示“如果不存在则创建 / 容错处理”,具体行为依版本而异,生产中建议明确检查。

删除schema:

1
sess.dropSchema("test_db");

使用 Collection(文档模型)

Collection 是 X DevAPI 中处理 JSON 文档的主要对象。

创建Collection

1
2
mysqlx::Schema db = sess.getSchema("test");
mysqlx::Collection coll = db.createCollection("my_collection", true);

获取已存在的Collection:

1
mysqlx::Collection coll = db.getCollection("my_collection");

删除Collection:

1
db.dropCollection("my_collection");

插入文档

插入单个文档

1
coll.add(R"({ "name": "Alice", "age": 25 })").execute();

插入多个文档

1
2
3
4
coll.add(
R"({ "name": "Bob", "age": 30 })",
R"({ "name": "Carol", "age": 28 })"
).execute();

使用 mysqlx::Dbdoc

1
2
mysqlx::DbDoc doc = mysqlx::DbDoc(R"({ "name": "David", "age": 40 })");
coll.add(doc).execute();

查询文档

查询全部

1
2
3
4
5
mysqlx::DocResult res = coll.find().execute();

for (mysqlx::DbDoc doc : res) {
std::cout << doc << std::endl;
}

带条件查询

1
2
3
4
mysqlx::DocResult res = coll.find("age > 26").execute();
for (mysqlx::DbDoc doc : res) {
std::cout << doc << std::endl;
}

排序和限制

1
2
3
4
mysqlx::DocResult res = coll.find("age >= 25")
.sort("age DESC")
.limit(10)
.execute();

参数绑定

1
2
3
4
mysqlx::DocResult res = coll.find("age > :age and name = :name")
.bind("age", 20)
.bind("name", "Alice")
.execute();

更新文档

1
2
3
coll.modify("name = 'Alice'")
.set("age", 26)
.execute();

可链式调用多个修改:

1
2
3
coll.modify("age < 18")
.set("status", "minor")
.execute();

还可使用:

  • set()
  • unset()
  • patch()
  • arrayAppend()
  • arrayInsert()

例如:

1
2
3
coll.modify("name = 'Bob'")
.unset("unused_field")
.execute();

删除文档

1
coll.remove("age < 18").execute();

删除全部文档(慎用):

1
coll.remove("true").execute();

获取自动生成的文档 ID

如果文档没有 _id,MySQL 可能会自动生成。

1
2
3
4
5
6
mysqlx::Result res = coll.add(R"({ "name": "Eve" })").execute();

std::vector<std::string> ids = res.getGeneratedIds();
for (const auto &id : ids) {
std::cout << "Generated ID: " << id << std::endl;
}

使用 Table(关系模型)

X DevAPI 不仅能操作文档,也能操作普通关系表。

获取表对象

1
2
mysqlx::Schema db = sess.getSchema("test");
mysqlx::Table tab = db.getTable("users");

插入数据

1
2
3
4
tab.insert("name", "age")
.values("Alice", 25)
.values("Bob", 30)
.execute();

查询数据

简单查询

1
2
3
4
5
6
7
8
mysqlx::RowResult res = tab.select("id", "name", "age").execute();

for (mysqlx::Row row : res) {
std::cout << "id=" << row[0]
<< ", name=" << row[1]
<< ", age=" << row[2]
<< std::endl;
}

带条件

1
2
3
4
5
mysqlx::RowResult res = tab.select("id", "name")
.where("age > 20")
.orderBy("age DESC")
.limit(5)
.execute();

参数绑定

1
2
3
4
mysqlx::RowResult res = tab.select("id", "name")
.where("age > :age")
.bind("age", 18)
.execute();

更新数据

1
2
3
4
tab.update()
.set("age", 35)
.where("name = 'Bob'")
.execute();

参数绑定方式:

1
2
3
4
5
tab.update()
.set("age", 40)
.where("name = :name")
.bind("name", "Alice")
.execute();

删除数据

1
2
3
tab.remove()
.where("age < 18")
.execute();

读取单行字段

1
2
3
4
5
6
7
8
mysqlx::RowResult res = tab.select("id", "name").where("id = 1").execute();
mysqlx::Row row = res.fetchOne();

if (!row.isNull()) {
int id = row[0];
std::string name = static_cast<std::string>(row[1]);
std::cout << id << ", " << name << std::endl;
}

注意:不同版本中 Row / Value 的转换写法可能略有区别。
常见方式包括显式转换或使用 .get<T>() 风格接口(取决于具体版本 API)。

执行原生 SQL

如果某些操作 X DevAPI 不方便,可以直接执行 SQL。

执行 SQL

1
2
3
mysqlx::SqlResult res = sess.sql("SELECT id, name FROM users WHERE age > ?")
.bind(20)
.execute();

遍历结果:

1
2
3
4
mysqlx::Row row;
while ((row = res.fetchOne())) {
std::cout << row[0] << ", " << row[1] << std::endl;
}

DDL 示例

1
2
3
4
5
6
sess.sql(
"CREATE TABLE IF NOT EXISTS users ("
"id INT PRIMARY KEY AUTO_INCREMENT, "
"name VARCHAR(50), "
"age INT)"
).execute();

事务处理

X DevAPI 支持事务。

基本事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try {
sess.startTransaction();

mysqlx::Schema db = sess.getSchema("test");
mysqlx::Table tab = db.getTable("users");

tab.insert("name", "age").values("Tom", 22).execute();
tab.insert("name", "age").values("Jerry", 23).execute();

sess.commit();
}
catch (const mysqlx::Error &err) {
std::cerr << "Transaction error: " << err << std::endl;
sess.rollback();
}

常见结果对象

mysqlx::Result

通常用于增删改等非查询操作:

可获取:

  • 受影响行数
  • 自动生成 ID
  • 警告数

示例:

1
2
3
4
5
mysqlx::Result res = tab.insert("name", "age")
.values("Alice", 25)
.execute();

std::cout << "Affected items: " << res.getAffectedItemsCount() << std::endl;

mysqlx::RowResult

用于表查询和 SQL 查询返回行数据。

1
mysqlx::RowResult res = tab.select("id", "name").execute();

mysqlx::DocResult

用于集合文档查询。

1
mysqlx::DocResult res = coll.find().execute();

索引操作(Collection)

Collection 可以创建索引,提高文档查询效率。

创建索引

1
2
3
4
5
6
7
coll.createIndex("idx_age", R"(
{
"fields": [
{ "field": "$.age", "type": "INT", "required": false }
]
}
)").execute();

唯一索引示例

1
2
3
4
5
6
7
8
coll.createIndex("idx_email_unique", R"(
{
"fields": [
{ "field": "$.email", "type": "TEXT(100)" }
],
"unique": true
}
)").execute();

删除索引:

1
coll.dropIndex("idx_age");

注意:Collection 索引定义是 JSON 格式,字段路径常写为 $.fieldName

错误处理

X DevAPI 常见异常类型:

  • mysqlx::Error
  • 标准异常 std::exception

推荐统一写法:

1
2
3
4
5
6
7
8
9
10
11
12
try {
mysqlx::Session sess("localhost", 33060, "root", "password");
}
catch (const mysqlx::Error &err) {
std::cerr << "MySQL Error: " << err.what() << std::endl;
}
catch (const std::exception &ex) {
std::cerr << "Exception: " << ex.what() << std::endl;
}
catch (...) {
std::cerr << "Unknown error" << std::endl;
}

完整示例:文档操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <iostream>
#include <mysqlx/xdevapi.h>

int main() {
try {
mysqlx::Session sess("localhost", 33060, "root", "password");

mysqlx::Schema db = sess.getSchema("test");
mysqlx::Collection coll = db.createCollection("people", true);

coll.add(R"({ "name": "Alice", "age": 25 })").execute();
coll.add(R"({ "name": "Bob", "age": 30 })").execute();

mysqlx::DocResult res = coll.find("age >= :age")
.bind("age", 26)
.sort("age DESC")
.execute();

for (mysqlx::DbDoc doc : res) {
std::cout << doc << std::endl;
}

coll.modify("name = 'Alice'")
.set("city", "Shanghai")
.execute();

coll.remove("name = 'Bob'").execute();
}
catch (const mysqlx::Error &err) {
std::cerr << "Error: " << err << std::endl;
return 1;
}
return 0;
}

完整示例:表操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <mysqlx/xdevapi.h>

int main() {
try {
mysqlx::Session sess("localhost", 33060, "root", "password");
mysqlx::Schema db = sess.getSchema("test");

sess.sql(
"CREATE TABLE IF NOT EXISTS users ("
"id INT PRIMARY KEY AUTO_INCREMENT, "
"name VARCHAR(50), "
"age INT)"
).execute();

mysqlx::Table tab = db.getTable("users");

tab.insert("name", "age")
.values("Alice", 25)
.values("Bob", 30)
.execute();

mysqlx::RowResult res = tab.select("id", "name", "age")
.where("age > :age")
.bind("age", 20)
.orderBy("age DESC")
.execute();

for (mysqlx::Row row : res) {
std::cout << row[0] << ", " << row[1] << ", " << row[2] << std::endl;
}

tab.update()
.set("age", 35)
.where("name = :name")
.bind("name", "Bob")
.execute();

tab.remove()
.where("name = :name")
.bind("name", "Alice")
.execute();
}
catch (const mysqlx::Error &err) {
std::cerr << "MySQL Error: " << err << std::endl;
return 1;
}
return 0;
}

编译方法

g++ 示例

具体路径按你的安装位置调整:

1
2
3
4
g++ -std=c++17 main.cpp -o app \
-I/usr/include/mysql-cppconn-8 \
-L/usr/lib \
-lmysqlcppconn8

有些环境库名可能不同,请以实际安装内容为准。

CMake 示例

1
2
3
4
5
6
7
8
9
cmake_minimum_required(VERSION 3.10)
project(mysqlx_demo)

set(CMAKE_CXX_STANDARD 17)

add_executable(mysqlx_demo main.cpp)

target_include_directories(mysqlx_demo PRIVATE /usr/include/mysql-cppconn-8)
target_link_libraries(mysqlx_demo PRIVATE mysqlcppconn8)

常见注意事项

X DevAPI 走的是 33060,不是 3306

很多人连接失败就是因为端口写成了传统 MySQL 端口。

要确认 MySQL X Plugin 已开启

否则即使 MySQL 正常运行,也无法用 X DevAPI 连接。

条件表达式不是完整 SQL

例如:

  • find("age > 20")
  • where("name = :name")

这些表达式是 X DevAPI 支持的语法子集,不完全等同于随便写一整条 SQL。

JSON 文档字段路径要注意

例如创建索引时常使用:

1
"field": "$.age"

优先使用参数绑定

不要自己拼接用户输入,避免注入问题:

1
2
3
4
tab.select("id", "name")
.where("name = :name")
.bind("name", user_input)
.execute();

自动生成 _id

Collection 中通常每个文档有 _id
如果你不自己提供,系统可能自动生成。

常用 API 速查

连接与 schema

1
2
mysqlx::Session sess("localhost", 33060, "root", "password");
mysqlx::Schema db = sess.getSchema("test");

collection

1
2
3
4
5
auto coll = db.getCollection("c1");
coll.add(R"({"name":"A"})").execute();
coll.find("name = 'A'").execute();
coll.modify("name = 'A'").set("age", 20).execute();
coll.remove("name = 'A'").execute();

table

1
2
3
4
5
auto tab = db.getTable("users");
tab.insert("name", "age").values("A", 20).execute();
tab.select("id", "name").where("age > 18").execute();
tab.update().set("age", 21).where("name='A'").execute();
tab.remove().where("name='A'").execute();

SQL

1
sess.sql("SELECT * FROM users WHERE age > ?").bind(18).execute();

事务

1
2
3
sess.startTransaction();
sess.commit();
sess.rollback();