MySQL Connector C++ 9.2 中文文档
第六章 Connector/C++ 源码包中的示例目录
Connector/C++ 的源码发行版包含一个 examples 目录,其中提供了以下核心类的使用示例:
- 6.1
Connection - 6.2
Driver - 6.3
PreparedStatement - 6.4
ResultSet - 6.5
ResultSetMetaData - 6.6
Statement
示例涵盖的主题:
- 使用
Driver类连接 MySQL - 通过普通语句(
Statement)创建表、插入数据、查询数据 - 通过预处理语句(
PreparedStatement)创建表、插入数据、查询数据 - 绕过预处理语句限制的技巧
- 访问结果集元数据(
ResultSetMetaData)
注意:
- 本文档中的部分示例为代码片段,非完整程序。完整示例请查看 Connector/C++ 安装目录下的
examples目录。 - 测试前需阅读
examples目录中的README文件,并编辑examples.h文件配置连接信息(如主机、用户名、密码),然后通过make命令重新编译代码。
示例程序列表
connect.cpp- 功能:创建数据库连接、插入数据、处理异常。
connection_meta_schemaobj.cpp- 功能:获取连接对象的元数据,如数据库/表列表、MySQL 版本、驱动版本。
debug_output.cpp- 功能:启用或禁用 Connector/C++ 的调试协议。
exceptions.cpp- 功能:深入解析驱动抛出的异常类型及错误信息获取方法。
prepared_statements.cpp- 功能:执行预处理语句,包括处理 MySQL 服务器不支持的预处理 SQL 语句的示例。
resultset.cpp- 功能:使用游标遍历结果集并获取数据。
resultset_meta.cpp- 功能:获取结果集的元数据,如列数量、列类型。
resultset_types.cpp- 功能:展示从元数据方法返回的结果集(更偏向测试而非示例)。
standalone_example.cpp- 功能:一个未集成到常规 CMake 构建中的独立简单程序。
statements.cpp- 功能:通过普通语句(非预处理语句)执行 SQL。
cpp_trace_analyzer.cpp- 功能:过滤调试跟踪输出的示例。注意:此脚本不受官方支持,详细用法请查看代码内注释。
6.1 连接到 MySQL
要建立与 MySQL 服务器的连接,需通过 sql::mysql::MySQL_Driver 对象获取 sql::Connection 实例。sql::mysql::MySQL_Driver 对象由 sql::mysql::get_mysql_driver_instance() 返回。
sql::mysql::MySQL_Driver *driver;
sql::Connection *con;
driver = sql::mysql::get_mysql_driver_instance();
con = driver->connect("tcp://127.0.0.1:3306", "user", "password");
delete con;
注意事项:
- 资源管理:确保在不再需要
sql::Connection对象(即con)时立即释放它。但不要显式释放驱动对象driver,Connector/C++ 会自动管理并释放。 - 线程安全:
get_mysql_driver_instance()内部调用get_driver_instance(),此方法非线程安全。若需在多线程中调用,需通过互斥锁(mutex)防止并发访问。
连接状态检查与重连:
提供sql::Connection::isValid()用于连接状态管理,检查连接是否有效(存活)。此方法通过轻量级查询SELECT 或数据库驱动的内部机制快速验证连接。sql::Connection::reconnect():若连接已断开,尝试重新建立连接。适用于网络波动或服务端主动断开的情况。
连接参数扩展:
更多连接选项(如超时配置、字符集、SSL 等)可参考《Connector/C++ 连接选项》章节。例如:
// 示例:设置连接超时为 5 秒
con->setClientOption("OPT_CONNECT_TIMEOUT", "5");
关键提示:
-
保活机制:若需长时间保持空闲连接,建议通过定时执行简单查询(如
SELECT)或启用连接池的心跳检测(如HikariCP的connectionTestQuery)避免服务端因wait_timeout断开连接。 -
错误处理:连接失败时,捕获
sql::SQLException异常以获取详细错误信息(如错误码、状态码及错误描述)。
扩展阅读:
- 连接池实现:单例模式下的连接管理需谨慎处理资源释放,避免内存泄漏(参考
shared_ptr或连接池库如HikariCP)。 - 预处理语句:建议优先使用
PreparedStatement提升性能并防止 SQL 注入。
6.2 执行简单查询
在 Connector/C++ 中,可通过 sql::Statement 的以下方法执行简单查询:
execute():适用于不返回结果集(如 DDL 语句)或可能返回多个结果集/更新计数的复杂 SQL。executeQuery():专用于 SELECT 查询,返回sql::ResultSet对象以遍历数据。executeUpdate():用于 INSERT、UPDATE、DELETE 等修改数据的操作,返回受影响的行数。
代码示例 :
sql::mysql::MySQL_Driver *driver;
sql::Connection *con;
sql::Statement *stmt;
// 初始化驱动并建立连接
driver = sql::mysql::get_mysql_driver_instance();
con = driver->connect(
"tcp://127.0.0.1:3306", "user", "password");
// 创建 Statement 对象
stmt = con->createStatement();
// 执行 SQL 语句
stmt->execute("USE " EXAMPLE_DB); // 选择数据库
stmt->execute("DROP TABLE IF EXISTS test"); // 删除表(DDL)
stmt->execute("CREATE TABLE test(id INT, label CHAR(1))"); // 创建表(DDL)
stmt->executeUpdate(
"INSERT INTO test(id, label) VALUES (1, 'a')"); // 插入数据(DML)
// 显式释放资源
delete stmt;
delete con;
关键注意事项:
- 资源释放:必须显式调用
delete释放sql::Statement和sql::Connection对象,避免内存泄漏。 - SQL方法选择:
- 执行 查询(如
SELECT)时优先使用executeQuery()。 - 执行 数据修改(如
INSERT)时使用executeUpdate(),其返回值表示受影响的行数。 execute()主要用于动态 SQL 或处理未知类型的语句。
- 执行 查询(如
- 线程安全:
get_mysql_driver_instance()非线程安全,多线程环境需加锁。
补充说明
- DDL 与 DML 区别:
- DDL(数据定义语言)如
CREATE TABLE,不返回结果集,建议使用execute()。 - DML(数据操作语言)如
INSERT,建议使用executeUpdate()以获取操作影响的行数。
- DDL(数据定义语言)如
- 错误处理:实际开发中应捕获
sql::SQLException异常,以处理连接或执行失败的情况。
6.3 获取结果集
(简单)语句(Statement)和预处理语句(PreparedStatement)获取结果集的 API 是相同的。若查询返回单一结果集,可使用 sql::Statement::executeQuery() 或 sql::PreparedStatement::executeQuery() 执行查询,这两个方法均返回 sql::ResultSet 对象。默认情况下,Connector/C++ 会在客户端缓冲所有结果集以支持游标操作。
// ...
sql::Connection *con;
sql::Statement *stmt;
sql::ResultSet *res;
// ...
// 创建 Statement 对象
stmt = con->createStatement();
// 执行查询并获取结果集
res = stmt->executeQuery(
"SELECT id, label FROM test ORDER BY id ASC");
// 遍历结果集
while (res->next()) {
// 方式 1:使用数字索引(从 1 开始)
cout << "id = " << res->getInt(1); // getInt(1) 获取第一列的值
// 方式 2:使用列名(推荐)
cout << ", label = '" << res->getString("label") << "'" << endl;
}
// 显式释放资源
delete res;
delete stmt;
delete con;
关键注意事项:
- 列索引从 1 开始:例如,第一列通过
res->getInt(1)访问。 - 资源释放:必须显式调用
delete释放sql::Statement、sql::Connection和sql::ResultSet对象,避免内存泄漏。 - 游标使用:完整示例可参考 Connector/C++ 安装包中的示例代码。
扩展说明:
- 结果集遍历:
ResultSet::next()将游标移动到下一行,返回true表示仍有数据,false表示遍历完成。- 数据类型转换:根据列类型选择对应方法(如
getInt()、getString())。若类型不匹配会抛出异常。
- 客户端缓冲:
- 默认行为会将所有结果集加载到客户端内存,支持双向游标(可回滚)。
- 若处理大数据集,建议通过
setResultSetType()设置流式结果集(sql::ResultSet::TYPE_FORWARD_ONLY)减少内存占用。
示例:流式结果集配置
stmt = con->createStatement();
stmt->setResultSetType(sql::ResultSet::TYPE_FORWARD_ONLY); // 设置为仅向前游标
res = stmt->executeQuery("SELECT * FROM large_table");
错误处理:实际开发中需捕获 sql::SQLException 异常,例如:
try {
res = stmt->executeQuery("SELECT ...");
// ...
} catch (sql::SQLException &e) {
cerr << "SQL Error: " << e.what() << ", Code: " << e.getErrorCode() << endl;
}
6.4 使用预处理语句
如果您对 MySQL 中的预处理语句不熟悉,可以查看 examples/prepared_statement.cpp 文件中的源代码注释和解释。
sql::PreparedStatement 通过将 SQL 查询传递给 sql::Connection::prepareStatement() 来创建。由于 sql::PreparedStatement 继承自 sql::Statement,因此一旦您学会了如何使用普通语句(sql::Statement),您会对该 API 感到熟悉。例如,获取结果的语法是相同的。
// ...
sql::Connection *con;
sql::PreparedStatement *prep_stmt;
// ...
// 创建预处理语句
prep_stmt = con->prepareStatement("INSERT INTO test(id, label) VALUES (?, ?)");
// 设置参数并执行
prep_stmt->setInt(1, 1);
prep_stmt->setString(2, "a");
prep_stmt->execute();
prep_stmt->setInt(1, 2);
prep_stmt->setString(2, "b");
prep_stmt->execute();
// 显式释放资源
delete prep_stmt;
delete con;
注意事项:
- 资源释放:必须显式调用
delete释放sql::PreparedStatement和sql::Connection对象,避免内存泄漏。 - 参数化查询:预处理语句通过占位符
?实现参数化查询,防止 SQL 注入并提高执行效率。 - 线程安全:
get_mysql_driver_instance()非线程安全,多线程环境需加锁。
扩展说明:
- 参数设置:
- 使用
setInt()、setString()等方法为占位符赋值,索引从 1 开始。 - 支持多种数据类型(如
setDouble()、setDate()等)。
- 使用
- 执行方法:
execute():用于执行不返回结果集的 SQL 语句(如 DDL)。executeQuery():用于执行返回结果集的查询(如SELECT)。executeUpdate():用于执行修改数据的操作(如INSERT、UPDATE),返回受影响的行数。
6.5 完整示例1
/* Standard C++ includes */
#include <stdlib.h>
#include <iostream>
/*
Include directly the different
headers from cppconn/ and mysql_driver.h + mysql_util.h
(and mysql_connection.h). This will reduce your build time!
*/
#include "mysql_connection.h"
#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
using namespace std;
int main(void)
{
cout << endl;
cout << "Running 'SELECT 'Hello World!' »
AS _message'..." << endl;
try {
sql::Driver *driver;
sql::Connection *con;
sql::Statement *stmt;
sql::ResultSet *res;
/* Create a connection */
driver = get_driver_instance();
con = driver->connect(
"tcp://127.0.0.1:3306", "root", "root");
/* Connect to the MySQL test database */
con->setSchema("test");
stmt = con->createStatement();
res = stmt->executeQuery(
"SELECT 'Hello World!' AS _message");
while (res->next()) {
cout << "\t... MySQL replies: ";
/* Access column data by alias or column name */
cout << res->getString("_message") << endl;
cout << "\t... MySQL says it again: ";
/* Access column data by numeric offset, 1 is the first column */
cout << res->getString(1) << endl;
}
delete res;
delete stmt;
delete con;
} catch (sql::SQLException &e) {
cout << "# ERR: SQLException in " << __FILE__;
cout << "(" << __FUNCTION__ << ") on line " »
<< __LINE__ << endl;
cout << "# ERR: " << e.what();
cout << " (MySQL error code: " << e.getErrorCode();
cout << ", SQLState: " << e.getSQLState() << " )" << endl;
}
cout << endl;
return EXIT_SUCCESS;
}
6.6 完整示例2
/* Standard C++ includes */
#include <stdlib.h>
#include <iostream>
/*
Include directly the different
headers from cppconn/ and mysql_driver.h + mysql_util.h
(and mysql_connection.h). This will reduce your build time!
*/
#include "mysql_connection.h"
#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>
using namespace std;
int main(void)
{
cout << endl;
cout << "Let's have MySQL count from 10 to 1..." << endl;
try {
sql::Driver *driver;
sql::Connection *con;
sql::Statement *stmt;
sql::ResultSet *res;
sql::PreparedStatement *pstmt;
/* Create a connection */
driver = get_driver_instance();
con = driver->connect(
"tcp://127.0.0.1:3306", "root", "root");
/* Connect to the MySQL test database */
con->setSchema("test");
stmt = con->createStatement();
stmt->execute("DROP TABLE IF EXISTS test");
stmt->execute("CREATE TABLE test(id INT)");
delete stmt;
/* '?' is the supported placeholder syntax */
pstmt = con->prepareStatement(
"INSERT INTO test(id) VALUES (?)");
for (int i = 1; i <= 10; i++) {
pstmt->setInt(1, i);
pstmt->executeUpdate();
}
delete pstmt;
/* Select in ascending order */
pstmt = con->prepareStatement(
"SELECT id FROM test ORDER BY id ASC");
res = pstmt->executeQuery();
/* Fetch in reverse = descending order! */
res->afterLast();
while (res->previous())
cout << "\t... MySQL counts: " << res->getInt("id") << endl;
delete res;
delete pstmt;
delete con;
} catch (sql::SQLException &e) {
cout << "# ERR: SQLException in " << __FILE__;
cout << "(" << __FUNCTION__ << ") on line " »
<< __LINE__ << endl;
cout << "# ERR: " << e.what();
cout << " (MySQL error code: " << e.getErrorCode();
cout << ", SQLState: " << e.getSQLState() << »
" )" << endl;
}
cout << endl;
return EXIT_SUCCESS;
}
6.7 连接到密码过期的账户
MySQL 支持密码过期功能,如《密码管理》章节所述。如果客户端应用程序使用密码已过期的账户连接 MySQL 用户,客户端功能将受到限制,直到重置账户密码为止;更多信息请参阅《服务器处理过期密码》章节。
Connector/C++ 应用程序可以通过 OPT_CAN_HANDLE_EXPIRED_PASSWORDS 和 preInit 连接选项重置过期密码,这些选项在《Connector/C++ 连接选项》第 10 章中有详细描述。启用 OPT_CAN_HANDLE_EXPIRED_PASSWORDS 选项,并将设置密码的 ALTER USER 语句作为 preInit 选项的值:
opts["OPT_CAN_HANDLE_EXPIRED_PASSWORDS"] = true;
opts["preInit"] = sql::SQLString("ALTER USER 'user' IDENTIFIED BY 'new-pwd';");
ALTER USER 语句在连接后立即设置新密码。使用这些选项连接后,新密码应已生效,会话可以正常使用。任何新会话都必须使用新密码,但不再需要 OPT_CAN_HANDLE_EXPIRED_PASSWORDS 或 preInit 选项。
扩展说明 :
-
密码过期机制:
- MySQL 允许通过
ALTER USER语句手动设置密码过期,或通过全局变量default_password_lifetime自动管理密码过期。 - 密码过期后,客户端只能执行重置密码的操作,其他操作将被限制。
- MySQL 允许通过
-
Connector/C++ 处理过期密码:
- 通过
OPT_CAN_HANDLE_EXPIRED_PASSWORDS和preInit选项,Connector/C++ 可以在连接时自动重置密码,避免手动干预。
- 通过
-
注意事项:
- 重置密码后,确保所有新会话使用新密码。
- 避免将过期密码重置为当前值,建议设置一个全新的密码以提高安全性。