关系数据库原理

实验5:C/C++数据库编程


返回主页』『实验1』 『实验2』『实验3』『实验4』 『实验5』『实验6』『实验7』 『实验8』『大作业

上一次实验我们讲述了perl与mysql的连接,这次实验我们将采用C/C++与mysql进行连接,执行查询。 事实上MySQL已经提供了头文件(/opt/mysql/include/mysql/mysql.h),对于程序员来说这大大缩短了开发的过程。


一、实验目的

  1. 掌握C/C++与MySQL连接的方法。
  2. 掌握C/C++执行SQL查询的方法。


二、上机实验步骤

  1. 实验环境
    【硬件环境】
    【操作系统】Redhat Fedora Linux 8.0
    【脚本语言】gcc-x.x.x/g++-x.x.x
    【数据库环境】MySQL 5.0.41

  2. 准备工作

    首先确定mysql.h及其相应版本的shared object(/opt/mysql/lib/mysql/libmysqlclient.so.x)已经安装,理论上如果 采用源码安装方式安装的MySQL,这两个文件已经存在于你的系统中。

    最重要的一点,你必须确保你的MySQL Server Daemon正在运行中,首先为你的程序开发新增用户。

  3. MYSQL采用的数据结构

    【MYSQL】数据结构是最常用的,因为它是连接MySQL Server的handle,每一个handle代表程序与MySQL Server唯一的 连接,所有insert,select,delete等工作都需要MySQL handle作为参数以识别不同用户、数据库以及程序。

    【MYSQL_RES】表示的是MySQL Result Set(同Microsoft SQL-Server的Record Set),顾名思义,其包含的是数据库 传递回来的结果,但我们并不会直接读取【MYSQL_RES】的数据,而是使用【MYSQL_ROW】,也即是以行为单位读取数据。

  4. 连接MySQL Server

    在程序头上必须加入#include ,让编译器能够找到所有MySQL Client的函数定义,然后使用mysql_init() 准备连接,mysql_init()函数的原型为:

    MYSQL *mysql_init(MYSQL *mysql)

    下面是一个简单的例子:

    #include <stdio.h> #include <mysql.h> int main(int argc, char **argv) { MYSQL mysql_conn; /* Connection handle */ if (mysql_init(&mysql_conn) != NULL) { /* the initialization succeeds */ ... } else { /* the initialization fails */ ... } return 0; }

    将上述程序保存为test.c,采用gcc进行编译,在编译时必须指定mysql.h所在路径,并加入-lmysqlclient:

    gcc test.c -I/opt/mysql/include/mysql -L/opt/mysql/lib/mysql -lmysqlclient -o test

    如果是C++程序,则采用g++进行编译,g++的参数与gcc类似。

    handle被初始化后,就可以使用mysql_real_connect()或者mysql_connect()连接MySQL Server了,这两个 函数的参数包括handle, host, username, password,dbname等:

    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)

    第一个参数是handle,2-4参数分別为host name、user name及password, 第五个参数是db_name,你也可把第五个参数设为NULL,之后使用mysql_select_db()来选择数据库, 第六个参数是MySQL Server的连接池,一般都把它设为MYSQL_PORT(这正是mysql.h和libmysqlclient.so.x要配合的原因), 第七个参数是MySQL Server及client之间传输的渠道(socket或named pipe),但MySQL Server会根据host name建立另一渠道, 除非你有真的需要,否则你需在第七个参数填上NULL。最后是client_flag,包括压缩协议、查询协议、加密协议等。

    下面是一个简单的例子:

    #include <stdio.h> #include <mysql.h> int main(int argc, char **argv) { MYSQL mysql_conn /* Connection handle */ if (mysql_init(&mysql_conn) != NULL) { if (mysql_real_connect(&mysql_conn, "localhost", "test", "test_pass", "sample_db", MYSQL_PORT, NULL, 0) != NULL) { (void) printf("GOOD!\n"); } else { (void) printf("Connection fails.\n"); } } else { (void) printf("Initialization fails.\n"); return -1; } mysql_close(&mysql_conn); return 0; }
  5. 查询数据库

    当成功连接MySQL Server后,便可以使用mysql_query()或者mysql_real_query()查询数据库。但mysql_query()不能处理 binary data。如果你的查询包含binary data,就必须使用mysql_real_query(),这个函数需要提供该查询字串的长度。

    mysql_query()的原型如下:

    int mysql_query(MYSQL *mysql, const char *query) 第一个参数是MySQL连接的handle,第二个参数是查询字串。如果查询成功的话,mysql_query返回0,否则返回非0。

    如果你的查询语句并没有结果返回,例如DELETE/UPDAE/INSERT等,mysql_query被执行后便完成整个操作了; 如果你要执行INSERT/SHOW/DESCRIBE等,在存取结果前,必须使用mysql_store_result建立Result Handle。

    mysql_store_result()的原型如下:

    MYSQL_RES *mysql_store_result(MYSQL *mysql)

    例子如下所示:

    #include <stdio.h> #include <mysql.h> int main(int argc, char **argv) { MYSQL mysql_conn /* Connection handle */ MYSQL_RES *mysql_result; if (mysql_init(&mysql_conn) != NULL) { if (mysql_real_connect(&mysql_conn, "localhost", "test", "test_pass", "sample_db", MYSQL_PORT, NULL, 0) != NULL) { /* Please assume that this query has no error */ mysql_query(&mysql_conn, "select * from table1"); mysql_result = mysql_store_result(&mysql_conn); /* Free the result to release the heap memory*/ mysql_free_result(mysql_result); } else { (void) printf("Connection fails.\n"); } } else { (void) printf("Initialization fails.\n"); return -1; } mysql_close(&mysql_conn); return 0; }

    请注意mysql_result是一个指针,因为mysql_store_result会自动分配内存存储查询结果, 所以后面需要执行mysql_free_result(MYSQL_RES*)来释放内存。

  6. 提取查询结果

    提取结果前必须使用mysql_store_result()分配内存给查询结果,然后再用mysql_fetch_row()逐行提取数据。 结果的行数可以用mysql_num_rows(MYSQL_RES*)传回。mysql_fetch_row()函数的原型如下:

    MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)

    MYSQL_ROW是一个数组结构,数组中每一个元素依次为该行的column value。

    下面是一个简单的例子:

    #include <stdio.h> #include <dmalloc.h> #include <mysql.h> int main(int argc, char **argv) { MYSQL mysql_conn; /* Connection handle */ MYSQL_RES *mysql_result; /* Result handle */ MYSQL_ROW mysql_row; /* Row data */ int f1, f2, num_row, num_col; if (mysql_init(&mysql_conn) != NULL) { if (mysql_real_connect(&mysql_conn, "localhost", "test", "testpass", "test", MYSQL_PORT, NULL, 0) != NULL) { if (mysql_query(&mysql_conn, "select * from testtable limit 10") == 0) { mysql_result = mysql_store_result(&mysql_conn); num_row = mysql_num_rows(mysql_result); /* Get the no. of row */ num_col = mysql_num_fields(mysql_result); /* Get the no. of column */ for (f1 = 0; f1 < num_row; f1++) { mysql_row = mysql_fetch_row(mysql_result); /* Fetch one by one */ for (f2 = 0; f2 < num_col; f2++) { printf("[Row %d, Col %d] ==> [%s]\n", f1+1, f2+1, mysql_row[f2]); } } } else { (void) printf("Query fails\n"); } } else { int i = mysql_errno(&mysql_conn); const *s = mysql_error(&mysql_conn); (void) printf("Connection fails(ERROR %d): %s\n", i, s); } } else { (void) printf("Initialization fails\n"); } mysql_free_result(mysql_result); mysql_close(&mysql_conn); return 0; }

    请注意如果数据库很大,而又没有用mysql_free_result()释放内存的话,很容易发生内存溢出的问题。

  7. 上机任务
    1. 熟悉采用C/C++连接、查询和获取并显示MySQL数据的方法和函数。
    2. 以DOINT数据库为例,写一个C++程序,以pfam ID作为参数,列表显示与该domain相互作用的domain,并将结果输出到 一个目标文件。
    3. 如果你想对C/C++数据库开发有更加深入的了解,请参考C API 以及MySQL++

三、实验报告

完成以上练习,并将实验报告以rar附件的形式发送到ricket.woo AT gmail.com,文件名为5060809XXXlab5.rar。实验报告的内容 应包括实验环境,实验内容和结果讨论以及perl源程序。报告提交的截至日期是2009年5月27日。