练习:熟悉上述预处理类工作模式,模拟精简一个将时间插入数据库的程序。将时间存入数据库有两种方式: 1. 使用SQL语句方式
2. 预处理环境句柄变量方式存入
提示:
MYSQL_TIME ?ts; 浏览头文件 mysql_time.h 熟悉MYSQL_TIME结构体。
MYSQL_BIND ?bind[3];
MYSQL_STMT ?*stmt;
可直接使用SQL语句提前创建表test_table2,也可以使用mysql_query函数来创建。
create table test_table2 (date_field date, time_field time, timestamp_field timestamp);
char query[1024] = "INSERT INTO test_table2(date_field, time_field, timestamp_field) VALUES(?,?,?)";
stmt = mysql_stmt_init(mysql);
MYSQL_TIME 是一个结构体,使用typedef定义。位于mysql_time.h文件中。
API参考:refman-5.6-en.a4.pdf手册25.2.10. 日期和时间值的C API处理
多查询执行的C API函数
一次性执行多条SQL语句,包括select、drop、update、create等。 如:
mysql_query(mysql,"DROP TABLE IF EXISTS test_table;\
????????????? ???CREATE TABLE test_table(id INT);\
?????????? ???INSERT INTO test_table VALUES(10);\
????????? ???UPDATE test_table SET id=20 WHERE id=10;\
????????? ???SELECT * FROM test_table;\
?????????? ???DROP TABLE test_table");
文档:25.2.9. 多查询执行的C API处理。中文文档只有demo框架。查阅对应英文文档refman-5.6-en.a4.pdf。关键字Multiple 23.8.17
注意:打桩函数——函数接口
if (mysql_real_connect (mysql, host_name, user_name, password,
db_name, port_num, socket_name, CLIENT_MULTI_STATEMENTS) == NULL)
CLIENT_MULTI_STATEMENTS:客户端通知Server,将要发送多个SQL语句。
mysql_field_count(mysql):影响的行数。 如:
当select * from dept; 执行结束,提示:“5 rows in set” 表示影响了4行。
当Create一张表, 执行结束,提示:“Query OK, 0 rows affected (0.01 sec)”
当delete一行, 执行结束,提示:“Query OK, 1 row affected (0.00 sec)”
mysql_field_count函数调用后会将影响的行数保存到句柄 mysql 中。
将帮助文档中的demo导入程序,分析与我们之前掌握的API函数间的区别与联系:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mysql.h"
void process_result_set(MYSQL *mysql, MYSQL_RES *result)
{
int i, num;
num = mysql_field_count(mysql);
MYSQL_FIELD *fields = NULL;
fields = mysql_fetch_fields(result);
for (i = 0; i < num; i++) {
printf("%10s\t", fields[i].name);
}
printf("\n");
MYSQL_ROW row = NULL;
while ((row = mysql_fetch_row(result))) {
for (i = 0; i < num; i++) {
printf("%10s\t", row[i]);
}
printf("\n");
}
}
int main(void)
{
int ret = 0, status = 0;
MYSQL_RES *result = NULL;
MYSQL *mysql = mysql_init(NULL);
if (mysql == NULL) {
//unsigned int mysql_errno(MYSQL *mysql)
ret = mysql_errno(mysql);
printf("mysql_init err:%d\n", ret);
return ret;
}
mysql = mysql_real_connect(mysql, "localhost", "root", "123456", "mydb61", 0, NULL, CLIENT_MULTI_STATEMENTS);
if (mysql == NULL) {
ret = mysql_errno(mysql);
printf("mysql_init err:%d\n", ret);
return ret;
}
/以下为demo源码//
/* execute multiple statements */
status = mysql_query(mysql,"DROP TABLE IF EXISTS test_table;\
CREATE TABLE test_table(id INT);\
INSERT INTO test_table VALUES(10);\
UPDATE test_table SET id=20 WHERE id=10;\
SELECT * FROM test_table;");
DROP TABLE test_table
if (status)
{
printf("Could not execute statement(s)");
mysql_close(mysql);
exit(0);
}
/* process each statement result */
do {
/* did current statement return data? */
result = mysql_store_result(mysql);
if (result)
{
/* yes; process rows and free the result set */
process_result_set(mysql, result);
mysql_free_result(result);
}
else /* no result set or error */
{
if (mysql_field_count(mysql) == 0)
{
printf("%lld rows affected\n",
mysql_affected_rows(mysql));
}
else /* some error occurred */
{
printf("Could not retrieve result set\n");
break;
} }
/* more results? -1 = no, >0 = error, 0 = yes (keep looping) */
if ((status = mysql_next_result(mysql)) > 0)
printf("Could not execute statement\n");
printf("------------status: %d\n", status);
} while (status == 0);
mysql_close(mysql);
return 0;
}
process_result_set函数是文档中给我们预留的打桩函数,需要我们在使用的过程中,自己实现它。
函数实现就是借助mysql和result两个参数打印一条sql语句查询到的结果集到屏幕。
可以直接使用mysq_tool.c中if (strncmp(sqlbuf, "select", 6) == 0 || strncmp(sqlbuf, "SELECT", 6) == 0)内的代码。“获取结果集”片段可以删除。“释放结果集”片段可以删除。API示例中含有该部分内容。
常见错误:在process_result_set函数实现中,不要使用mysql_store_result(mysql)再次获取结果集, 该result已经在API函数接口传入,直接使用参数result即可。否则会出现【段错误】。
测试MySQL中事务的特性。
MySQL的事务的默认自动提交的,每执行一个sql语句都自动commit
Oracle的事务是自动打开的(以你执行的一条DML语句为标志),但每次执行需要手动commit
在程序中设置autocommit修改MySQL事务的属性。
set autocommit = 0 禁止自动提交
set autocommit = 1 开启自动提交MySQL中InnoDB引擎才支持事务默认自动提交机制。MYISAM引擎不支持。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mysql.h"
#define SET_TRAN "SET AUTOCOMMIT=0" ? //手动commit
#define UNSET_TRAN "SET AUTOCOMMIT=1" //自动commit
//设置事务为手动提交
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);
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);
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, "localhost", "root", "123456", "mydb2", 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;
}
ret = mysql_AutoTran(mysql); // =再次= 修改事务属性为【自动】commit
if (ret != 0) {
printf("mysql_OperationTran() err:%d\n", ret);
return ret;
}
ret = mysql_OperationTran(mysql); // =再次= 修改事务属性为【手动】commit
if (ret != 0) {
printf("mysql_OperationTran() err:%d\n", ret);
return ret;
}
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;
}