MySQL Connector/C 通常被称为 MySQL C API,它提供了一组 C 语言的函数和数据结构,允许开发人员使用 C/C++ 语言来连接、查询和管理数据库。这个 API 允许开发人员直接在其 C/C++ 应用程序中使用兼容 MySQL 协议的数据库。
本文将介绍如何使用 MySQL Connector/C (libmysqlclient) 驱动和 TDSQL Boundless 数据库构建一个应用程序,实现创建表、插入数据和查询数据等基本操作。
前提条件
在安装使用 MySQL Connector/C (libmysqlclient) 前请确保设置了基本的数据库开发环境,要求如下:
GCC 版本为3.4.6及以上,推荐使用4.8.5版本。
CMake 版本为2.8.12及以上。
您已部署 TDSQL Boundless 数据库并创建了相应的数据库实例。
操作步骤
1. 获取 TDSQL Boundless 数据库连接信息。
2. 安装 MySQL Connector/C 驱动。
3. 编写应用程序。
4. 编译应用程序。
5. 运行应用程序。
步骤一:获取数据库连接串
mysql -h$host -P$port -u$user_name -p$password -D$database_name
参数说明:
参数 | 说明 |
$host | TDSQL Boundless 数据库连接 IP 地址或域名及端口(默认为 3306)。OLTP 类访问地址:对等节点 VIP 地址及端口号可登录控制台,通过实例详情页面,在基本信息区域,内网地址字段获取对等节点 VIP,内网端口字段获取对等节点 Port。 OLAP 类访问地址:列存节点 VIP 地址及端口号可登录控制台,通过实例详情页面,在实例架构图区域,Columnar Node 中查看对应的 VIP 和 Port 信息。 |
$port | |
$user_name | |
$password | |
$database_name | 需要访问的数据库名称。 |
注意:
连接数据库的用户需要拥有该数据库的
CREATE、INSERT、DROP 和 SELECT 权限。示例如下:
mysql -h192.168.1.100 -P3306 -utest_user -p****** -Dtest_db
步骤二:安装 MySQL Connector/C 驱动
根据您的操作系统选择相应的安装命令:
## 在 Ubuntu/Debian 系统上请使用下面的代码进行安装:sudo apt-get updatesudo apt-get install libmysqlclient-dev## 在 CentOS/RHEL 系统上请使用下面的代码进行安装:sudo yum install mysql-devel## 确认是否安装成功mysql_config --version
如果安装成功,
mysql_config --version 命令将输出已安装的 MySQL 客户端库版本号。步骤三:编写应用程序
打开你的文本编辑器,在文本编辑器中编辑示例
test.c 文件并保存,代码如下所示:#include <stdio.h>#include <stdlib.h>#include <mysql/mysql.h>int main() {MYSQL *conn = mysql_init(NULL); // 初始化 MySQL 连接if (conn == NULL) {fprintf(stderr, "mysql_init() failed\\n");return 1;}// 连接到 TDSQL Boundless 数据库服务器// 请将以下参数替换为实际的连接信息if (mysql_real_connect(conn, "host", "user", "passwd", "db", 3306, NULL, 0) == NULL) {fprintf(stderr, "mysql_real_connect() failed: %s\\n", mysql_error(conn));mysql_close(conn);return 1;}printf("成功连接到 TDSQL Boundless 数据库!\\n");// 设置字符集为 utf8mb4if (mysql_set_character_set(conn, "utf8mb4") != 0) {fprintf(stderr, "设置字符集失败: %s\\n", mysql_error(conn));}// 创建用户表if (mysql_query(conn, "CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT, name VARCHAR(255), email VARCHAR(255), PRIMARY KEY(id))") != 0) {fprintf(stderr, "创建表失败: %s\\n", mysql_error(conn));mysql_close(conn);return 1;}printf("表创建成功!\\n");// 插入数据if (mysql_query(conn, "INSERT INTO users (name, email) VALUES ('小明', 'xiaoming@example.com')") != 0) {fprintf(stderr, "插入数据失败: %s\\n", mysql_error(conn));mysql_close(conn);return 1;}printf("数据插入成功!\\n");// 查询数据if (mysql_query(conn, "SELECT * FROM users") == 0) {MYSQL_RES *result = mysql_store_result(conn);if (result != NULL) {int num_fields = mysql_num_fields(result);MYSQL_ROW row;printf("\\n查询结果:\\n");printf("----------------------------------------\\n");while ((row = mysql_fetch_row(result))) {for (int i = 0; i < num_fields; i++) {printf("%s ", row[i] ? row[i] : "NULL");}printf("\\n");}printf("----------------------------------------\\n");printf("共 %lu 条记录\\n", (unsigned long)mysql_num_rows(result));mysql_free_result(result);} else {fprintf(stderr, "获取结果集失败: %s\\n", mysql_error(conn));}} else {fprintf(stderr, "查询数据失败: %s\\n", mysql_error(conn));}mysql_close(conn); // 关闭连接printf("数据库连接已关闭。\\n");return 0;}
在 Linux 环境下,可以使用
vi test.c 或者 vim test.c 命令编辑 test.c 文件,修改文件中的数据库连接信息,确保与实际情况相符。test.c 文件中的数据库连接信息示例如下:// 这部分连接信息需要修改为你所获取到连接串的真实信息,示例如下if (mysql_real_connect(conn, "192.168.1.100", "test_user", "your_password", "test_db", 3306, NULL, 0) == NULL) {fprintf(stderr, "mysql_real_connect() failed: %s\\n", mysql_error(conn));mysql_close(conn);return 1;}
步骤四:编译应用程序
代码编辑完成后,可以通过如下命令进行编译。
gcc -o test test.c `mysql_config --cflags --libs`
或者手动指定头文件和库路径:
gcc -o test test.c -I/usr/include/mysql -L/usr/lib/mysql -lmysqlclient
编译成功后,会生成可执行文件
test。步骤五:运行应用程序
通过如下命令运行应用程序。
./test
输出结果如下,说明数据库连接成功,示例语句正确执行:
成功连接到 TDSQL Boundless 数据库!表创建成功!数据插入成功!查询结果:----------------------------------------1 小明 xiaoming@example.com----------------------------------------共 1 条记录测试表已清理。数据库连接已关闭。
更多示例
示例1:使用预处理语句插入数据
预处理语句可以有效防止 SQL 注入,并提高重复执行相同 SQL 语句的性能。
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <mysql/mysql.h>int main() {MYSQL *conn = mysql_init(NULL);MYSQL_STMT *stmt;MYSQL_BIND bind[2];char name[64] = "张三";char email[128] = "zhangsan@example.com";unsigned long name_length;unsigned long email_length;if (conn == NULL) {fprintf(stderr, "mysql_init() failed\\n");return 1;}// 连接到 TDSQL Boundless 数据库if (mysql_real_connect(conn, "host", "user", "passwd", "db", 3306, NULL, 0) == NULL) {fprintf(stderr, "mysql_real_connect() failed: %s\\n", mysql_error(conn));mysql_close(conn);return 1;}// 设置字符集mysql_set_character_set(conn, "utf8mb4");// 确保表存在mysql_query(conn, "CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT, name VARCHAR(255), email VARCHAR(255), PRIMARY KEY(id))");// 初始化预处理语句stmt = mysql_stmt_init(conn);if (stmt == NULL) {fprintf(stderr, "mysql_stmt_init() failed\\n");mysql_close(conn);return 1;}// 准备 SQL 语句const char *sql = "INSERT INTO users (name, email) VALUES (?, ?)";if (mysql_stmt_prepare(stmt, sql, strlen(sql))) {fprintf(stderr, "mysql_stmt_prepare() failed: %s\\n", mysql_stmt_error(stmt));mysql_stmt_close(stmt);mysql_close(conn);return 1;}// 绑定参数memset(bind, 0, sizeof(bind));// 参数 1: namename_length = strlen(name);bind[0].buffer_type = MYSQL_TYPE_STRING;bind[0].buffer = name;bind[0].buffer_length = sizeof(name);bind[0].length = &name_length;// 参数 2: emailemail_length = strlen(email);bind[1].buffer_type = MYSQL_TYPE_STRING;bind[1].buffer = email;bind[1].buffer_length = sizeof(email);bind[1].length = &email_length;if (mysql_stmt_bind_param(stmt, bind)) {fprintf(stderr, "mysql_stmt_bind_param() failed: %s\\n", mysql_stmt_error(stmt));mysql_stmt_close(stmt);mysql_close(conn);return 1;}// 执行预处理语句if (mysql_stmt_execute(stmt)) {fprintf(stderr, "mysql_stmt_execute() failed: %s\\n", mysql_stmt_error(stmt));mysql_stmt_close(stmt);mysql_close(conn);return 1;}printf("预处理语句执行成功!插入 ID: %lu\\n", (unsigned long)mysql_stmt_insert_id(stmt));mysql_stmt_close(stmt);mysql_close(conn);return 0;}
示例2:事务处理
事务可以确保一组数据库操作要么全部成功,要么全部失败。
#include <stdio.h>#include <stdlib.h>#include <mysql/mysql.h>int main() {MYSQL *conn = mysql_init(NULL);int success = 1;if (conn == NULL) {fprintf(stderr, "mysql_init() failed\\n");return 1;}// 连接到 TDSQL Boundless 数据库if (mysql_real_connect(conn, "host", "user", "passwd", "db", 3306, NULL, 0) == NULL) {fprintf(stderr, "mysql_real_connect() failed: %s\\n", mysql_error(conn));mysql_close(conn);return 1;}mysql_set_character_set(conn, "utf8mb4");// 关闭自动提交,开始事务if (mysql_autocommit(conn, 0)) {fprintf(stderr, "关闭自动提交失败: %s\\n", mysql_error(conn));mysql_close(conn);return 1;}printf("事务开始...\\n");// 执行第一条 SQLif (mysql_query(conn, "UPDATE accounts SET balance = balance - 100 WHERE user_id = 1")) {fprintf(stderr, "SQL 1 执行失败: %s\\n", mysql_error(conn));success = 0;}// 执行第二条 SQLif (success && mysql_query(conn, "UPDATE accounts SET balance = balance + 100 WHERE user_id = 2")) {fprintf(stderr, "SQL 2 执行失败: %s\\n", mysql_error(conn));success = 0;}if (success) {// 提交事务if (mysql_commit(conn)) {fprintf(stderr, "提交事务失败: %s\\n", mysql_error(conn));} else {printf("事务提交成功!\\n");}} else {// 回滚事务if (mysql_rollback(conn)) {fprintf(stderr, "回滚事务失败: %s\\n", mysql_error(conn));} else {printf("事务已回滚\\n");}}// 恢复自动提交mysql_autocommit(conn, 1);mysql_close(conn);return 0;}
示例3:批量查询与结果遍历
#include <stdio.h>#include <stdlib.h>#include <mysql/mysql.h>int main() {MYSQL *conn = mysql_init(NULL);MYSQL_RES *result;MYSQL_ROW row;MYSQL_FIELD *fields;unsigned int num_fields;unsigned int i;if (conn == NULL) {fprintf(stderr, "mysql_init() failed\\n");return 1;}// 连接到 TDSQL Boundless 数据库if (mysql_real_connect(conn, "host", "user", "passwd", "db", 3306, NULL, 0) == NULL) {fprintf(stderr, "mysql_real_connect() failed: %s\\n", mysql_error(conn));mysql_close(conn);return 1;}mysql_set_character_set(conn, "utf8mb4");// 执行查询if (mysql_query(conn, "SELECT id, name, email FROM users ORDER BY id DESC LIMIT 100")) {fprintf(stderr, "查询失败: %s\\n", mysql_error(conn));mysql_close(conn);return 1;}// 获取结果集result = mysql_store_result(conn);if (result == NULL) {fprintf(stderr, "获取结果集失败: %s\\n", mysql_error(conn));mysql_close(conn);return 1;}// 获取字段数量和字段信息num_fields = mysql_num_fields(result);fields = mysql_fetch_fields(result);// 打印列名printf("\\n");for (i = 0; i < num_fields; i++) {printf("%-20s", fields[i].name);}printf("\\n");// 打印分隔线for (i = 0; i < num_fields; i++) {printf("--------------------");}printf("\\n");// 遍历结果集while ((row = mysql_fetch_row(result)) != NULL) {for (i = 0; i < num_fields; i++) {printf("%-20s", row[i] ? row[i] : "NULL");}printf("\\n");}printf("\\n共 %lu 行数据\\n", (unsigned long)mysql_num_rows(result));// 释放结果集mysql_free_result(result);mysql_close(conn);return 0;}
常用 API 参考
函数 | 说明 |
mysql_init() | 初始化 MySQL 连接句柄 |
mysql_real_connect() | 连接到数据库服务器 |
mysql_close() | 关闭数据库连接 |
mysql_query() | 执行 SQL 语句 |
mysql_store_result() | 获取完整结果集 |
mysql_use_result() | 逐行获取结果集 |
mysql_fetch_row() | 获取下一行数据 |
mysql_num_rows() | 获取结果集行数 |
mysql_num_fields() | 获取结果集列数 |
mysql_fetch_fields() | 获取字段信息 |
mysql_affected_rows() | 获取受影响的行数 |
mysql_free_result() | 释放结果集 |
mysql_error() | 获取错误信息 |
mysql_errno() | 获取错误码 |
mysql_set_character_set() | 设置字符集 |
mysql_autocommit() | 设置自动提交模式 |
mysql_commit() | 提交事务 |
mysql_rollback() | 回滚事务 |
mysql_stmt_init() | 初始化预处理语句 |
mysql_stmt_prepare() | 准备预处理语句 |
mysql_stmt_bind_param() | 绑定预处理语句参数 |
mysql_stmt_execute() | 执行预处理语句 |
mysql_stmt_close() | 关闭预处理语句 |
常见问题
1. 编译时找不到 mysql.h 头文件
问题:
fatal error: mysql/mysql.h: No such file or directory解决方案:确保已安装 MySQL 开发库,并使用正确的编译参数:
# 安装开发库sudo apt-get install libmysqlclient-dev # Ubuntu/Debiansudo yum install mysql-devel # CentOS/RHEL# 使用 mysql_config 获取正确的编译参数gcc -o test test.c `mysql_config --cflags --libs`
2. 运行时找不到 libmysqlclient.so
问题:
error while loading shared libraries: libmysqlclient.so解决方案:
# 方法1:添加库路径到环境变量export LD_LIBRARY_PATH=/usr/lib/mysql:$LD_LIBRARY_PATH# 方法2:更新动态链接库缓存sudo ldconfig
3. 中文显示乱码
问题:查询结果中文显示为乱码
解决方案:连接后设置字符集为
utf8mb4。mysql_set_character_set(conn, "utf8mb4");
4. 连接超时
问题:连接数据库时超时
解决方案:在连接前设置超时参数:
unsigned int timeout = 30;mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);