程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C中級 MariaDB Connector/C API 編程教程,mariadbconnector

C中級 MariaDB Connector/C API 編程教程,mariadbconnector

編輯:關於C語言

C中級 MariaDB Connector/C API 編程教程,mariadbconnector


引言 - 環境搭建

  首先開始環境搭建. 主要在Window 10 + Visual Studio 2015 上構建使用 mariadb connector/c api 進行數據操作開發.

為什麼選擇在window上搭建開發環境呢? 最核考慮是 更 方便 看源碼!!!

  記得以前也寫過一個在ubuntu上mariadb api開發教程, 有興趣也可以參照看一下, 數據庫層api是一樣的.

   c基礎 mariadb處理簡單案例 http://www.cnblogs.com/life2refuel/p/5574544.html

本文重點講解

  1. MariaDB在window 環境上搭建.

  2. MariaDB Connector/C API 的 HelloWorld

  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, "[email protected]", "帝王大廈,長江口", -1, { 2, "09.10 教師節快樂" } },
        { 0, "09.10 教師節快樂", 1, "[email protected]", "各大地毯,松花江", 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

     

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved