引言 - 环境搭建html
首先开始环境搭建. 主要在Window 10 + Visual Studio 2015 上构建使用 mariadb connector/c api 进行数据操做开发.mysql
为何选择在window上搭建开发环境呢? 最核考虑是 更 方便 看源码!!!sql
记得之前也写过一个在ubuntu上mariadb api开发教程, 有兴趣也能够参照看一下, 数据库层api是同样的.数据库
c基础 mariadb处理简单案例 http://www.cnblogs.com/life2refuel/p/5574544.html编程
本文重点讲解ubuntu
1. MariaDB在window 环境上搭建.windows
2. MariaDB Connector/C API 的 HelloWorldapi
3. Blob数据的insert 和 select安全
OK, 那开始吧, 先介绍须要下载的资源种子
1. MaraDB window : https://mariadb.com/my_portal/download/mariadb-enterprise#windows数据结构
2. MariaDB Connector/C Download https://mariadb.com/kb/en/mariadb/mariadb-connector-c/
有了这些资源, 开始解压和安装, 先弄mariadb的压缩包, 解压完毕以后是下面这样 . 我放在了E盘下.
再设置一下 Path变量 (window 10 Path变量设置以下图)
环境变量设置好了以后安装 Conector/C 库的安装包 , 安装完毕后在C盘, MaeiaDB文件夹路径下会遇到如下文件目录
如今基本软件和驱动都已经安装完毕了. 后面任务是让mariadb 服务启动起来, 打开管理员模式下的cmd窗口, 执行
:: 开启mariadb 服务, 须要管理员权限 mysqld.exe --install mariadb net start mariadb
扩充一点, 对于暂停, 卸载, 删除 命令以下
:: 下面是中止,卸载,删除服务命令 net stop mariadb mysqld.exe --remove mariadb sc delete mariadb
是否是很简单, 按照上面作了以后, 基本上mariadb 服务就已经启动起来了(前提脸不黑, O(∩_∩)O哈哈~).
开始执行下面, sql脚本, 建立用户和构建测试数据表
mysql -uroot -p -- 开始使用test数据库, 进行数据测试 use test; create table tb_user ( id int unsigned not null auto_increment comment '员工编号', name varchar(20) not null comment '员工姓名', sex tinyint not null comment '员工性别, 0女士, 1男士, 其它扩展', email varchar(30) not null comment '员工邮箱', department varchar(50) not null comment '员工所在部门', employtime int unsigned not null default 0 comment '入职时间', salary int not null default 0 comment '员工工资', ext blob comment '后期使用, 扩展数据', primary key(id) ) engine = innodb default charset = latin1; -- 为用户建立权限 -- 为 seluser 查询权限 -- 为 noruser 所用权限 -- 开始建立用户, 并刷新 create user 'seluser'@'localhost' identified by '7seluser'; create user 'noruser'@'localhost' identified by '7noruser'; flush privileges; -- 设置不一样用户权限 grant select on test.* to 'seluser'@'localhost'; grant all on test.* to 'noruser'@'localhost';
建立了两个用户, seluser和noruser, 分别具备test数据库下面读权限和全部权限. 扯一点, 权限管理实际上是软件开发中一个共性, 哪里都须要.
由于权限它是权力在虚拟系统中缩影. 后面说一下 ,为何用 latin1不用 utf-8. 这也是个''坑'', 推荐看看下面资料.
编码ascii latin1 utf8 简介 http://blog.sina.com.cn/s/blog_5edf2a9f0100sicm.html
这步完成后, 就能经过mariadb命令进行操做了, 如同下面操做内容. 最终软件环境就搭建完毕了.
前言 - 环境测试, 搭建HelloWorld Demo
目前能够开始着手编程开发了, 主要依赖的圣经是下面官网API Functions 说明. 咱们所须要的一切均可以从下面内容中找见.
MariaDB Connector/C API Functions https://mariadb.com/kb/en/mariadb/mariadb-connectorc-api-functions/
那行, 打开VS, 建立控制台程序. 开始添加库目录, 头文件目录等. 参照下面流程先在项目中添加 引用目录
再添加静态库目录
再为此项目指定导入静态库文件
其实VS 项目管理最核心文件就是*.sln 和 *. vcxproj 文件. 例如打开其中一个文件, 看见下面的XML组织管理结构. 很清晰的看出VS 项目是如何管理引用, 资源等公有内容的.
以上完成后, 如今先写一个 HelloWorld的Demo mariadb_heoo.c
#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <mysql.h> #define _STR_MHOST "localhost" #define _STR_MUSER "seluser" #define _STR_MPASSWD "7seluser" #define _STR_MDB "test" static inline void _mysql_check(MYSQL * con) { fprintf(stderr, "%s\n", mysql_error(con)); mysql_close(con); exit(EXIT_FAILURE); } /* * 这里测试从mariadb 数据中拉取数据 * ip : localhost * name : seluser * passwd : 7seluser */ int main(int argc, char * argv[]) { // 建立数据链接对象, 须要和 mysql_close成对出现 MYSQL * con = mysql_init(NULL); if (NULL == con) { fputs("main mysql_init NULL == con! error !\n", stderr); exit(EXIT_FAILURE); } if (!mysql_real_connect(con, _STR_MHOST, _STR_MUSER, _STR_MPASSWD, _STR_MDB, 0, NULL, 0)) _mysql_check(con); if (mysql_query(con, "show tables;")) _mysql_check(con); puts("mariadb is connect and run succesed!"); /* * 这里拉取数据 */ MYSQL_RES * res = mysql_store_result(con); if (NULL == res) _mysql_check(con); MYSQL_ROW row; unsigned rlen = mysql_num_fields(res); printf("mariadb now row length = %u\n", rlen); // 打印行数据 while ((row = mysql_fetch_row(res))) { for (unsigned i = 0; i < rlen; ++i) printf("%s ", row[i]); putchar('\n'); } // 释放结果内存 mysql_free_result(res); // 释放mysql客户端连接对象 mysql_close(con); getchar(); return 0; }
这个演示Demo 主要是拉取 show tables; 返回数据. 上面都是开发中套路, 参照注释, 代码容易明爱. 主要流程包括 初始化, 连接, 请求查询, 解析结果, 关闭.
当咱们运行的时候, 还须要添加上动态库 libmariadb.dll
运行最终结果以下, 到这里基础Hello World就大功告成了.
正文 - 实战blob数据的insert and select
很恭喜到了这里, 以上前戏基本完毕了. 这里先把前面一个关于 latin1一个坑补上. 这个坑形成缘由是, 传统C/C++ 使用的是ascii码,
对于汉字转utf-8麻烦, 而latin1是对ascii码扩充, 因此汉字也能正常显示. 这也是不少老系统或框架在和DB交互的时候, 使用latin1编码的缘由.
此刻开始blob 练习演示. 先简单回顾一下 mariadb中经常使用的数据类型, 了解blob是啥.
类 型 占用字节数 无符号数的取值范围 有符号数的取值范围 tinyint 1 0-255 -128-127 int 4 0-(2^32-1) -(2^32/2)-(2^32/2-1) bigint 8 0-(2^64-1) -(2^64/2)-(2^64/2-1) varchar 1-65535 类型的长度是可变,其取值范围为0-65535。 blob 65k 保存二进制数据
对于mariadb 的二进制blob类型 须要使用下面api构建 ,
unsigned long STDCALL mysql_real_escape_string(MYSQL *mysql, char *to,const char *from, unsigned long length);
内部序列化成其内部保存的''串''. 那咱们依赖test.workers表插入数据 . 首先定义对映的一种数据结构以下
#define _INT_WNAME (63) #define _INT_WEMAIL (127) #define _INT_WDEPAR (255) // workers 扩展信息, 当作其另外一半吧 struct workers_ext { unsigned int id; // 惟一标识id char name[_INT_WNAME + 1]; // 姓名信息 }; // 对应数据库 test.workers 表内容 struct workers { unsigned int id; // 惟一标识id char name[_INT_WNAME + 1]; // 姓名信息 char sex; // 0 女士, 1男士 char email[_INT_WEMAIL + 1]; // 邮箱 char department[_INT_WDEPAR + 1]; // 部门介绍 int salary; // 基本工资 struct workers_ext ext; // 扩展数据 };
这里struct workers_ext 结构就是对映test.workers 中 ext blob字段. 项目的业务例子参照 mariadb_insert.c
#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <mysql.h> #define LEN(arr) (sizeof(arr) / sizeof(*(arr))) #define _STR_MHOST "localhost" #define _STR_MUSER "noruser" #define _STR_MPASSWD "7noruser" #define _STR_MDB "test" static inline void _mysql_check(MYSQL * con) { fprintf(stderr, "%s\n", mysql_error(con)); mysql_close(con); exit(EXIT_FAILURE); } #define _INT_WNAME (63) #define _INT_WEMAIL (127) #define _INT_WDEPAR (255) // workers 扩展信息, 当作其另外一半吧 struct workers_ext { unsigned int id; // 惟一标识id char name[_INT_WNAME + 1]; // 姓名信息 }; // 对应数据库 test.workers 表内容 struct workers { unsigned int id; // 惟一标识id char name[_INT_WNAME + 1]; // 姓名信息 char sex; // 0 女士, 1男士 char email[_INT_WEMAIL + 1]; // 邮箱 char department[_INT_WDEPAR + 1]; // 部门介绍 int salary; // 基本工资 struct workers_ext ext; // 扩展数据 }; #define _INT_WINSERTSQL (6*1024) // 默认最大6k, 程序决定, 不是线程安全 // 获得最终insert 拼接的字符串 static void _workers_get_insertsql(MYSQL * con, struct workers * worker) { char query[_INT_WINSERTSQL + 1]; assert(con && worker); // 保存扩展数据, 2 * size + 1 是api规定的, 返回最终编码长度 char chunk[2 * sizeof(struct workers_ext) + 1]; mysql_real_escape_string(con, chunk, (const char *)&worker->ext, sizeof(struct workers_ext)); int len = snprintf(query, LEN(query), "insert into workers(name, sex, email, department, salary, ext) " "values('%s', %d, '%s', '%s', %d, '%s');", worker->name, worker->sex, worker->email, worker->department, worker->salary, chunk); if (len > _INT_WINSERTSQL) { fprintf(stderr, "_workers_get_insertsql snprintf len = %d is too long!\n", len); return; } // 这里能够插入到数据库 if (mysql_real_query(con, query, len)) _mysql_check(con); } /* * 这里测试写入复杂数据到mariadb中, 例如插入blob数据 */ int mariadb_insert(int argc, char * argv[]) { // 建立数据链接对象, 须要和 mysql_close成对出现 MYSQL * con = mysql_init(NULL); if (NULL == con) { fputs("main mysql_init NULL == con! error !\n", stderr); exit(EXIT_FAILURE); } // 开始建立TCP常链接对象 if (!mysql_real_connect(con, _STR_MHOST, _STR_MUSER, _STR_MPASSWD, _STR_MDB, 0, NULL, 0)) _mysql_check(con); // 每次插入就只重置2条数据 if (mysql_query(con, "truncate table workers;")) _mysql_check(con); struct workers workers[] = { { 0, "09.09 毛无敌诞辰", 1, "666666@666.com", "帝王大厦,长江口", -1, { 2, "09.10 教师节快乐" } }, { 0, "09.10 教师节快乐", 1, "55555@555.com", "各大地毯,松花江", 555, { 1, "09.09 毛无敌诞辰" } }, }; // 开始插入数据 for (int i = 0; i < LEN(workers); ++i) _workers_get_insertsql(con, workers + i); puts("mariadb localhost test.workers reset is succesed!"); // 释放mysql客户端连接对象 mysql_close(con); getchar(); return 0; }
上面演示中主要执行插入代码见 _workers_get_insertsql 函数, 完成sql语句的拼接, 和query查询操做. 最终的插入结果
一些正常, 经过上面例子学习, 以为应该对于mariadb 的 connector/c 驱动 api 有点头绪了. 仍是很容易理解的, 由于没有转弯的地方, 很直白.
O(∩_∩)O哈哈~
数据构建好了, 天然数据的查询也要有呢. 参照 mariadb_select.c
#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <mysql.h> #define _STR_MHOST "localhost" #define _STR_MUSER "seluser" #define _STR_MPASSWD "7seluser" #define _STR_MDB "test" static inline void _mysql_check(MYSQL * con) { fprintf(stderr, "%s\n", mysql_error(con)); mysql_close(con); exit(EXIT_FAILURE); } #define _INT_WNAME (63) #define _INT_WEMAIL (127) #define _INT_WDEPAR (255) // workers 扩展信息, 当作其另外一半吧 struct workers_ext { unsigned id; // 惟一标识id char name[_INT_WNAME + 1]; // 姓名信息 }; // 对应数据库 test.workers 表内容 struct workers { unsigned id; // 惟一标识id char name[_INT_WNAME + 1]; // 姓名信息 char sex; // 0 女士, 1男士 char email[_INT_WEMAIL + 1]; // 邮箱 char department[_INT_WDEPAR + 1]; // 部门介绍 unsigned employtime; // 入职时间 int salary; // 基本工资 struct workers_ext ext; // 扩展数据 }; /* * 这里测试写入复杂数据到mariadb中, 例如插入blob数据 */ int mariadb_select(int argc, char * argv[]) { // 建立数据链接对象, 须要和 mysql_close成对出现 MYSQL * con = mysql_init(NULL); if (NULL == con) { fputs("main mysql_init NULL == con! error !\n", stderr); exit(EXIT_FAILURE); } // 开始建立TCP常链接对象 if (!mysql_real_connect(con, _STR_MHOST, _STR_MUSER, _STR_MPASSWD, _STR_MDB, 0, NULL, 0)) _mysql_check(con); // 这里读取数据 if (mysql_query(con, "select * from workers;")) _mysql_check(con); /* * 这里拉取数据 */ MYSQL_RES * res = mysql_store_result(con); if (NULL == res) _mysql_check(con); MYSQL_ROW row; // 打印行数据 while ((row = mysql_fetch_row(res))) { // 获得各个列长度 unsigned long * clens = mysql_fetch_lengths(res); if(NULL == clens) _mysql_check(con); // 获得最后一个数据返回 struct workers worker; worker.id = (unsigned)strtoul(row[0], NULL, 0); strcpy(worker.name, row[1]); worker.sex = (char)atoi(row[2]); strcpy(worker.email, row[3]); strcpy(worker.department, row[4]); worker.employtime = (unsigned)strtoul(row[5], NULL, 0); worker.salary = atoi(row[6]); memcpy(&worker.ext, row[7], clens[7]); // 简单打印数据 printf("{ %u, '%s', %d, '%s', '%s', %u, %d, { %u, '%s' } }\n", worker.id, worker.name, worker.sex, worker.email, worker.department, worker.employtime, worker.salary, worker.ext.id, worker.ext.name); } // 释放结果内存 mysql_free_result(res); // 释放mysql客户端连接对象 mysql_close(con); getchar(); return 0; }
简单说明一下 worker.id = (unsigned)strtoul(row[0], NULL, 0); 这行代码, 咱们先看一下 strtoul 原型
_Check_return_ _ACRTIMP unsigned long __cdecl strtoul( _In_z_ char const* _String, _Out_opt_ _Deref_post_z_ char** _EndPtr, _In_ int _Radix );
返回值存在 unsigned, 这个很重要. 由于有符号和无符号数值之间转换存在符号位问题. 上面作法采用高精度的无符号转过来, 精度不损失.符号位不参与影响.
最终的效果以下
oh Yeah!
一切都搞定了, 经过这些步骤练习, 关于mariadb connector/c api funciton 基本操做, 还有如何打渔已经都有些眉目了. 之后那就看之后的复杂业务了.
遇到, 只须要多查查官网API说明就能迎刃而解. 固然最重要的仍是勤思考, 多动手.
后记 - 若是还有明天, 你要怎样装扮你的脸
错误是不免的, 发现会及时更正....
Here We Are Again http://music.163.com/#/song?id=27876900