引言 - 環境搭建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