MySQL C/C++ 的 API

MySQL 提供了一个用于 C/C++ 的 API,称为 MySQL Connector/C。该 API 允许通过 C/C++ 程序与 MySQL 数据库进行交互。

函数名称 参数 返回值 描述
mysql_init MYSQL *mysql MYSQL * 初始化一个 MySQL 对象,用于连接 MySQL 服务器。
mysql_real_connect MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag MYSQL * 连接到 MySQL 数据库服务器。
mysql_close MYSQL *mysql void 关闭与 MySQL 服务器的连接。
mysql_query MYSQL *mysql, const char *query int 执行一条 SQL 查询。返回 0 表示成功,非 0 表示失败。
mysql_store_result MYSQL *mysql MYSQL_RES * 检索完整的结果集。返回指向结果集的指针,或者失败时返回 NULL。
mysql_free_result MYSQL_RES *result void 释放结果集的内存。
mysql_fetch_row MYSQL_RES *result MYSQL_ROW 从结果集中获取下一行。返回表示行的数组,或者没有更多数据时返回 NULL。
mysql_affected_rows MYSQL *mysql my_ulonglong 返回最近执行的 SQL 语句影响的行数。
mysql_num_rows MYSQL_RES *result my_ulonglong 返回结果集中行的数量。
mysql_num_fields MYSQL_RES *result unsigned int 返回结果集中的字段数。
mysql_error MYSQL *mysql const char * 返回最近一次 MySQL 操作的错误消息字符串。
mysql_real_escape_string MYSQL *mysql, char *to, const char *from, unsigned long length unsigned long 转义字符串中的特殊字符,使其可以安全地用于 SQL 语句中。
mysql_commit MYSQL *mysql int 提交当前事务。返回 0 表示成功,非 0 表示失败。
mysql_rollback MYSQL *mysql int 回滚当前事务。返回 0 表示成功,非 0 表示失败。
mysql_set_character_set MYSQL *mysql, const char *charset int 设置当前连接使用的字符集。返回 0 表示成功,非 0 表示失败。
mysql_autocommit MYSQL *mysql, my_bool mode int 开启或关闭自动提交功能。传入 0 表示关闭,1 表示开启。
mysql_stmt_init MYSQL *mysql MYSQL_STMT * 初始化一个预处理语句句柄。
mysql_stmt_prepare MYSQL_STMT *stmt, const char *query, unsigned long length int 预处理一个 SQL 查询。返回 0 表示成功,非 0 表示失败。
mysql_stmt_bind_param MYSQL_STMT *stmt, MYSQL_BIND *bind int 绑定参数到预处理语句。
mysql_stmt_bind_result MYSQL_STMT *stmt, MYSQL_BIND *bind int 绑定结果变量到预处理语句。
mysql_stmt_execute MYSQL_STMT *stmt int 执行一个预处理语句。返回 0 表示成功,非 0 表示失败。
mysql_stmt_fetch MYSQL_STMT *stmt int 获取执行结果中的下一行。返回 0 表示成功,非 0 表示失败或无更多行。
mysql_stmt_close MYSQL_STMT *stmt int 关闭一个预处理语句句柄。返回 0 表示成功,非 0 表示失败。
mysql_stmt_result_metadata MYSQL_STMT *stmt MYSQL_RES * 获取一个预处理语句执行后返回结果的元数据。返回结果集结构指针,或者 NULL 表示没有元数据。
mysql_stmt_num_rows MYSQL_STMT *stmt my_ulonglong 获取结果集中返回的行数。
mysql_stmt_store_result MYSQL_STMT *stmt int 将完整的结果集存储在客户端内存中。返回 0 表示成功,非 0 表示失败。
mysql_stmt_free_result MYSQL_STMT *stmt void 释放预处理语句的结果集。
mysql_stmt_reset MYSQL_STMT *stmt int 重置预处理语句,使其可以重新执行。返回 0 表示成功,非 0 表示失败。

说明

  • 返回值为 int 类型的函数:通常 0 表示成功,非 0 表示失败。
  • 返回值为指针的函数 :通常返回一个指向对象的指针,NULL 表示失败。
  • MYSQL_BIND 是一个用于绑定参数或结果的结构,通常用于预处理语句(mysql_stmt)相关的函数中。

1. 初始化 MySQL 连接

函数 mysql_init 用于初始化 MySQL 连接。

#include <mysql/mysql.h>

MYSQL *mysql_init(MYSQL *mysql);

  • 参数:

    • mysql:指向 MYSQL 结构体的指针,可以为 NULL
  • 返回值:

    • 成功:返回一个 MYSQL 结构体指针。
    • 失败:返回 NULL
  • 示例:

  • MYSQL *conn;

    conn = mysql_init(NULL);

    if (conn == NULL) {

    fprintf(stderr, "mysql_init() failed\n");

    exit(EXIT_FAILURE);

    }

2. 连接数据库

使用 mysql_real_connect 函数建立与数据库的连接。

MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag);

  • 参数:

    • mysql: 已初始化的 MYSQL 结构体指针。
    • host: 数据库服务器地址(如 "localhost" 或 IP 地址)。
    • user: 数据库用户名。
    • passwd: 数据库用户密码。
    • db: 需要连接的数据库名。
    • port: MySQL 服务器的端口号,通常为 3306。
    • unix_socket: 本地 Unix Socket,若未使用可设为 NULL
    • client_flag: 客户端标志位,通常为 0。
  • 返回值:

    • 成功:返回 MYSQL 结构体指针。
    • 失败:返回 NULL
  • 示例:

  • if (mysql_real_connect(conn, "localhost", "user", "password", "testdb", 0, NULL, 0) == NULL) {

    fprintf(stderr, "mysql_real_connect() failed\n");

    mysql_close(conn);

    exit(EXIT_FAILURE);

    }

3. 执行 SQL 查询

函数 mysql_query 用于执行 SQL 语句。

int mysql_query(MYSQL *mysql, const char *query);

  • 参数:

    • mysql: 已连接的 MYSQL 结构体指针。
    • query: 要执行的 SQL 查询字符串。
  • 返回值:

    • 成功:返回 0。
    • 失败:返回非 0 值。
  • 示例:

  • if (mysql_query(conn, "CREATE TABLE test (id INT, name VARCHAR(20))")) {

    fprintf(stderr, "CREATE TABLE failed. Error: %s\n", mysql_error(conn));

    }

4. 获取查询结果

使用 mysql_store_result 获取查询的结果集,并使用 mysql_fetch_row 获取每一行的记录。

MYSQL_RES *mysql_store_result(MYSQL *mysql);

MYSQL_ROW mysql_fetch_row(MYSQL_RES *result);

  • 参数:

    • mysql_store_result: 返回查询结果的指针,若查询不返回数据则返回 NULL
    • mysql_fetch_row: 返回结果集中的下一行数据,若无更多数据返回 NULL
  • 示例:

  • if (mysql_query(conn, "SELECT id, name FROM test")) {

    fprintf(stderr, "SELECT failed. Error: %s\n", mysql_error(conn));

    }

    MYSQL_RES *result = mysql_store_result(conn);

    if (result == NULL) {

    fprintf(stderr, "mysql_store_result() failed. Error: %s\n", mysql_error(conn));

    }

    int num_fields = mysql_num_fields(result);

    MYSQL_ROW row;

    while ((row = mysql_fetch_row(result))) {

    for(int i = 0; i < num_fields; i++) {

    printf("%s ", row[i] ? row[i] : "NULL");

    }

    printf("\n");

    }

    mysql_free_result(result);

5. 关闭 MySQL 连接

使用 mysql_close 关闭与 MySQL 的连接。

void mysql_close(MYSQL *mysql);

  • 参数:

    • mysql: 连接的 MYSQL 结构体指针。
  • 示例:

  • mysql_close(conn);

6. 错误处理

通过 mysql_error 获取最近的错误信息。

const char *mysql_error(MYSQL *mysql);

  • 参数:

    • mysql: 连接的 MYSQL 结构体指针。
  • 返回值:

    • 返回一个 C 字符串,包含错误信息。
  • 示例:

  • fprintf(stderr, "Error: %s\n", mysql_error(conn));

完整示例

#include <mysql/mysql.h>

#include <stdio.h>

#include <stdlib.h>

int main() {

MYSQL *conn;

MYSQL_RES *res;

MYSQL_ROW row;

// 初始化 MySQL 连接

conn = mysql_init(NULL);

if (conn == NULL) {

fprintf(stderr, "mysql_init() failed\n");

exit(EXIT_FAILURE);

}

// 连接到数据库

if (mysql_real_connect(conn, "localhost", "user", "password", "testdb", 0, NULL, 0) == NULL) {

fprintf(stderr, "mysql_real_connect() failed\n");

mysql_close(conn);

exit(EXIT_FAILURE);

}

// 执行 SQL 查询

if (mysql_query(conn, "SELECT id, name FROM test")) {

fprintf(stderr, "SELECT failed. Error: %s\n", mysql_error(conn));

mysql_close(conn);

exit(EXIT_FAILURE);

}

// 获取结果集

res = mysql_store_result(conn);

if (res == NULL) {

fprintf(stderr, "mysql_store_result() failed. Error: %s\n", mysql_error(conn));

mysql_close(conn);

exit(EXIT_FAILURE);

}

// 输出查询结果

int num_fields = mysql_num_fields(res);

while ((row = mysql_fetch_row(res))) {

for(int i = 0; i < num_fields; i++) {

printf("%s ", row[i] ? row[i] : "NULL");

}

printf("\n");

}

// 释放结果集

mysql_free_result(res);

// 关闭 MySQL 连接

mysql_close(conn);

exit(EXIT_SUCCESS);

}

其他常用 API 函数

  • mysql_affected_rows: 获取查询影响的行数。
  • mysql_num_fields: 获取结果集中字段的数量。
  • mysql_field_count: 获取查询的字段数。
  • mysql_insert_id: 获取插入操作后生成的自增 ID。

7. 事务处理

MySQL 支持事务,可以使用 mysql_autocommitmysql_commitmysql_rollback 来手动管理事务。

自动提交模式

int mysql_autocommit(MYSQL *mysql, my_bool mode);

  • 参数:

    • mysql: 已连接的 MYSQL 结构体指针。
    • mode: 设置是否自动提交,1 为启用自动提交,0 为关闭自动提交。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
  • 示例:

  • mysql_autocommit(conn, 0); // 关闭自动提交

提交事务

int mysql_commit(MYSQL *mysql);

  • 参数:

    • mysql: 已连接的 MYSQL 结构体指针。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
  • 示例:

  • if (mysql_commit(conn)) {

    fprintf(stderr, "Commit failed. Error: %s\n", mysql_error(conn));

    }

回滚事务

int mysql_rollback(MYSQL *mysql);

  • 参数:

    • mysql: 已连接的 MYSQL 结构体指针。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
  • 示例:

  • if (mysql_rollback(conn)) {

    fprintf(stderr, "Rollback failed. Error: %s\n", mysql_error(conn));

    }

8. 预处理语句 (Prepared Statements)

预处理语句用于执行重复的 SQL 语句或提高安全性,避免 SQL 注入。主要函数有 mysql_stmt_initmysql_stmt_preparemysql_stmt_execute 等。

初始化预处理语句

MYSQL_STMT *mysql_stmt_init(MYSQL *mysql);

  • 参数 :
    • mysql: 已连接的 MYSQL 结构体指针。
  • 返回值 :
    • 成功返回 MYSQL_STMT 结构体指针,失败返回 NULL
准备 SQL 语句

int mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned long length);

  • 参数:

    • stmt: mysql_stmt_init 返回的预处理语句结构体指针。
    • query: SQL 查询语句。
    • length: SQL 查询语句的长度。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
执行预处理语句

int mysql_stmt_execute(MYSQL_STMT *stmt);

  • 参数:

    • stmt: 已准备的预处理语句结构体指针。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
  • 示例:

  • MYSQL_STMT *stmt;

    stmt = mysql_stmt_init(conn);

    if (!stmt) {

    fprintf(stderr, "mysql_stmt_init() failed\n");

    }

    const char *query = "INSERT INTO test (id, name) VALUES (?, ?)";

    if (mysql_stmt_prepare(stmt, query, strlen(query))) {

    fprintf(stderr, "mysql_stmt_prepare() failed. Error: %s\n", mysql_error(conn));

    }

    // 设置参数并执行预处理语句(略)

    if (mysql_stmt_execute(stmt)) {

    fprintf(stderr, "mysql_stmt_execute() failed. Error: %s\n", mysql_error(conn));

    }

    mysql_stmt_close(stmt);

9. 获取结果字段的元数据

使用 mysql_fetch_fields 可以获取结果集中各字段的元数据。

MYSQL_FIELD *mysql_fetch_fields(MYSQL_RES *result);

  • 参数:

    • result: 结果集指针。
  • 返回值:

    • 返回字段数组。
  • 示例:

  • MYSQL_RES *result = mysql_store_result(conn);

    MYSQL_FIELD *fields = mysql_fetch_fields(result);

    for (int i = 0; i < mysql_num_fields(result); i++) {

    printf("Field %d: %s\n", i, fields[i].name);

    }

10. 大数据集处理

使用 mysql_use_result 可以处理大数据集,避免一次性将所有数据加载到内存中。

MYSQL_RES *mysql_use_result(MYSQL *mysql);

  • 参数:

    • mysql: 连接的 MYSQL 结构体指针。
  • 返回值:

    • 成功返回结果集指针,失败返回 NULL
  • 示例:

  • MYSQL_RES *result = mysql_use_result(conn);

    MYSQL_ROW row;

    while ((row = mysql_fetch_row(result))) {

    // 处理每一行数据

    }

    mysql_free_result(result);

11. 多线程支持

MySQL C API 是线程安全的,但在多线程环境下,需要使用以下两个函数:

初始化多线程环境

int mysql_thread_init(void);

结束多线程环境
void mysql_thread_end(void);

在多线程程序开始时,调用 mysql_thread_init,在结束时调用 mysql_thread_end,确保多线程环境的正确使用。

12. 字符集操作

MySQL 提供了一些函数来处理字符集,确保数据库与应用程序之间的数据正确编码。

设置客户端字符集

int mysql_set_character_set(MYSQL *mysql, const char *csname);

  • 参数:

    • mysql: 连接的 MYSQL 结构体指针。
    • csname: 字符集名称(如 "utf8")。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
  • 示例:

  • if (mysql_set_character_set(conn, "utf8")) {

    fprintf(stderr, "Failed to set character set to utf8. Error: %s\n", mysql_error(conn));

    }

获取当前客户端字符集

const char *mysql_character_set_name(MYSQL *mysql);

  • 返回值:

    • 返回当前客户端字符集的名称。
  • 示例:

  • printf("Client character set: %s\n", mysql_character_set_name(conn));

13. 错误处理及诊断

MySQL 提供了丰富的错误处理和诊断函数,如 mysql_errnomysql_sqlstatemysql_warning_count

获取错误代码

unsigned int mysql_errno(MYSQL *mysql);

获取 SQL 状态码

const char *mysql_sqlstate(MYSQL *mysql);

获取警告数量

unsigned int mysql_warning_count(MYSQL *mysql);

demo:

printf("MySQL Error No: %u\n", mysql_errno(conn));

printf("MySQL SQLState: %s\n", mysql_sqlstate(conn));

printf("MySQL Warning Count: %u\n", mysql_warning_count(conn));

14. 断线重连

MySQL 提供了自动重连功能,通过以下代码可以启用断线重连:

my_bool reconnect = 1;

mysql_options(conn, MYSQL_OPT_RECONNECT, &reconnect);

15. 关闭 MySQL 连接

别忘了每次操作结束后关闭连接:

mysql_close(conn);

16. 预处理语句 (Prepared Statements) 高级操作

预处理语句 (MYSQL_STMT) 是处理动态查询、重复执行 SQL 操作和防止 SQL 注入的重要机制。通过预处理语句,可以提高性能并增强 SQL 的安全性。

初始化预处理语句

MYSQL_STMT *mysql_stmt_init(MYSQL *mysql);

  • 参数:

    • stmt: 初始化的 MYSQL_STMT 结构体指针。
    • query: SQL 查询语句。
    • length: SQL 查询语句的长度(以字节为单位)。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
绑定参数

在执行预处理语句前,必须将参数绑定到 SQL 语句中。使用 mysql_stmt_bind_param 来绑定参数。

int mysql_stmt_bind_param(MYSQL_STMT *stmt, MYSQL_BIND *bind);

  • 参数:

    • stmt: 预处理语句的结构体指针。
    • bind: 指向包含要绑定参数的 MYSQL_BIND 结构体数组。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
MYSQL_BIND 结构体

MYSQL_BIND 用于定义输入或输出参数。其成员包括:

  • buffer: 指向数据的指针。
  • buffer_type: 参数的数据类型,如 MYSQL_TYPE_LONGMYSQL_TYPE_STRING
  • is_null: 指向标识参数是否为 NULL 的指针。
  • length: 指向保存数据长度的指针。
示例:绑定参数

MYSQL_STMT *stmt;
MYSQL_BIND bind[2];
int id;
char name[20];

stmt = mysql_stmt_init(conn);
const char *query = "INSERT INTO test (id, name) VALUES (?, ?)";
mysql_stmt_prepare(stmt, query, strlen(query));

memset(bind, 0, sizeof(bind));

// 绑定 ID 参数
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = (char *)&id;

// 绑定 Name 参数
bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = name;
bind[1].buffer_length = sizeof(name);

// 绑定参数
mysql_stmt_bind_param(stmt, bind);

// 设置参数值
id = 1;
strcpy(name, "Alice");

// 执行预处理语句
mysql_stmt_execute(stmt);

mysql_stmt_close(stmt);

执行预处理语句

int mysql_stmt_execute(MYSQL_STMT *stmt);

  • 参数:

    • stmt: 已准备的预处理语句结构体指针。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
绑定结果

查询预处理语句需要绑定结果变量到 MYSQL_BIND 结构体中。

int mysql_stmt_bind_result(MYSQL_STMT *stmt, MYSQL_BIND *bind);

  • 参数:

    • stmt: 预处理语句的结构体指针。
    • bind: 指向包含结果数据的 MYSQL_BIND 结构体数组。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
示例:绑定查询结果

MYSQL_STMT *stmt;
MYSQL_BIND bind[2];
int id;
char name[20];

stmt = mysql_stmt_init(conn);
const char *query = "SELECT id, name FROM test WHERE id = ?";
mysql_stmt_prepare(stmt, query, strlen(query));

// 绑定参数
MYSQL_BIND param[1];
memset(param, 0, sizeof(param));
param[0].buffer_type = MYSQL_TYPE_LONG;
param[0].buffer = (char *)&id;
mysql_stmt_bind_param(stmt, param);

id = 1;
mysql_stmt_execute(stmt);

// 绑定结果
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = (char *)&id;

bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = name;
bind[1].buffer_length = sizeof(name);

mysql_stmt_bind_result(stmt, bind);

// 获取结果
while (mysql_stmt_fetch(stmt) == 0) {
printf("ID: %d, Name: %s\n", id, name);
}

mysql_stmt_close(stmt);

获取结果行

int mysql_stmt_fetch(MYSQL_STMT *stmt);

  • 参数:

    • stmt: 预处理语句的结构体指针。
  • 返回值:

    • 0:成功获取一行结果。
    • MYSQL_NO_DATA:没有更多数据。
    • 其他非零值:发生错误。
释放预处理语句

int mysql_stmt_close(MYSQL_STMT *stmt);

  • 参数:

    • stmt: 预处理语句的结构体指针。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。

17. 获取预处理语句的元数据

可以通过 mysql_stmt_param_metadatamysql_stmt_result_metadata 函数获取预处理语句的参数和结果元数据。

获取参数元数据

MYSQL_RES *mysql_stmt_param_metadata(MYSQL_STMT *stmt);

  • 参数 :
    • stmt: 预处理语句的结构体指针。
  • 返回值 :
    • 返回结果集,包含预处理语句参数的元数据。
获取结果元数据
复制代码
MYSQL_RES *mysql_stmt_result_metadata(MYSQL_STMT *stmt);
  • 参数:

    • stmt: 预处理语句的结构体指针。
  • 返回值:

    • 返回结果集,包含预处理语句结果集的元数据。
  • 示例:

  • MYSQL_RES *result_metadata = mysql_stmt_result_metadata(stmt);

    if (result_metadata) {

    int num_fields = mysql_num_fields(result_metadata);

    printf("Number of result fields: %d\n", num_fields);

    mysql_free_result(result_metadata);

    }

18. 事务和预处理语句的结合

通过预处理语句和事务的结合,可以确保多条 SQL 语句要么全部成功,要么全部失败。

示例:事务与预处理语句结合

mysql_autocommit(conn, 0); // 关闭自动提交

MYSQL_STMT *stmt = mysql_stmt_init(conn);
const char *query = "INSERT INTO test (id, name) VALUES (?, ?)";
mysql_stmt_prepare(stmt, query, strlen(query));

MYSQL_BIND bind[2];
int id = 1;
char name[20] = "Alice";

// 绑定参数
memset(bind, 0, sizeof(bind));
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = (char *)&id;

bind[1].buffer_type = MYSQL_TYPE_STRING;
bind[1].buffer = name;
bind[1].buffer_length = sizeof(name);

mysql_stmt_bind_param(stmt, bind);

if (mysql_stmt_execute(stmt)) {
mysql_rollback(conn); // 执行失败,回滚
} else {
mysql_commit(conn); // 执行成功,提交事务
}

mysql_stmt_close(stmt);
mysql_autocommit(conn, 1); // 恢复自动提交

19. 批量插入

使用 mysql_stmt_execute 可以进行批量插入,通过修改绑定的数据,反复执行 mysql_stmt_execute

  • 示例:

MYSQL_STMT *stmt;

stmt = mysql_stmt_init(conn);

const char *query = "INSERT INTO test (id, name) VALUES (?, ?)";

mysql_stmt_prepare(stmt, query, strlen(query));

MYSQL_BIND bind[2];

int id;

char name[20];

memset(bind, 0, sizeof(bind));

// 绑定参数

bind[0].buffer_type = MYSQL_TYPE_LONG;

bind[0].buffer = (char *)&id;

bind[1].buffer_type = MYSQL_TYPE_STRING;

bind[1].buffer = name;

bind[1].buffer_length = sizeof(name);

mysql_stmt_bind_param(stmt, bind);

// 批量插入数据

for (int i = 1; i <= 10; i++) {

id = i;

sprintf(name, "Name%d", i);

mysql_stmt_execute(stmt);

}

mysql_stmt_close(stmt);

20. 存储过程

MySQL 支持通过预处理语句调用存储过程。

const char *query = "CALL procedure_name(?, ?)";

mysql_stmt_prepare(stmt, query, strlen(query));

通过绑定参数和执行来调用存储过程,和普通预处理语句非常类似。

21. 多结果集处理 (Multiple Result Sets)

在某些情况下(例如存储过程或执行多个查询),MySQL 返回多个结果集。MySQL C API 提供了一些函数来处理这些结果集。

检测是否有更多的结果集

int mysql_more_results(MYSQL *mysql);

  • 参数:

    • mysql: 已连接的 MYSQL 结构体指针。
  • 返回值:

    • 如果有更多的结果集返回非 0 值,否则返回 0。
移动到下一个结果集

int mysql_next_result(MYSQL *mysql);

  • 参数:

    • mysql: 已连接的 MYSQL 结构体指针。
  • 返回值:

    • 成功返回 0,出错返回非 0 值。
示例:处理多结果集

if (mysql_query(conn, "CALL my_procedure();")) {
fprintf(stderr, "Query failed: %s\n", mysql_error(conn));
}

do {
MYSQL_RES *result = mysql_store_result(conn);
if (result) {
// 处理当前结果集
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
printf("Row: %s\n", row[0]);
}
mysql_free_result(result);
}
} while (mysql_more_results(conn) && mysql_next_result(conn) == 0);

22. 多查询执行 (Multiple Queries)

MySQL 支持一次性执行多条 SQL 语句。通过 mysql_querymysql_real_query 执行包含多条 SQL 语句的查询,结果集可使用 mysql_store_result 获取。

int mysql_query(MYSQL *mysql, const char *query);

int mysql_real_query(MYSQL *mysql, const char *query, unsigned long length);

  • 参数:

    • query: 包含多条 SQL 语句的字符串。
  • 返回值:

    • 成功返回 0,失败返回非 0 值。
示例:多查询执行

const char *query = "SELECT * FROM table1; SELECT * FROM table2;";
if (mysql_query(conn, query)) {
fprintf(stderr, "Query failed: %s\n", mysql_error(conn));
}

do {
MYSQL_RES *result = mysql_store_result(conn);
if (result) {
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
printf("Row: %s\n", row[0]);
}
mysql_free_result(result);
}
} while (mysql_more_results(conn) && mysql_next_result(conn) == 0);

23. 大数据操作

对于非常大的数据集,可以使用流式读取或写入以避免内存不足。mysql_use_result 允许逐行处理结果集,而不是将整个结果集载入内存。

逐行获取大数据集

MYSQL_RES *mysql_use_result(MYSQL *mysql);

  • 返回值 :
    • 返回结果集指针,用于流式读取。
示例:逐行读取结果

MYSQL_RES *result = mysql_use_result(conn);
MYSQL_ROW row;
while ((row = mysql_fetch_row(result))) {
printf("Row: %s\n", row[0]);
}
mysql_free_result(result);

24. 异步查询

MySQL C API 的传统函数都是同步的。对于某些场景,异步查询可以提高性能。虽然原生 C API 不提供完全异步的查询接口,但可以通过分离连接的线程执行来实现异步操作。也可以使用 mysql_poll 来检查某些非阻塞操作的状态。

示例:异步查询模型

通常通过创建新线程执行查询,然后主线程处理其他任务,最终等待查询完成。

void *run_query(void *arg) {

MYSQL *conn = (MYSQL *)arg;

mysql_query(conn, "SELECT * FROM test");

// 处理查询结果

return NULL;

}

int main() {

MYSQL *conn = mysql_init(NULL);

mysql_real_connect(conn, "host", "user", "password", "database", 0, NULL, 0);

pthread_t thread;

pthread_create(&thread, NULL, run_query, conn);

// 主线程可以处理其他任务

// ...

pthread_join(thread, NULL);

mysql_close(conn);

return 0;

}

25. SSL 安全连接

MySQL C API 支持 SSL 加密连接,通过 mysql_ssl_set 函数可以配置 SSL 相关的参数。

设置 SSL 连接

int mysql_ssl_set(MYSQL *mysql,
const char *key,
const char *cert,
const char *ca,
const char *capath,
const char *cipher);

  • 参数:

    • key: 客户端私钥文件。
    • cert: 客户端证书文件。
    • ca: 受信任的 CA 文件。
    • capath: 受信任的 CA 文件路径。
    • cipher: SSL 加密算法。
  • 示例:

  • MYSQL *conn = mysql_init(NULL);

    mysql_ssl_set(conn, "client-key.pem", "client-cert.pem", "ca-cert.pem", NULL, NULL);

    mysql_real_connect(conn, "host", "user", "password", "database", 0, NULL, CLIENT_SSL);

26. 存储过程的输出参数处理

当调用存储过程并处理输出参数时,需使用预处理语句并绑定输出参数到结果集中。与普通预处理语句不同,输出参数需要绑定为 MYSQL_BINDis_nulllength 字段。

示例:存储过程输出参数

MYSQL_STMT *stmt = mysql_stmt_init(conn);
const char *query = "CALL my_procedure(?, ?, ?)";
mysql_stmt_prepare(stmt, query, strlen(query));

MYSQL_BIND bind[3];
memset(bind, 0, sizeof(bind));

// 绑定输入参数
int input = 5;
bind[0].buffer_type = MYSQL_TYPE_LONG;
bind[0].buffer = (char *)&input;

// 绑定输出参数
int output;
unsigned long length;
my_bool is_null;
bind[1].buffer_type = MYSQL_TYPE_LONG;
bind[1].buffer = (char *)&output;
bind[1].length = &length;
bind[1].is_null = &is_null;

mysql_stmt_bind_param(stmt, bind);
mysql_stmt_execute(stmt);

// 获取输出参数
mysql_stmt_fetch(stmt);
printf("Output: %d\n", output);

mysql_stmt_close(stmt);

27. 自定义错误处理和诊断

除了通过 mysql_error 获取错误信息,还可以使用 mysql_errnomysql_sqlstate 进一步诊断错误。

​​​​​​​

28. 自定义连接超时和重试机制

可以使用 mysql_options 设置连接超时、重试次数等高级选项。

设置连接超时

int timeout = 10; // 设置超时时间为 10 秒
mysql_options(conn, MYSQL_OPT_CONNECT_TIMEOUT, &timeout);

自动重连

my_bool reconnect = 1;

mysql_options(conn, MYSQL_OPT_RECONNECT, &reconnect);

练习项目:图书馆管理系统

项目设计:图书馆管理系统

项目简介:

我们将实现一个 图书馆管理系统,通过控制台界面与用户交互,支持对用户、图书、借阅信息的全面管理。系统还会包括一些高级功能,比如:

  • 用户角色权限管理(管理员和普通用户)
  • 预定与借阅图书的操作
  • 图书库存检查与批量入库
  • 日志记录(操作记录)
  • 数据持久化(本地数据库)
功能需求:
  1. 用户管理
    • 注册用户,分为管理员和普通用户,管理员具有更多权限。
  2. 图书管理
    • 添加图书、编辑图书信息、删除图书。
  3. 借阅与归还
    • 用户可以借阅或归还图书,管理员可以查看借阅记录。
  4. 图书预定
    • 如果图书已被借出,用户可以预定,待图书归还后自动通知。
  5. 库存管理
    • 管理员可以查看库存不足的图书,进行批量入库操作。
  6. 日志管理
    • 系统记录所有操作日志,供管理员查看。
数据库设计:

CREATE DATABASE library_management;

USE library_management;

CREATE TABLE users (

id INT AUTO_INCREMENT PRIMARY KEY,

username VARCHAR(50) NOT NULL,

password VARCHAR(100) NOT NULL,

role ENUM('admin', 'user') NOT NULL

);

CREATE TABLE books (

id INT AUTO_INCREMENT PRIMARY KEY,

title VARCHAR(100) NOT NULL,

author VARCHAR(100),

stock INT DEFAULT 0

);

CREATE TABLE borrow_records (

id INT AUTO_INCREMENT PRIMARY KEY,

user_id INT,

book_id INT,

borrow_date DATE,

return_date DATE,

status ENUM('borrowed', 'returned') DEFAULT 'borrowed',

FOREIGN KEY (user_id) REFERENCES users(id),

FOREIGN KEY (book_id) REFERENCES books(id)

);

CREATE TABLE reservations (

id INT AUTO_INCREMENT PRIMARY KEY,

user_id INT,

book_id INT,

reservation_date DATE,

FOREIGN KEY (user_id) REFERENCES users(id),

FOREIGN KEY (book_id) REFERENCES books(id)

);

项目结构:

library_management/

├── include/

│ ├── database.h # 数据库操作头文件

│ ├── user.h # 用户管理头文件

│ ├── book.h # 图书管理头文件

│ ├── borrow.h # 借阅管理头文件

│ ├── reservation.h # 预定管理头文件

├── src/

│ ├── database.c # 数据库操作实现

│ ├── user.c # 用户管理实现

│ ├── book.c # 图书管理实现

│ ├── borrow.c # 借阅管理实现

│ ├── reservation.c # 预定管理实现

│ ├── main.c # 主程序入口

├── Makefile # 编译工程的 Makefile(在Windows环境下可以使用MinGW或Visual Studio)

└── README.md # 项目说明文档

技术栈:

  1. 开发语言:C/C++
  2. 数据库:MySQL(Windows 下可以安装 MySQL Server)
  3. 开发环境:Visual Studio 或 MinGW (GCC),支持 C/C++ 开发
  4. Windows 专有库:可选使用 WinAPI 实现图形界面或控制台输出的美化

第一步:设置 Windows 开发环境

1. MySQL 在 Windows 上的安装与配置
  • 下载并安装 MySQL,安装 MySQL Server 和 MySQL C API 客户端库。
  • 配置数据库,确保 MySQL 服务正常运行,并设置好用户权限。
2. 安装开发工具
  • Visual Studio:可以直接安装 C/C++ 开发环境,集成调试、编译、运行功能。
  • MinGW:如果更喜欢命令行,可以安装 MinGW,它可以在 Windows 上提供类似 Linux 的 GCC 编译环境。

第二步:数据库操作模块

我们首先编写数据库模块来支持与 MySQL 数据库的交互。由于在 Windows 下开发,可以利用 MySQL 提供的 C API 函数,与 MySQL 进行连接、执行 SQL 语句、获取查询结果等。

include/database.h

#ifndef DATABASE_H
#define DATABASE_H

#include <mysql/mysql.h>

// 初始化数据库连接
MYSQL* init_db();

// 关闭数据库连接
void close_db(MYSQL *conn);

// 执行查询并返回结果
MYSQL_RES* execute_query(MYSQL *conn, const char *query);

// 执行更新或插入操作
int execute_update(MYSQL *conn, const char *query);

#endif

src/database.c

#include <stdio.h>

#include <stdlib.h>

#include <mysql/mysql.h>

#include "database.h"

// 初始化数据库连接

MYSQL* init_db() {

MYSQL *conn = mysql_init(NULL);

if (conn == NULL) {

fprintf(stderr, "mysql_init() failed\n");

return NULL;

}

if (mysql_real_connect(conn, "localhost", "root", "password", "library_management", 0, NULL, 0) == NULL) {

fprintf(stderr, "mysql_real_connect() failed: %s\n", mysql_error(conn));

mysql_close(conn);

return NULL;

}

return conn;

}

// 关闭数据库连接

void close_db(MYSQL *conn) {

mysql_close(conn);

}

// 执行查询并返回结果

MYSQL_RES* execute_query(MYSQL *conn, const char *query) {

if (mysql_query(conn, query)) {

fprintf(stderr, "Query failed: %s\n", mysql_error(conn));

return NULL;

}

return mysql_store_result(conn);

}

// 执行更新或插入操作

int execute_update(MYSQL *conn, const char *query) {

if (mysql_query(conn, query)) {

fprintf(stderr, "Update failed: %s\n", mysql_error(conn));

return 0;

}

return 1;

}

第三步:用户管理模块

include/user.h

#ifndef USER_H
#define USER_H

#include <mysql/mysql.h>

// 用户登录
int user_login(MYSQL *conn, const char *username, const char *password);

// 用户注册
int user_register(MYSQL *conn, const char *username, const char *password, const char *role);

// 获取用户角色
char* get_user_role(MYSQL *conn, const char *username);

#endif

src/user.c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "database.h"

#include "user.h"

// 用户登录

int user_login(MYSQL *conn, const char *username, const char *password) {

char query[256];

sprintf(query, "SELECT * FROM users WHERE username = '%s' AND password = '%s'", username, password);

MYSQL_RES *res = execute_query(conn, query);

if (mysql_num_rows(res) > 0) {

mysql_free_result(res);

return 1;

}

mysql_free_result(res);

return 0;

}

// 用户注册

int user_register(MYSQL *conn, const char *username, const char *password, const char *role) {

char query[256];

sprintf(query, "INSERT INTO users (username, password, role) VALUES ('%s', '%s', '%s')", username, password, role);

return execute_update(conn, query);

}

// 获取用户角色

char* get_user_role(MYSQL *conn, const char *username) {

char query[256];

sprintf(query, "SELECT role FROM users WHERE username = '%s'", username);

MYSQL_RES *res = execute_query(conn, query);

MYSQL_ROW row = mysql_fetch_row(res);

char *role = strdup(row[0]);

mysql_free_result(res);

return role;

}

第四步:图书管理模块

include/book.h

#ifndef BOOK_H
#define BOOK_H

#include <mysql/mysql.h>

// 添加图书
int add_book(MYSQL *conn, const char *title, const char *author, int stock);

// 获取所有图书
MYSQL_RES* get_all_books(MYSQL *conn);

// 根据书籍 ID 获取图书信息
MYSQL_RES* get_book_by_id(MYSQL *conn, int id);

// 修改图书信息
int update_book(MYSQL *conn, int id, const char *title, const char *author, int stock);

// 删除图书
int delete_book(MYSQL *conn, int id);

#endif

src/book.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "database.h"
#include "book.h"

// 添加图书
int add_book(MYSQL *conn, const char *title, const char *author, int stock) {
char query[256];
sprintf(query, "INSERT INTO books (title, author, stock) VALUES ('%s', '%s', %d)", title, author, stock);
return execute_update(conn, query);
}

// 获取所有图书
MYSQL_RES* get_all_books(MYSQL *conn) {
const char *query = "SELECT * FROM books";
return execute_query(conn, query);
}

// 根据书籍 ID 获取图书信息
MYSQL_RES* get_book_by_id(MYSQL *conn, int id) {
char query[100];
sprintf(query, "SELECT * FROM books WHERE id = %d", id);
return execute_query(conn, query);
}

// 修改图书信息
int update_book(MYSQL *conn, int id, const char *title, const char *author, int stock) {
char query[256];
sprintf(query, "UPDATE books SET title = '%s', author = '%s', stock = %d WHERE id = %d", title, author, stock, id);
return execute_update(conn, query);
}

// 删除图书
int delete_book(MYSQL *conn, int id) {
char query[100];
sprintf(query, "DELETE FROM books WHERE id = %d", id);
return execute_update(conn, query);
}

借阅管理模块
include/borrow.h

#ifndef BORROW_H
#define BORROW_H

#include <mysql/mysql.h>

// 借阅图书
int borrow_book(MYSQL *conn, int user_id, int book_id);

// 归还图书
int return_book(MYSQL *conn, int user_id, int book_id);

// 获取用户的借阅记录
MYSQL_RES* get_borrow_records(MYSQL *conn, int user_id);

#endif

src/borrow.c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "database.h"

#include "borrow.h"

// 借阅图书

int borrow_book(MYSQL *conn, int user_id, int book_id) {

char query[256];

sprintf(query, "INSERT INTO borrow_records (user_id, book_id, borrow_date) VALUES (%d, %d, CURDATE())", user_id, book_id);

return execute_update(conn, query);

}

// 归还图书

int return_book(MYSQL *conn, int user_id, int book_id) {

char query[256];

sprintf(query, "UPDATE borrow_records SET return_date = CURDATE(), status = 'returned' WHERE user_id = %d AND book_id = %d AND status = 'borrowed'", user_id, book_id);

return execute_update(conn, query);

}

// 获取用户的借阅记录

MYSQL_RES* get_borrow_records(MYSQL *conn, int user_id) {

char query[256];

sprintf(query, "SELECT * FROM borrow_records WHERE user_id = %d", user_id);

return execute_query(conn, query);

}

预定管理模块
include/reservation.h

#ifndef RESERVATION_H
#define RESERVATION_H

#include <mysql/mysql.h>

// 预定图书
int reserve_book(MYSQL *conn, int user_id, int book_id);

// 获取用户的预定记录
MYSQL_RES* get_reservations(MYSQL *conn, int user_id);

#endif

src/reservation.c

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include "database.h"

#include "reservation.h"

// 预定图书

int reserve_book(MYSQL *conn, int user_id, int book_id) {

char query[256];

sprintf(query, "INSERT INTO reservations (user_id, book_id, reservation_date) VALUES (%d, %d, CURDATE())", user_id, book_id);

return execute_update(conn, query);

}

// 获取用户的预定记录

MYSQL_RES* get_reservations(MYSQL *conn, int user_id) {

char query[256];

sprintf(query, "SELECT * FROM reservations WHERE user_id = %d", user_id);

return execute_query(conn, query);

}

主程序模块
src/main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "database.h"
#include "user.h"
#include "book.h"
#include "borrow.h"
#include "reservation.h"

void show_menu() {
printf("\n*** 图书馆管理系统 ***\n");
printf("1. 用户注册\n");
printf("2. 用户登录\n");
printf("3. 添加图书 (管理员)\n");
printf("4. 查看所有图书\n");
printf("5. 借阅图书\n");
printf("6. 归还图书\n");
printf("7. 预定图书\n");
printf("8. 查看借阅记录\n");
printf("9. 退出\n");
}

int main() {
MYSQL *conn = init_db();
if (conn == NULL) {
return 1;
}

int choice;
char username[50], password[100], role[10];
int user_id;

while (1) {
show_menu();
printf("请选择操作: ");
scanf("%d", &choice);

switch (choice) {
case 1: // 用户注册
printf("输入用户名: ");
scanf("%s", username);
printf("输入密码: ");
scanf("%s", password);
printf("输入角色 (admin/user): ");
scanf("%s", role);
user_register(conn, username, password, role);
break;
case 2: // 用户登录
printf("输入用户名: ");
scanf("%s", username);
printf("输入密码: ");
scanf("%s", password);
if (user_login(conn, username, password)) {
printf("登录成功!\n");
user_id = mysql_insert_id(conn); // 假设这里是用户 ID
} else {
printf("登录失败!\n");
}
break;
case 3: // 添加图书(仅管理员)
// 此处需要判断用户角色,省略
break;
case 4: // 查看所有图书
{
MYSQL_RES *res = get_all_books(conn);
MYSQL_ROW row;
printf("图书列表:\n");
while ((row = mysql_fetch_row(res))) {
printf("ID: %s, 书名: %s, 作者: %s, 库存: %s\n", row[0], row[1], row[2], row[3]);
}
mysql_free_result(res);
break;
}
case 5: // 借阅图书
printf("输入图书 ID: ");
int book_id;
scanf("%d", &book_id);
borrow_book(conn, user_id, book_id);
break;
case 6: // 归还图书
printf("输入图书 ID: ");
scanf("%d", &book_id);
return_book(conn, user_id, book_id);
break;
case 7: // 预定图书
printf("输入图书 ID: ");
scanf("%d", &book_id);
reserve_book(conn, user_id, book_id);
break;
case 8: // 查看借阅记录
{
MYSQL_RES *res = get_borrow_records(conn, user_id);
MYSQL_ROW row;
printf("借阅记录:\n");
while ((row = mysql_fetch_row(res))) {
printf("ID: %s, 图书 ID: %s, 借阅日期: %s, 归还日期: %s, 状态: %s\n", row[0], row[2], row[3], row[4], row[5]);
}
mysql_free_result(res);
break;
}
case 9: // 退出
close_db(conn);
return 0;
default:
printf("无效选项,请重试。\n");
}
}
return 0;
}

相关推荐
Ajiang28247353041 分钟前
对于C++中stack和queue的认识以及priority_queue的模拟实现
开发语言·c++
网易独家音乐人Mike Zhou3 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
‘’林花谢了春红‘’5 小时前
C++ list (链表)容器
c++·链表·list
i道i6 小时前
MySQL win安装 和 pymysql使用示例
数据库·mysql
搬砖的小码农_Sky6 小时前
C语言:数组
c语言·数据结构
机器视觉知识推荐、就业指导6 小时前
C++设计模式:建造者模式(Builder) 房屋建造案例
c++
Oak Zhang7 小时前
sharding-jdbc自定义分片算法,表对应关系存储在mysql中,缓存到redis或者本地
redis·mysql·缓存
久醉不在酒8 小时前
MySQL数据库运维及集群搭建
运维·数据库·mysql
Yang.998 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3