C中级 MariaDB Connector/C API 编程教程

引言 - 环境搭建

  首先开始环境搭建. 主要在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

时间: 2024-12-31 06:10:35

C中级 MariaDB Connector/C API 编程教程的相关文章

Flink Program Guide (2) -- DataStream API编程指导 -- For Java

v\:* {behavior:url(#default#VML);} o\:* {behavior:url(#default#VML);} w\:* {behavior:url(#default#VML);} .shape {behavior:url(#default#VML);} 张安 张安 2 1 2016-08-02T10:56:00Z 2016-08-02T10:56:00Z 1 2945 16790 139 39 19696 16.00 false false false false

Windows API 编程学习记录&lt;二&gt;

恩,开始写Windows API编程第二节吧. 上次介绍了几个关于Windows API编程最基本的概念,但是如果只是看这些概念,估计还是对Windows API不是很了解.这节我们就使用Windows API 让大家来了解下Windows API的用法. 第一个介绍的Windows API 当然是最经典的MessageBox,这个API 的作用就是在电脑上显示一个对话框,我们先来看看这个API的定义吧: int WINAPI MessageBox(HWND hWnd, LPCTSTR lpTe

Windows API 编程学习记录&lt;三&gt;

恩,开始写API编程的第三节,其实马上要考试了,但是不把这节写完,心里总感觉不舒服啊.写完赶紧去复习啊       在前两节中,我们介绍了Windows API 编程的一些基本概念和一个最基本API函数 MessageBox的使用,在这节中,我们就来正式编写一个Windows的窗口程序. 在具体编写代码之前,我们必须先要了解一下API 编写窗口程序具体的三个基本步骤:             1. 注册窗口类:             2.创建窗口:             3.显示窗口: 恩,

聚币网API使用教程 demo

原文 http://30daydo.com/article/181 目前还在完善,等功能完善了,就更新到csdn. 更新 2017-05-27 官方有API的文档,可是这个文档就像一个草稿一样,两个基本例子都没有. 所以自己摸索一下,自己写一个现成的例子给大家,可以有个参考. 首先看一下官方的API文档: 一.API使用说明 1.请求过程说明 1.1 构造请求数据,用户数据按照Jubi提供的接口规则,通过程序生成签名和要传输给Jubi的数据集合: 1.2 发送请求数据,把构造完成的数据集合通过P

Linux统系统开发11 Socket API编程2 多进程 多线程 高并发处理

[本文谢绝转载原文来自http://990487026.blog.51cto.com] <纲要> Linux统系统开发11 Socket API编程2 多进程 多线程 高并发处理 UDP服务器 客户端最小模型,处理字符转大写 TCP 多进程并发服务器模型,为每个客户端开启一个进程: TCP 多线程服务器模型,使用wrap函数封装 作业: ---------------------------------------------------- UDP服务器 客户端最小模型,处理字符转大写 [em

sqlite3 C API编程

1,创建测试数据库,表; 2,数据库连接对象函数 sqlite3是数据库连接对象(database connection object),用来操作数据库(operator DBs);打开数据库对象函数(open db object function),int sqlite3_open(const char *filename, //数据库文件名,指我们在CLI 输入[[email protected] ~]#sqlite3 /ruiysqlite3 **ppDb //创建的数据库连接对象); i

超全面的.NET GDI+图形图像编程教程

本篇主题内容是.NET GDI+图形图像编程系列的教程,不要被这个滚动条吓到,为了查找方便,我没有分开写,上面加了目录了,而且很多都是源码和图片~ (*^_^*) 本人也为了学习深刻,另一方面也是为了分享给大家,纯手工码了好几天的字,喜欢的表忘了点赞哦~给点小小的动力~ 目录: <GDI+绘图基础> 1 GDI+概述 2 Graphics类 2.1 Graphics类的方法成员 2.2 引用命名空间 3 常用画图对象 3.1 Pen类 3.2 Color结构 3.3 Font类 3.4 Bru

ASP.NET Web API系列教程(目录)(转)

注:微软随ASP.NET MVC 4一起还发布了一个框架,叫做ASP.NET Web API.这是一个用来在.NET平台上建立HTTP服务的Web API框架,是微软的又一项令人振奋的技术.目前,国内对此关注的人似乎还不多,有关ASP.NET Web API的文章也不多见.为此,本人打算对微软ASP.NET Web API官方网站上的一些教程进行翻译,以期让更多的国人了解.学习和使用这项ASP.NET Web API. ASP.NET Web API系列教程目录 Introduction:Wha

Linux统系统开发12 Socket API编程3 TCP状态转换 多路IO高并发select poll epoll udp组播 线程池

[本文谢绝转载原文来自http://990487026.blog.51cto.com] Linux统系统开发12 Socket API编程3 TCP状态转换 多路IO高并发select  poll  epoll udp组播 线程池 TCP 11种状态理解: 1,客户端正常发起关闭请求 2,客户端与服务端同时发起关闭请求 3,FIN_WAIT1直接转变TIME_WAIT 4,客户端接收来自服务器的关闭连接请求 多路IO转接服务器: select模型 poll模型 epoll模型 udp组播模型 线