数据库编程——MySQL API
在学习数据库编程总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
09-数据库编程day04(mysql api)
目录:
一、学习目标
二、复习
1、Oracle
2、MySQL
三、MySQL剩余问题
1、MySQL相关约束
2、中文乱码问题
四、MySQL API
1、通过api连接数据库
2、通过api增加数据
3、通过api实现查询分析
4、实现查询的功能
5、查询改进
6、客户端实现
7、字符集的问题
8、字符集问题处理
9、预处理的流程
10、预处理的内存映射
11、MySQL通过api处理事务
12、课后作业
五、monogodb数据库
1、monogodb数据库的安装
2、monogodbc+驱动安装
一、学习目标
1.mysql的约束
2.mysql的中文乱码问题
3.通过api连接mysql实现增删改查
4.了解mysql预处理api的思想和实现流程
5.mysql通过API开启事务
二、复习
1、Oracle
》oracle常用的数据类型?
○ number
○ varchar2
○ date
》oracle有哪些约束?
○ check
○ unique
○ 非空
○ 主键
○ 外键
》建表注意事项?
○ 第一个必须是字母
○ 表名不能超过30长度
○ 表空间+权限
--建一个表
create table t1(id number(10,2),name varchar2(30),hiredate date default sysdate);
--哪个对象的作用是简化复杂查询以及隔离数据访问? 视图,同义词
--如何避免主键冲突? 使用序列,currval,nextval
--如何提高查询效率? 创建索引,创建一个索引表
create index indexname on tablename(columnname);
--怎么批量创建同义词?
select * from tab;
create synonym syname for username.tablename;
--使用tab字典生成创建语句
select 'create synonym '||tname||' for scott.'||tname||';' from tab;
2、MySQL
》Mysql安装所需要的包哪几个?
client,server,develop
》Mysql的组织结构?
root(用户)--> 库(scott)--》表
mysql库的操作
mysql表的操作
mysql数据的操作
○mysql 的group by语法检查不严格
》mysql多表查询
○ 内连接 inner join
○ 外连接
▪ 左外 left outer join
▪ 右外 right outer join
口诀:1)--> (left|right)join 2)where -> on
三、MySQL剩余问题
1、MySQL相关约束
》约束的种类:
○ 主键
○ 外键
○ 非空
○ 唯一
create table myclass (
id INT(11) primary key auto_increment, (MySQL增加功能:auto_increment主键自动增长,很好的解决了主键冲突的问题!)
name varchar(20) unique,
hiredate timestamp
);
mysql> insert into myclass(name) values('yekai');
mysql> select * from myclass;
mysql> insert into myclass(name) values('fuhongxue');
mysql> insert into myclass(id,name) values(5,'luxiaojia');
mysql> select * from myclass;
mysql> insert into myclass(name) values('lixunhuan');
create table student (
id INT(11) primary key auto_increment,
name varchar(20) unique,
passwd varchar(15) not null,
classid INT(11),
constraint stu_classid_FK foreign key(classid) references myclass(id)
);
(定义外键约束,名字stu_classid_FK,foreign key关键字,约束的外键classid,references约束来自myclass表的id字段)
mysql> insert into student(name,passwd,classid) values('xiaoming','123',1);
mysql> insert into student(name,passwd,classid) values('xiaoming','123',9); (name唯一,所以报错)
mysql> insert into student(name,passwd,classid) values('xiaohong','123',9); (没有9班,所以报错)
mysql> insert into student(name,passwd,classid) values('xiaohong','123',6);
mysql> insert into student(name,passwd,classid) values('xiaohua',null,6); (密码不能为空,所以报错)
mysql> delete from myclass where id=1;(两个表创建了关联,所以删除报错)
2、中文乱码问题
》因素1: MySQL自身的设计
【实验步骤1】:
mysql> show variables like 'character%'; 查看所有应用的字符集
【实验步骤2】:
$ mysql -uroot -p123456 --default_character_set=gbk 指定字符集登录数据库
mysql> show variables like 'character%';
影响了与客户端相关联的 3处 (最外层)
在这种状态下执行use mydb2;
mysql> select * from employee;
查看输出,会出现乱码。
原来的三条数据,是以utf8的形式存储到数据库中,当使用gbk连接以后,数据库仍按照utf8的形式将数据返回,出错。
【实验步骤3】:
在该环境下插入带有中文的一行数据。
mysql> insert into employee(id,name,sex,birthday,salary,entry_date,resume) values(10,'张三疯',1,'1983-09-21',15000,'2012-06-24','一个老牛');
ERROR 1366 (HY000): Incorrect string value: '\x80\xE4\xB8\xAA\xE8\x80...' for column 'resume' at row 1
字符集乱的话,报的错不可信!!!
》因素2:操作系统的语言集
linux操作系统 是一个 多用户的操作
[root@localhost ~]# cat /etc/sysconfig/i18n
LANG="zh_CN.UTF-8"
操作系统的菜单按照zh_CN显示, 文件存储按照utf8
linux操作系统语言环境 和 用户的配置的语言环境LANG 相互影响
[mysql01@localhost ~]$ echo $LANG
zh_CN.UTF-8
【实验步骤4】:
修改用户下的.bash_profile 中的LANG,屏蔽操作系统的LANG设置。再查数据库
mysql> select * from employee;
结论: 用户的LANG设置,影响了应用程序的语言环境,导致myql的语言环境发生了改变。
mysql> show variables like 'character%';
在此环境下,检索中文会出现乱码。
【实验步骤5】:在上述环境之下,向数据库中插入中文。
insert into employee(id,name,sex,birthday,salary,entry_date,resume) values(5,'张三疯',1,'1987-05-21',15000,'2014-06-24','一个老牛');
数据能插入到数据库中,没 有 报 任 何 错 误!但显示不正确。
》因素3:文件存储格式
》适应开发环境:
四、MySQL API
1、通过api连接数据库
》依赖的文件
/usr/include/mysql/mysql.h
/usr/lib64/mysql/libmysqlclient.a
如何查找?
shell> locate mysql.h
shell> libmysqlclient.a
》开始编程:
首先要做的事登录到mysql,退出
mysql_init 初始化
mysql_real_connect 连接到数据库
mysql_close 关闭连接
》MYSQL *mysql_init(MYSQL *mysql)
成功返回MYSQL*指针,失败返回NULL
》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)
host主机ip (mysql为mysql_init返回的指针)
user用户名(数据库)
passwd 密码
db 要登录的库名
port 端口 默认填0
○ mysql 默认端口 3306
○ oracle 默认端口 1521
○ mongodb 默认端口 27017
unix_socket套接字,默认填NULL
client_flag客户端标志,一般填0
返回值:如果成功返回MYSQL* ,失败返回NULL
》关闭连接 void mysql_close(MYSQL *mysql) 传送mysql句柄
>touch 01_hello.c
>vi 01_hello.c
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include "mysql.h" #define _HOST_ "127.0.0.1" #define _USER_ "root" //数据库用户 #define _PASSWD_ "123" #define _DBNAME_ "SCOTT" int main() { //1. init MYSQL*mysql = mysql_init(NULL); if(mysql == NULL){ printf("init err\n"); exit(1); } //2. real_connect mysql = mysql_real_connect(mysql,_HOST_,_USER_,_PASSWD_,_DBNAME_,0,NULL,0); if(mysql == NULL){ printf("connect err\n"); exit(1); } printf("hello mysql!\n"); //3. close mysql_close(mysql); return 0; }
>gcc 01_hello.c -I/usr/include/mysql -L/usr/lib64/mysql -lmysqlclient
编译产生错误:
(dlopen打开一个动态库)
编译时持续报错,用man查看(gxx—使用了g++,所以添加-lstdc++;man dlclose—添加-ldl;man pthread_mutex_trylock—添加-lpthread;man clock_gettime—添加-lrt)
>gcc 01_hello.c -I/usr/include/mysql -L/usr/lib64/mysql -lmysqlclient -lstdc++ -ldl -lpthread -lrt
>./a.out (报错:connect err)
分析:问题出在mysql_real_connect函数,远程登录可以登录MySQL,证明前边4个参数没有问题,后边两个参数默认,也不会有问题,分析可知,第5个参数出现问题(库名大写了,应该为小写,改为scott)。更改后再次gcc编译,运行:
2、通过api增加数据
--连接后执行一次插入操作
》sql执行函数
int mysql_query(MYSQL *mysql, const char *query)
○ mysql连接句柄
○ query是一个sql
返回值:成功返回0,错误非0值
》练习:
(打开另一个终端,登录MySQL后执行mysql>select * from dept;)
回原终端:
>touch 02_insert.c
>vi 02_insert.c
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include "mysql.h" #define _HOST_ "127.0.0.1" #define _USER_ "root" //数据库用户 #define _PASSWD_ "123" #define _DBNAME_ "scott" int main() { //1. init MYSQL*mysql = mysql_init(NULL); if(mysql == NULL){ printf("init err\n"); exit(1); } //2. real_connect mysql = mysql_real_connect(mysql,_HOST_,_USER_,_PASSWD_,_DBNAME_,0,NULL,0); if(mysql == NULL){ printf("connect err\n"); exit(1); } printf("hello mysql!\n"); char rSql[256]={0}; strcpy(rSql,"insert into dept values(60,'60name','60loc')"); if(mysql_query(mysql,rSql) != 0){ printf("mysql_query err\n"); exit(1); } //3. close mysql_close(mysql); return 0; }
>touch makefile
>vi makefile
SrcFiles=$(wildcard *.c) TargetFiles=$(patsubst %.c,%,$(SrcFiles)) IncPath=/usr/include/mysql LibPath=/usr/lib64/mysql PubLib=-lmysqlclient -lstdc++ -ldl -lpthread -lrt all:$(TargetFiles) %:%.c gcc -o $@ $^ -I$(IncPath) -L$(LibPath) $(PubLib) clean: -rm -f $(TargetFiles)
>make -n(先找到all伪目标,然后向下找,执行%:%.c)
>make -n clean(执行删除)
>make
>./02_insert
(打开另一个终端,登录MySQL后执行mysql>select * from dept;)
3、通过api实现查询分析
》第三步:
实现查询select * from emp
○ 执行mysql_query
○ 显示结果集
》显示结果集的函数:
○MYSQL_RES *mysql_store_result(MYSQL *mysql)
▪ mysql 连接句柄
▪ 返回MYSQL_RES*结构指针
○ 调用mysql_free_result()释放结果集
▪ void mysql_free_result(MYSQL_RES *result)
▪ void mysql_free_result(MYSQL_RES *result)
○ 调用mysql_fetch_row()获取结果集行
▪ MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)
▪ typedef char **MYSQL_ROW;
4、实现查询的功能
>touch 03_select.c
>vi 03_select.c
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include "mysql.h" #define _HOST_ "127.0.0.1" #define _USER_ "root" //数据库用户 #define _PASSWD_ "123" #define _DBNAME_ "scott" int main() { //1. init MYSQL*mysql = mysql_init(NULL); if(mysql == NULL){ printf("init err\n"); exit(1); } //2. real_connect mysql = mysql_real_connect(mysql,_HOST_,_USER_,_PASSWD_,_DBNAME_,0,NULL,0); if(mysql == NULL){ printf("connect err\n"); exit(1); } printf("hello mysql!\n"); char rSql[256]={0}; strcpy(rSql,"select * from emp"); if(mysql_query(mysql,rSql) != 0){ printf("mysql_query err\n"); exit(1); } //取回结果集 int i=0; MYSQL_RES * result = mysql_store_result(mysql); MYSQL_ROW row; if(result != NULL){ //需要打印结果集 while((row= mysql_fetch_row(result)) != NULL){//循环取一行 for(i = 0;i< 8; i ++){ printf("%s\t",row[i]); } printf("\n"); } //释放结果集 mysql_free_result(result); } //3. close mysql_close(mysql); return 0; }
>make
>./03_select
5、查询改进
>touch 04_select.c
>vi 04_select.c
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include "mysql.h" #define _HOST_ "127.0.0.1" #define _USER_ "root" //数据库用户 #define _PASSWD_ "123" #define _DBNAME_ "scott" void show_result(MYSQL_RES * result) { //打印表头 unsigned int num_fields; unsigned int i; MYSQL_FIELD *fields; num_fields = mysql_num_fields(result); fields = mysql_fetch_fields(result); for(i = 0; i < num_fields; i++) { printf("%s\t", fields[i].name); } printf("\n+-------+--------+-----------+------+------------+------+------+--------+\n"); MYSQL_ROW row; num_fields = mysql_num_fields(result);//取字段个数 while ((row = mysql_fetch_row(result)))//循环取一行 { for(i = 0; i < num_fields; i++) { printf("%s\t", row[i] ? row[i] : "NULL"); } printf("\n"); } } int main() { //1. init MYSQL*mysql = mysql_init(NULL); if(mysql == NULL){ printf("init err\n"); exit(1); } //2. real_connect mysql = mysql_real_connect(mysql,_HOST_,_USER_,_PASSWD_,_DBNAME_,0,NULL,0); if(mysql == NULL){ printf("connect err\n"); exit(1); } printf("hello mysql!\n"); char rSql[256]={0}; strcpy(rSql,"select * from emp"); if(mysql_query(mysql,rSql) != 0){ printf("mysql_query err\n"); exit(1); } //取回结果集 int i=0; MYSQL_RES * result = mysql_store_result(mysql); MYSQL_ROW row; if(result != NULL){ //需要打印结果集 show_result(result); mysql_free_result(result);//释放结果集 } //3. close mysql_close(mysql); return 0; }
>make
>./04_select
6、客户端实现
》实现客户端:
○ 初始化
○ 连接
○ 循环等待sql输入
○ 执行sql
▪ 如果有结果集,打印结果集
○ 关闭
>touch 05_client.c
>vi 05_client.c
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include "mysql.h" #define _HOST_ "127.0.0.1" #define _USER_ "root" //数据库用户 #define _PASSWD_ "123" #define _DBNAME_ "scott" void show_result(MYSQL_RES * result,MYSQL *mysql) { //打印表头 unsigned int num_fields; unsigned int i; MYSQL_FIELD *fields; num_fields = mysql_num_fields(result); fields = mysql_fetch_fields(result); for(i = 0; i < num_fields; i++) { printf("%s\t", fields[i].name); } printf("\n----------------------------------------------------------\n");//华丽分割线 MYSQL_ROW row; num_fields = mysql_num_fields(result);//取字段个数 while ((row = mysql_fetch_row(result)))//循环取一行 { for(i = 0; i < num_fields; i++) { printf("%s\t", row[i] ? row[i] : "NULL"); } printf("\n"); } printf("\n----------------------------------------------------------\n");//华丽分割线 //3 rows in set (0.28 sec) printf("%ld rows in set \n",mysql_affected_rows(mysql));//影响的数目 } int main() { //1. init MYSQL*mysql = mysql_init(NULL); if(mysql == NULL){ printf("init err\n"); exit(1); } //2. real_connect mysql = mysql_real_connect(mysql,_HOST_,_USER_,_PASSWD_,_DBNAME_,0,NULL,0); if(mysql == NULL){ printf("connect err\n"); exit(1); } printf("welcome to mysql!\n"); char rSql[1024]={0}; while(1){ write(STDOUT_FILENO,"yoursql>",8); memset(rSql,0x00,sizeof(rSql)); read(STDIN_FILENO,rSql,sizeof(rSql));//读入sql if(strncmp(rSql,"quit",4) == 0){ printf("bye bye!\n"); break; } //执行sql if(mysql_query(mysql,rSql) != 0){ printf("mysql_query err\n"); continue; } //取回结果集 int i=0; MYSQL_RES * result = mysql_store_result(mysql); MYSQL_ROW row; if(result != NULL){ //需要打印结果集 show_result(result,mysql); mysql_free_result(result);//释放结果集 }else{ //Query OK, 1 row affected (0.16 sec) printf("Query OK, %ld row affected \n",mysql_affected_rows(mysql)); //printf("%ld products updated",(long) mysql_affected_rows(&mysql)); } } //3. close mysql_close(mysql); return 0; }
>make
>./05_client
yoursql>select * from dept;
yoursql>insert into dept values(60, '60name', '60loc');
yoursql>select * from dept;
yoursql>desc dept;
yoursql>show tables;
7、字符集的问题
yoursql>desc dept;
yoursql>insert into dept(deptno,dname) values(61, '财务');
yoursql>select * from dept;
但是,打开另一个终端登录MySQL,输入指令
mysql>select * from dept;
出现问题:乱码。
问题分析:MySQL默认是以gbk格式登录的,用自己客户端传入中文,传出中文,而用原mysql读出会乱码。如何解决?
8、字符集问题处理
》设置字符集
int mysql_set_character_set(MYSQL *mysql, char *csname)
>vi 05_client.c
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include "mysql.h" #define _HOST_ "127.0.0.1" #define _USER_ "root" //数据库用户 #define _PASSWD_ "123" #define _DBNAME_ "scott" void show_result(MYSQL_RES * result,MYSQL *mysql) { //打印表头 unsigned int num_fields; unsigned int i; MYSQL_FIELD *fields; num_fields = mysql_num_fields(result); fields = mysql_fetch_fields(result); for(i = 0; i < num_fields; i++) { printf("%s\t", fields[i].name); } printf("\n----------------------------------------------------------\n");//华丽分割线 MYSQL_ROW row; num_fields = mysql_num_fields(result);//取字段个数 while ((row = mysql_fetch_row(result)))//循环取一行 { for(i = 0; i < num_fields; i++) { printf("%s\t", row[i] ? row[i] : "NULL"); } printf("\n"); } printf("\n----------------------------------------------------------\n");//华丽分割线 //3 rows in set (0.28 sec) printf("%ld rows in set \n",mysql_affected_rows(mysql));//影响的数目 } int main() { //1. init MYSQL*mysql = mysql_init(NULL); if(mysql == NULL){ printf("init err\n"); exit(1); } //2. real_connect mysql = mysql_real_connect(mysql,_HOST_,_USER_,_PASSWD_,_DBNAME_,0,NULL,0); if(mysql == NULL){ printf("connect err\n"); exit(1); } mysql_set_character_set(mysql, "utf8");//设置字符集 printf("welcome to mysql!\n"); char rSql[1024]={0}; while(1){ write(STDOUT_FILENO,"yoursql>",8); memset(rSql,0x00,sizeof(rSql)); read(STDIN_FILENO,rSql,sizeof(rSql));//读入sql if(strncmp(rSql,"quit",4) == 0){ printf("bye bye!\n"); break; } //执行sql if(mysql_query(mysql,rSql) != 0){ printf("mysql_query err\n"); continue; } //取回结果集 int i=0; MYSQL_RES * result = mysql_store_result(mysql); MYSQL_ROW row; if(result != NULL){ //需要打印结果集 show_result(result,mysql); mysql_free_result(result);//释放结果集 }else{ //Query OK, 1 row affected (0.16 sec) printf("Query OK, %ld row affected \n",mysql_affected_rows(mysql)); //printf("%ld products updated",(long) mysql_affected_rows(&mysql)); } } //3. close mysql_close(mysql); return 0; }
>make
>./05_client.c
现在两端看到的一致了,都是乱码,然后删除数据,重新插入。
yoursql>delete from dept where deptno=61;
yoursql>insert into dept values(61,'财务', '北京');
9、预处理的流程
对于多次执行的语句,预处理执行比直接执行快,主要原因在于,仅对查询执行一次解析操作。在直接执行的情况下,每次执行语句时,均将进行查询。此外,由于每次执行预处理语句时仅需发送参数的数据,从而减少了网络通信量。
预处理语句的另一个优点是,它采用了二进制协议,从而使得客户端和服务器之间的数据传输更有效率。
下述语句可用作预处理语句:CREATE TABLE、DELETE、DO、INSERT、REPLACE、SELECT、SET、UPDATE、以及大多数SHOW语句。在MySQL 5.1中,不支持其他语句。
》预处理流程图:
》可分析代码:(07_prepare_insert.c)

#include <stdio.h> #include "mysql.h" #include <stdlib.h> #include <string.h> #define _HOST_ "localhost" //主机 #define _USER_ "root" //mysql用户,非主机 #define _PASSWD_ "123" //密码 #define _DBNAME_ "scott" //库名 #define STRING_SIZE 50 #define DROP_SAMPLE_TABLE "DROP TABLE IF EXISTS test_table" #define CREATE_SAMPLE_TABLE "CREATE TABLE test_table(col1 INT,\ col2 VARCHAR(40),\ col3 SMALLINT,\ col4 TIMESTAMP)" #define INSERT_SAMPLE "INSERT INTO test_table(col1,col2,col3) VALUES(?,?,?)" void prepare_insert(MYSQL *mysql); int main() { //1.初始化 MYSQL * mysql = NULL; mysql = mysql_init(NULL) ; if(mysql == NULL ) { printf("mysql init err\n"); exit(1); } //2.连接 mysql = mysql_real_connect(mysql, _HOST_,_USER_, _PASSWD_,_DBNAME_, 0, NULL,0); if(mysql == NULL) { printf("mysql_real_connect connect err\n"); exit(1); } printf("welcome to mysql \n"); prepare_insert(mysql); //3.关闭 mysql_close(mysql); return 0; } void prepare_insert(MYSQL *mysql) { MYSQL_STMT *stmt;//预处理的句柄 MYSQL_BIND bind[3];//绑定变量 my_ulonglong affected_rows; int param_count; short small_data; int int_data; char str_data[STRING_SIZE]; unsigned long str_length; my_bool is_null; if (mysql_query(mysql, DROP_SAMPLE_TABLE))//删除表 { fprintf(stderr, " DROP TABLE failed\n"); fprintf(stderr, " %s\n", mysql_error(mysql)); exit(0); } if (mysql_query(mysql, CREATE_SAMPLE_TABLE))//创建表 { fprintf(stderr, " CREATE TABLE failed\n"); fprintf(stderr, " %s\n", mysql_error(mysql)); exit(0); } /* Prepare an INSERT query with 3 parameters */ /* (the TIMESTAMP column is not named; the server */ /* sets it to the current date and time) */ stmt = mysql_stmt_init(mysql); //预处理的初始化 if (!stmt) { fprintf(stderr, " mysql_stmt_init(), out of memory\n"); exit(0); } if (mysql_stmt_prepare(stmt, INSERT_SAMPLE, strlen(INSERT_SAMPLE))) //insert 语句 的预处理 { fprintf(stderr, " mysql_stmt_prepare(), INSERT failed\n"); fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); exit(0); } fprintf(stdout, " prepare, INSERT successful\n"); /* Get the parameter count from the statement */ param_count= mysql_stmt_param_count(stmt);//获得参数个数 fprintf(stdout, " total parameters in INSERT: %d\n", param_count); if (param_count != 3) /* validate parameter count */ { fprintf(stderr, " invalid parameter count returned by MySQL\n"); exit(0); } /* Bind the data for all 3 parameters */ memset(bind, 0, sizeof(bind)); /* INTEGER PARAM */ /* This is a number type, so there is no need to specify buffer_length */ bind[0].buffer_type= MYSQL_TYPE_LONG; bind[0].buffer= (char *)&int_data;//内存地址的映射 bind[0].is_null= 0; bind[0].length= 0; /* STRING PARAM */ bind[1].buffer_type= MYSQL_TYPE_STRING; bind[1].buffer= (char *)str_data;//char 100 bind[1].buffer_length= STRING_SIZE; bind[1].is_null= 0; bind[1].length= &str_length; /* SMALLINT PARAM */ bind[2].buffer_type= MYSQL_TYPE_SHORT; bind[2].buffer= (char *)&small_data; bind[2].is_null= &is_null;//是否为null的指示器 bind[2].length= 0; /* Bind the buffers */ if (mysql_stmt_bind_param(stmt, bind)) //绑定变量 参数绑定 { fprintf(stderr, " mysql_stmt_bind_param() failed\n"); fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); exit(0); } //第一波赋值 int_data= 10; /* integer */ strncpy(str_data, "MySQL", STRING_SIZE); /* string */ str_length= strlen(str_data); /* INSERT SMALLINT data as NULL */ is_null= 1;//指示插入的第三个字段是否为null /* Execute the INSERT statement - 1*/ if (mysql_stmt_execute(stmt)) //预处理的执行,第一次执行 { fprintf(stderr, " mysql_stmt_execute(), 1 failed\n"); fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); exit(0); } /* Get the total number of affected rows */ affected_rows= mysql_stmt_affected_rows(stmt);//预处理的影响条数 fprintf(stdout, " total affected rows(insert 1): %lu\n", (unsigned long) affected_rows); if (affected_rows != 1) /* validate affected rows */ { fprintf(stderr, " invalid affected rows by MySQL\n"); exit(0); } //第二波赋值 int_data= 1000; strncpy(str_data, "The most popular Open Source database", STRING_SIZE); str_length= strlen(str_data); small_data= 1000; /* smallint */ is_null= 1; /* reset */ /* Execute the INSERT statement - 2*/ if (mysql_stmt_execute(stmt))//第二次执行 { fprintf(stderr, " mysql_stmt_execute, 2 failed\n"); fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); exit(0); } /* Get the total rows affected */ affected_rows= mysql_stmt_affected_rows(stmt); fprintf(stdout, " total affected rows(insert 2): %lu\n", (unsigned long) affected_rows); if (affected_rows != 1) /* validate affected rows */ { fprintf(stderr, " invalid affected rows by MySQL\n"); exit(0); } /* Close the statement */ if (mysql_stmt_close(stmt)) { fprintf(stderr, " failed while closing the statement\n"); fprintf(stderr, " %s\n", mysql_stmt_error(stmt)); exit(0); } }
10、预处理的内存映射
》预处理内存映射图:
>make
>./07_prepare_insert
打开另一个客户端,登录MySQL查看(mysql>select * from test_table)
vi 07_prepare_insert.c更改165行的代码为:is_null= 0; 然后重新编译执行,并在另一个客户端查看。
11、MySQL通过api处理事务
》分析代码(06_client_format.c):

//编写客户端 #include <stdio.h> #include "mysql.h" #include <stdlib.h> #include <string.h> #define _HOST_ "localhost" //主机 #define _USER_ "root" //mysql用户,非主机 #define _PASSWD_ "123" //密码 #define _DBNAME_ "scott" //库名 #define _MAX_COLS_ 30 //定义格式化输出结构体 typedef struct _strFormat{ int length; // 每个字段(列)最终显示的长度 char sfm[10]; // 每个列的展示串的格式化效果 char Column[100]; // 列显示效果 | empno char splitLine[100]; // 华丽的分割线显示效果 +------- }strFormat; /* +-------+--------+-----------+------+------------+------+------+--------+ | empno | ename | job | mgr | hiredate | sal | comm | deptno | +-------+--------+-----------+------+------------+------+------+--------+ | 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800 | NULL | 20 | */ //显示结果集 void show_result(MYSQL_RES *result,MYSQL * mysql) { //展示result的结果 if(result != NULL) { //代表结果集中有记录 if(result->row_count == 0) { printf("Empty set\n"); return; } //打印表头 unsigned int num_fields; strFormat fm[_MAX_COLS_]; bzero(&fm,sizeof(strFormat)*_MAX_COLS_); unsigned int i,j ; MYSQL_FIELD *fields; //printf("-------------------------------------------------------------------------\n"); num_fields = mysql_num_fields(result); fields = mysql_fetch_fields(result);//取回表头,即各个列名 //以下循环为生成之前定义结构体对应的各个列的标准格式化 for(i = 0; i < num_fields; i++) { fm[i].length = fields[i].name_length > fields[i].max_length ? fields[i].name_length:fields[i].max_length;//每个字段有多个长度,取名字长度和内容最长 两者之间的最大值 fm[i].splitLine[0]='+'; for(j = 1 ; j < fm[i].length+3; j ++) { fm[i].splitLine[j]='-'; } if(i == num_fields -1)//最后一个字段需要特别处理一下,需要用"|" 作为结束 { sprintf(fm[i].sfm,"| %c-%ds |",'%',fm[i].length); // %-17c fm[i].splitLine[j]='+'; } else { sprintf(fm[i].sfm,"| %c-%ds ",'%',fm[i].length);//得到的目标 是 "| %-8s " } sprintf(fm[i].Column,fm[i].sfm,fields[i].name); } for(j = 0 ; j < 3; j ++ )//此处为打印表头,注意,表头三行,华丽的分割线中间是列名 { if(j == 1)//处理列名 { for(i = 0; i < num_fields; i++) { printf("%s",fm[i].Column); } } else//处理华丽分割线 { for(i = 0; i < num_fields; i++) { printf("%s",fm[i].splitLine); } } printf("\n"); } //printf("-------------------------------------------------------------------------\n"); MYSQL_ROW row; num_fields = mysql_num_fields(result); while ((row = mysql_fetch_row(result)))//循环展示每一行的的内容 { for(i = 0; i < num_fields; i++)//针对每一行,展示每一列,使用之前得到的输出格式化串 { printf(fm[i].sfm, row[i] ? row[i] : "NULL"); } printf("\n"); } for(i = 0; i < num_fields; i++) { printf("%s",fm[i].splitLine); } printf("\n%ld rows in set \n",(long) mysql_affected_rows(mysql)); } } int main() { //1.初始化 MYSQL * mysql = NULL; mysql = mysql_init(NULL) ; if(mysql == NULL ) { printf("mysql init err\n"); exit(1); } //2.连接 mysql = mysql_real_connect(mysql, _HOST_,_USER_, _PASSWD_,_DBNAME_, 0, NULL,0); if(mysql == NULL) { printf("mysql_real_connect connect err\n"); exit(1); } mysql_set_character_set(mysql, "utf8");//设置字符集 printf("welcome to mysql \n"); char rSql[512]; while(1)//管理台 { printf("yekaisql>"); memset(rSql,0x00,sizeof(rSql)); fgets(rSql,sizeof(rSql),stdin); //判断是否退出 quit if(strncmp(rSql,"quit",4) == 0 || strncmp(rSql,"QUIT",4) == 0) { printf("bye bye\n"); break; } //查询 if(mysql_query(mysql,rSql) != 0) { printf("query err\n"); //mysql_close(mysql); //exit(1); continue; } //打印结果 //先取回结果 MYSQL_RES *result = NULL ; result = mysql_store_result(mysql); if(result){ //打印结果集 show_result(result,mysql); //释放结果集 mysql_free_result(result); } else { printf("Query OK, %ld rows affected \n",(long) mysql_affected_rows(mysql)); } } //3.关闭 mysql_close(mysql); return 0; }
>make
>./06_client_format
yekaisql>select * from dept;
yekaisql>select * from emp;
yekaisql>quit
》分析代码(08_tran.c):

//mysql中的事务 #include <stdio.h> #include <stdlib.h> #include <string.h> #include "mysql.h" #define SET_TRAN "SET AUTOCOMMIT=0" //手动commit ————手动commit #define UNSET_TRAN "SET AUTOCOMMIT=1" //自动commit #define _HOST_ "127.0.0.1" #define _USER_ "root" #define _PASSWD_ "123" #define _DBNAME_ "scott" //设置事务为手动提交 int mysql_OperationTran(MYSQL *mysql) { //--开启事务 int ret = mysql_query(mysql, "start transaction"); if (ret != 0) { printf("mysql_OperationTran query start err: %s\n", mysql_error(mysql)); return ret; } //--设置事务为手动提交 ret = mysql_query(mysql, SET_TRAN); //set autocommmit = 0 if (ret != 0) { printf("mysql_OperationTran query set err: %s\n", mysql_error(mysql)); return ret; } return ret; } //设置事务为自动提交 int mysql_AutoTran(MYSQL *mysql) { //--开启事务 int ret = mysql_query(mysql, "start transaction"); if (ret != 0) { printf("mysql_AutoTran query start err: %s\n", mysql_error(mysql)); return ret; } //--设置事务为自动提交 ret = mysql_query(mysql, UNSET_TRAN); //"set autocommit = 1" if (ret != 0) { printf("mysql_AutoTran query set err: %s\n", mysql_error(mysql)); return ret; } return ret; } //执行commit,手动提交事务 int mysql_Commit(MYSQL *mysql) { int ret = mysql_query(mysql, "COMMIT"); //提交 if (ret != 0) { printf("commit err: %s\n", mysql_error(mysql)); return ret; } return ret; } //执行rollback,回滚事务 int mysql_Rollback(MYSQL *mysql) { int ret = mysql_query(mysql, "ROLLBACK"); if (ret != 0) { printf("rollback err: %s\n", mysql_error(mysql)); return ret; } return ret; } #define DROP_SAMPLE_TABLE "DROP TABLE IF EXISTS test_table" #define CREATE_SAMPLE_TABLE "CREATE TABLE test_table(col1 INT,\ col2 VARCHAR(10),\ col3 VARCHAR(10))" #define sql01 "INSERT INTO test_table(col1,col2,col3) VALUES(10, 'AAA', 'A1')" #define sql02 "INSERT INTO test_table(col1,col2,col3) VALUES(20, 'BBB', 'B2')" #define sql03 "INSERT INTO test_table(col1,col2,col3) VALUES(30, 'CCC', 'C3')" #define sql04 "INSERT INTO test_table(col1,col2,col3) VALUES(40, 'DDD', 'D4')" int main(void) { int ret = 0; MYSQL *mysql = mysql_init(NULL); mysql = mysql_real_connect(mysql, _HOST_, _USER_, _PASSWD_, _DBNAME_, 0, NULL, 0); if (mysql == NULL) { ret = mysql_errno(mysql); printf("func mysql_real_connect() err:%d\n", ret); return ret; } printf(" --- connect ok......\n"); //执行删除表 if (mysql_query(mysql, DROP_SAMPLE_TABLE)) { fprintf(stderr, " DROP TABLE failed\n"); fprintf(stderr, " %s\n", mysql_error(mysql)); exit(0); } //执行创建表 if (mysql_query(mysql, CREATE_SAMPLE_TABLE)) { fprintf(stderr, " CREATE TABLE failed\n"); fprintf(stderr, " %s\n", mysql_error(mysql)); exit(0); } ret = mysql_OperationTran(mysql); //开启事务,并修改事务属性为手动commit if (ret != 0) { printf("mysql_OperationTran() err:%d\n", ret); return ret; } ret = mysql_query(mysql, sql01); //向表中插入第一行数据 ‘AAA’ if (ret != 0) { printf("mysql_query() err:%d\n", ret); return ret; } ret = mysql_query(mysql, sql02); //向表中插入第二行数据 ‘BBB’ if (ret != 0) { printf("mysql_query() err:%d\n", ret); return ret; } ret = mysql_Commit(mysql); //手动提交事务 if (ret != 0) { printf("mysql_Commit() err:%d\n", ret); return ret; } //////////AAA BBB 进去了。 #if 1 ret = mysql_AutoTran(mysql); // =再次= 修改事务属性为【自动】commit if (ret != 0) { printf("mysql_OperationTran() err:%d\n", ret); return ret; } #else ret = mysql_OperationTran(mysql); // =再次= 修改事务属性为【手动】commit if (ret != 0) { printf("mysql_OperationTran() err:%d\n", ret); return ret; } #endif ret = mysql_query(mysql, sql03); //向表中插入第三行数据 ‘CCC’ if (ret != 0) { printf("mysql_query() err:%d\n", ret); return ret; } ret = mysql_query(mysql, sql04); //向表中插入第四行数据 ‘DDD’ if (ret != 0) { printf("mysql_query() err:%d\n", ret); return ret; } ret = mysql_Rollback(mysql); //直接rollback操作 if (ret != 0) { printf("mysql_Rollback() err:%d\n", ret); return ret; } //rollback操作是否能回退掉CCC、DDD的值,取决于事务属性。 mysql_close(mysql); return 0; }
>make
>./08_tran
(一定要确保set autocommit=1;)
打开另一个客户端,输入(mysql>select * from test_table;)
》设置为手动提交,相当于开启事务
set autocommit=0;(需要手动执行commit; 再另一个客户端才能看到结果。)
12、课后作业
》上传文件
upload srcfile desfile
//字符串拆分函数
void splitString(const char *Src, char delim, vector<string> &vsplit);
find_first_not_of 找第一个不是的
find_first_of 找第一个是的
》(MysqlTran.h和MysqlTran.cpp)代码如下:

#pragma once #include <iostream> #include <string> #include <vector> #include <stdlib.h> #include <stdio.h> using namespace std; class CMysqlTran { public: //构造函数,初始化mysql连接 CMysqlTran(const char *Host,const char *User, const char *Pass, const char *db); //析构函数,释放mysql连接 ~CMysqlTran(); //执行sql语句,用于insert,update,delete类sql使用 int ExeSql(const char *rSql); //查询sql返回结果数,用于统计查询sql的结果集数量 int SelectDataCount(const char *rSql); //查询sql并且显示相应的结果集 int SelectData(const char *rSql); private: }; //字符串拆分函数 void splitString(const char *Src, char delim, vector<string> &vsplit);

#include "MysqlTran.h" #include <mysql.h> MYSQL *mysql = NULL; void show_result(MYSQL_RES * result);//打印结果集函数 CMysqlTran::CMysqlTran(const char *Host, const char *User, const char *Pass, const char *db) { //mysql连接的初始化 mysql = mysql_init(NULL); if (mysql == NULL){ cout << "mysql init err" << endl; exit(1); } //连接到mysql数据库 mysql = mysql_real_connect(mysql, Host, User, Pass, db, 0, NULL, 0); if (mysql == NULL){ cout << "mysql conn err" << endl; exit(1); } } CMysqlTran::~CMysqlTran() { if (mysql){ //释放连接 mysql_close(mysql); } } int CMysqlTran::ExeSql(const char *rSql) { return mysql_query(mysql, rSql); } int CMysqlTran::SelectDataCount(const char *rSql) { int RowCount = 0; //执行查询sql if (mysql_query(mysql, rSql)){ cout << "run Sql err:" << rSql << endl; return -1; } //取回结果集 MYSQL_RES *result = mysql_store_result(mysql); if (result){ //获取结果集的行数 RowCount = result->row_count; return RowCount; } return 0; } int CMysqlTran::SelectData(const char *rSql) { if (mysql_query(mysql, rSql)){ cout << "run Sql err:" << rSql << endl; return -1; } //取回结果集,打印结果集 MYSQL_RES * result = mysql_store_result(mysql); //取回结果集 if (result != NULL)//代表有需要处理的结果集 { show_result(result);//打印结果集 mysql_free_result(result);//释放结果集 } } void show_result(MYSQL_RES * result)//打印结果集函数 { unsigned int num_fields; unsigned int i; MYSQL_FIELD *fields; num_fields = mysql_num_fields(result); // 取结果集的字段个数 fields = mysql_fetch_fields(result); // 取结果集中的字段结构信息 数组 for (i = 0; i < num_fields; i++) { printf("%s\t", fields[i].name);//打印结果集的字段名 } printf("\n-----------------------------------------------------------------------\n");//华丽的分割线 //获取行 //MYSQL_ROW row = mysql_fetch_row(MYSQL_RES *result) 返回结果为NULL代表 取完 MYSQL_ROW row; while ((row = mysql_fetch_row(result)) != NULL)//循环取每一行 { for (i = 0; i < num_fields; i++)//因为知道有8列,所以可以这样写. { printf("%s\t", row[i]); } printf("\n"); } printf("-----------------------------------------------------------------------\n");//华丽的分割线 printf("%llu rows in set \n", result->row_count);//打印获得的行数 } //quit //upload 11 22 //download 22 11 //list void splitString(const char *Src, char delim, vector<string> &vsplit) { string tmp = Src; vsplit.clear(); int index = 0; size_t last = 0; last = tmp.find_first_not_of(delim, last);//找到第一个不为分隔符的字符 index = tmp.find_first_of(delim, last);//找到第一个分隔符 while (index != string::npos)//npos代表字符串的结尾 { string target = tmp.substr(last, index - last); vsplit.push_back(target); //last = index +1; last = tmp.find_first_not_of(delim, index); index = tmp.find_first_of(delim, last); } if (index == string::npos && tmp.length() > last)//到末尾了,如果整个长度大于last坐标,说明还有最后一个字符要放到vector { vsplit.push_back(tmp.substr(last)); } #if 0 cout << vsplit.size() << endl; for (size_t i = 0; i < vsplit.size(); i++) { cout << "i=" << i << "," << vsplit[i].c_str() << endl; } #endif }
五、monogodb数据库
1、monogodb数据库的安装
》RedHat/CentOS下的安装步骤
http://docs.mongodb.org/manual/tutorial/install-mongodb-on-red-hat/
》下载mongodb压缩包
http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.6.10.tgz
如果虚拟机登录输入:(可以直接下载到本地)
shell>wget http://fastdl.mongodb.org/linux/mongodb-linux-x86_64-2.6.10.tgz
也可以使用下载好的安装包: mongodb-linux-x86_64-2.6.10.gz
root用户登录
>tar -zxvf mongodb-linux-x86_64-2.6.10.gz
>mv mongodb-linux-x86_64-2.6.10 /usr/local/mongodb
>cd /usr/local/mongodb
>mkdir db
>mkdir logs
>cd bin
》增加配置文件
>vi mongodb.conf
dbpath=/usr/local/mongodb/db
logpath=/usr/local/mongodb/logs/mongodb.log
port=27017
fork=true
nohttpinterface=true
》开机自动启动mongodb
>vi /etc/rc.d/rc.local
/usr/local/mongodb/bin/mongod --config /usr/local/mongodb/bin/mongodb.conf
》建议mongo快捷启动
ln -s /usr/local/mongodb/bin/mongo /usr/local/bin/mongo
》ubuntu安装
sudo apt-get install mongodb
2、monogodbc+驱动安装
》MongoDB的 C++驱动下载源 目前有三个版本:
1)26compat
下载网址 https://github.com/mongodb/mongo-cxx-driver/tree/26compat
2)legacy
下载网址 https://github.com/mongodb/mongo-cxx-driver/tree/legacy
3)master
下载网址 https://github.com/mongodb/mongo-cxx-driver/tree/master
》Redhat/centOS下安装MongoDB的C++驱动
1)安装boost库准标准库
下载boost_1_55_0.zip:http://jaist.dl.sourceforge.net/project/boost/boost/1.55.0/boost_1_55_0.zip
2)安装PCRE c++的正则表达式的第三方库
下载pcre-8.00.tar.gz:http://jaist.dl.sourceforge.net/project/pcre/pcre/8.00/pcre-8.00.tar.gz
3)scons 编译驱动(因为mongoDB驱动编译不是用CMake,需要下载scons)
下载scons-2.5.0-1.noarch.rpm:http://jaist.dl.sourceforge.net/project/scons/scons/2.5.0/scons-2.5.0-1.noarch.rpm
4)编译驱动程序
下载mongodb的驱动程序:http://downloads.mongodb.org/cxx-driver/mongodb-linux-x86_64-v2.2-latest.tgz
》编译testmongo.cpp

//g++ -o testmongo testmongo.cpp -lmongoclient -lboost_thread -lboost_filesystem -lboost_program_options -L/home/itcast/driver/boost/lib -L/home/itcast/driver/mongo/lib -I/home/itcast/driver/mongo/include -I/home/itcast/driver/boost/include #include <iostream> #include "mongo/client/dbclient.h" char dbhost[20]="localhost"; using namespace mongo; using namespace std; void printIfAge(DBClientConnection& c, int age) { auto_ptr<DBClientCursor> cursor = c.query("tutorial.persons", QUERY( "age" << age ).sort("name") ); while( cursor->more() ) { BSONObj p = cursor->next(); cout << p.getStringField("name") << endl; } } void run() { DBClientConnection c; c.connect(dbhost); cout << "connected ok" << endl; BSONObj p = BSON( "name" << "Joe" << "age" << 33 ); c.insert("tutorial.persons", p); /**< 向person表中插入数据 */ p = BSON( "name" << "Jane" << "age" << 40 ); c.insert("tutorial.persons", p); p = BSON( "name" << "Abe" << "age" << 33 ); c.insert("tutorial.persons", p); p = BSON( "name" << "Samantha" << "age" << 21 << "city" << "Los Angeles" << "state" << "CA" ); c.insert("tutorial.persons", p); c.ensureIndex("tutorial.persons", fromjson("{age:1}")); cout << "count:" << c.count("tutorial.persons") << endl; /**< 显示person表中的数据数目 */ auto_ptr<DBClientCursor> cursor = c.query("tutorial.persons", BSONObj()); while( cursor->more() ) { cout << cursor->next().toString() << endl; } cout << "\nprintifage:\n"; printIfAge(c, 33); } int main(int argc,char *argv[]) { if(argc == 2) { memset(dbhost,0x00,sizeof(dbhost)); strcpy(dbhost,argv[1]); printf("connect to dbhost:[%s]\n",dbhost); } else { printf("connect to dbhost:[%s]\n",dbhost); printf("if you need to connet to remote service,please input ip!\n"); } try { run(); } catch( DBException &e ) { cout << "caught " << e.what() << endl; } return 0; }
>g++ -o testmongo testmongo.cpp -lmongoclient -lboost_thread -lboost_filesystem -lboost_program_options -L/home/itcast/driver/boost/lib -L/home/itcast/driver/mongo/lib -I/home/itcast/driver/mongo/include -I/home/itcast/driver/boost/include
>mongo(没有密码,直接登录)
>show dbs
打开另一个终端,切换到相应目录下,执行>./testmongo
再切换到原终端
在学习数据库编程总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
posted on 2020-07-20 16:06 Alliswell_WP 阅读(603) 评论(2) 编辑 收藏 举报
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 理解Rust引用及其生命周期标识(下)
· 从二进制到误差:逐行拆解C语言浮点运算中的4008175468544之谜
· .NET制作智能桌面机器人:结合BotSharp智能体框架开发语音交互
· 软件产品开发中常见的10个问题及处理方法
· .NET 原生驾驭 AI 新基建实战系列:向量数据库的应用与畅想
· 2025成都.NET开发者Connect圆满结束
· 后端思维之高并发处理方案
· 千万级大表的优化技巧
· 在 VS Code 中,一键安装 MCP Server!
· 10年+ .NET Coder 心语 ── 继承的思维:从思维模式到架构设计的深度解析