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

函数名称参数返回值描述
mysql_initMYSQL *mysqlMYSQL *初始化一个 MySQL 对象,用于连接 MySQL 服务器。
mysql_real_connectMYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flagMYSQL *连接到 MySQL 数据库服务器。
mysql_closeMYSQL *mysqlvoid关闭与 MySQL 服务器的连接。
mysql_queryMYSQL *mysql, const char *queryint执行一条 SQL 查询。返回 0 表示成功,非 0 表示失败。
mysql_store_resultMYSQL *mysqlMYSQL_RES *检索完整的结果集。返回指向结果集的指针,或者失败时返回 NULL。
mysql_free_resultMYSQL_RES *resultvoid释放结果集的内存。
mysql_fetch_rowMYSQL_RES *resultMYSQL_ROW从结果集中获取下一行。返回表示行的数组,或者没有更多数据时返回 NULL。
mysql_affected_rowsMYSQL *mysqlmy_ulonglong返回最近执行的 SQL 语句影响的行数。
mysql_num_rowsMYSQL_RES *resultmy_ulonglong返回结果集中行的数量。
mysql_num_fieldsMYSQL_RES *resultunsigned int返回结果集中的字段数。
mysql_errorMYSQL *mysqlconst char *返回最近一次 MySQL 操作的错误消息字符串。
mysql_real_escape_stringMYSQL *mysql, char *to, const char *from, unsigned long lengthunsigned long转义字符串中的特殊字符,使其可以安全地用于 SQL 语句中。
mysql_commitMYSQL *mysqlint提交当前事务。返回 0 表示成功,非 0 表示失败。
mysql_rollbackMYSQL *mysqlint回滚当前事务。返回 0 表示成功,非 0 表示失败。
mysql_set_character_setMYSQL *mysql, const char *charsetint设置当前连接使用的字符集。返回 0 表示成功,非 0 表示失败。
mysql_autocommitMYSQL *mysql, my_bool modeint开启或关闭自动提交功能。传入 0 表示关闭,1 表示开启。
mysql_stmt_initMYSQL *mysqlMYSQL_STMT *初始化一个预处理语句句柄。
mysql_stmt_prepareMYSQL_STMT *stmt, const char *query, unsigned long lengthint预处理一个 SQL 查询。返回 0 表示成功,非 0 表示失败。
mysql_stmt_bind_paramMYSQL_STMT *stmt, MYSQL_BIND *bindint绑定参数到预处理语句。
mysql_stmt_bind_resultMYSQL_STMT *stmt, MYSQL_BIND *bindint绑定结果变量到预处理语句。
mysql_stmt_executeMYSQL_STMT *stmtint执行一个预处理语句。返回 0 表示成功,非 0 表示失败。
mysql_stmt_fetchMYSQL_STMT *stmtint获取执行结果中的下一行。返回 0 表示成功,非 0 表示失败或无更多行。
mysql_stmt_closeMYSQL_STMT *stmtint关闭一个预处理语句句柄。返回 0 表示成功,非 0 表示失败。
mysql_stmt_result_metadataMYSQL_STMT *stmtMYSQL_RES *获取一个预处理语句执行后返回结果的元数据。返回结果集结构指针,或者 NULL 表示没有元数据。
mysql_stmt_num_rowsMYSQL_STMT *stmtmy_ulonglong获取结果集中返回的行数。
mysql_stmt_store_resultMYSQL_STMT *stmtint将完整的结果集存储在客户端内存中。返回 0 表示成功,非 0 表示失败。
mysql_stmt_free_resultMYSQL_STMT *stmtvoid释放预处理语句的结果集。
mysql_stmt_resetMYSQL_STMT *stmtint重置预处理语句,使其可以重新执行。返回 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;
}

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部