C++ 访问MySql 接口封装

一般操作mysql可以直接使用sql语句来建立数据库,修改、查询以及删除等。如果在程序里想要访问数据库又该如何操作呢?下文便是一个简单的例子,能够完成基本的操作,可在此基础上扩充,这个例子也是我将一个开源项目对mysql的部分操作提取出来,加以修改而来。
环境要求linux 平台支持 C++11 安装 mysql 、mysql-devel以及mysqlclient。
源码地址
https://github.com/jeremy505/mysql.git
简单示例创建如下数据表:
C++ 访问MySql 接口封装_第1张图片

C++ 访问MySql 接口封装_第2张图片
先上代码

#include "base/Logging.h"
#include "base/Singleton.h"
#include "mysql/MysqlManager.h"
int main(int argc, char* argv[])
{
    Logger::setLogLevel(Logger::INFO);
    // Logger::setLogLevel(Logger::FATAL);

    const char* dbserver = "localhost";
    const char* dbuser = "root";
    const char* dbpassword = "jeremy";
    const char* dbname = "People";
    CMysqlManager pmysql = Singleton::Instance();
    if (!pmysql.Init(dbserver, dbuser, dbpassword, dbname))
    {
         LOG_FATAL << "Init mysql failed, please check your database config..............";
    }

    // 初始化表
    // 1. t_user
    STableInfo info;
    info.m_strName = "friend";
    info.m_mapField["id"] = { "id", "bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID'", "bigint(20)" };
    info.m_mapField["name"] = { "name", "varchar(64) NOT NULL COMMENT '名字'", "varchar(64)" };
    info.m_mapField["age"] = { "age", "int(10) NOT NULL COMMENT '年龄'", "int(10)" };
    info.m_mapField["phonenumber"] = { "phonenumber", "varchar(64) DEFAULT NULL COMMENT '电话'", "varchar(64)" };


    info.m_strKeyString = "PRIMARY KEY (id)";
    pmysql.DeleteData("DELETE FROM " + info.m_strName);
    pmysql.InitSTableInfo(info);

    if(!pmysql.CreateTable())
    {
        LOG_FATAL << "mysql CreateTable failed, please check your table config..............";
    }

    STableData data;

    data.m_strName = "friend";
    data.m_mapData["id"] =  "1";
    data.m_mapData["name"] = "\"jeremy\"";
    data.m_mapData["age"] = "22";
    data.m_mapData["phonenumber"] = "12221221";

    if(!pmysql.InsertData(data))
    {
        LOG_WARN << "CMysqlManager::InsertData, data insert failed";
    }

    std::shared_ptr poConn = pmysql.GetCurpoConn();

    char sql[256] = { 0 };
    snprintf(sql, 256, "SELECT name, age, phonenumber FROM friend");
    QueryResult* pResult = poConn->Query(sql);
    if (NULL == pResult)
    {
        LOG_INFO << "UserManager::Query error, db=" << dbname;
        return false;
    }

    struct People_atrribute{
        string name;
        int age;
        string phonenumber;
    };

    People_atrribute someone;
    while (true)
    {
        Field* pRow = pResult->Fetch();
        if (pRow == NULL)
            break;

        someone.name = pRow[0].GetString();
        someone.age = pRow[1].GetInt32();
        someone.phonenumber = pRow[2].GetString();
        LOG_INFO << pRow[0].GetString() << "-" << pRow[1].GetInt32() << "-" << pRow[2].GetString();
        LOG_INFO << "name= " << someone.name
                 << "  age= " << someone.age
                 << "  phonenumber= " << someone.phonenumber;
        if (!pResult->NextRow())
        {
            break;
        }
    }

    pResult->EndQuery();

    return 0;
}

这里的日志接口主要是来自陈硕的网络库muduo中的日志,设计非常好,有兴趣的可以仔细研究一下,程序开头设置日志等级,另外一种方式是获取系统环境变量——getenv,如下

  if (::getenv("MUDUO_LOG_TRACE"))
    return Logger::TRACE;
  else if (::getenv("MUDUO_LOG_DEBUG"))
    return Logger::DEBUG;
  else
    return Logger::INFO;

如果定义 MUDUO_LOG_TRACE= 1 ,日志等级就是TRACE,
定义 MUDUO_LOG_DEBUG= 1 ,日志等级就是DEBUG,默认 INFO,可使用export 暂时增加环境变量,或者在启动脚本中永久增加,使环境变量的好处是不用修改重新编译程序,又或者可以采用在程序启动时传参输的做法。
数据库操作接口使用的 设计模式中的单件类:

    const char* dbserver = "localhost";
    const char* dbuser = "root";
    const char* dbpassword = "jeremy";
    const char* dbname = "People";
    CMysqlManager pmysql = Singleton::Instance();
    if (!pmysql.Init(dbserver, dbuser, dbpassword, dbname))
    {
         LOG_FATAL << "Init mysql failed, please check your database config..............";
    }

以上代码的功能是指定hostname, user , password以及要连接的数据库 People, 如果数据库People不存在就创建。类CMysqlManager 包含初始化连接、创建表信息、建表、插入以及删除接口:

    bool Init(const char* host, const char* user, const char* pwd, const char* dbname);
    void InitSTableInfo(const STableInfo& info);
    bool CreateTable(void);
    bool InsertData(const STableData& data);
    bool DeleteData(const string& cmd);

这些接口只是部分封装了MySql接口C API函数,能实现简单的功能,可以看看代码的实现。
先附上代码日志信息,便于后文说明:
C++ 访问MySql 接口封装_第3张图片
现在在数据库People 创建数据表 friend, 表friend包含 Id、name、age、phonenumber:

    // 初始化表
    // 1. t_user
    STableInfo info;
    info.m_strName = "friend";
    info.m_mapField["id"] = { "id", "bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID'", "bigint(20)" };
    info.m_mapField["name"] = { "name", "varchar(64) NOT NULL COMMENT '名字'", "varchar(64)" };
    info.m_mapField["age"] = { "age", "int(10) NOT NULL COMMENT '年龄'", "int(10)" };
    info.m_mapField["phonenumber"] = { "phonenumber", "varchar(64) DEFAULT NULL COMMENT '电话'", "varchar(64)" };


    info.m_strKeyString = "PRIMARY KEY (id)";
    pmysql.DeleteData("DELETE FROM " + info.m_strName);
    pmysql.InitSTableInfo(info);

    if(!pmysql.CreateTable())
    {
        LOG_FATAL << "mysql CreateTable failed, please check your table config..............";
    }

日志信息
这里写图片描述
结构体STableInfo 定义包含表名,使用map 的key–value来定义表的列,这里为了后面添加书防止Id防止,先删除了数据表的所有数据:

pmysql.DeleteData("DELETE FROM " + info.m_strName);

InitSTableInfo——添加表信息
CreateTable——建表
建表信息如下
C++ 访问MySql 接口封装_第4张图片
表friend 建好之后,此时没有任何数据,我们来插入数据,比如
Id = 1, name = “jeremy”, age = 22, phonenumber = 12221221,代码如下

    STableData data;

    data.m_strName = "friend";
    data.m_mapData["id"] =  "1";
    data.m_mapData["name"] = "\"jeremy\"";
    data.m_mapData["age"] = "22";
    data.m_mapData["phonenumber"] = "12221221";

    if(!pmysql.InsertData(data))
    {
        LOG_WARN << "CMysqlManager::InsertData, data insert failed";
    }

创建数据成功
C++ 访问MySql 接口封装_第5张图片
日志信息
这里写图片描述
可见,实际插入数据最终还是转化为了sql语句。
既然数据写入数据库,现在需要访问数据库获得表friend中 name =”jeremy”的信息,本示例提供了简单的操作,如下:

    std::shared_ptr poConn = pmysql.GetCurpoConn();
    char sql[256] = { 0 };
    snprintf(sql, 256, "SELECT name, age, phonenumber FROM friend");
    QueryResult* pResult = poConn->Query(sql);
    if (NULL == pResult)
    {
        LOG_INFO << "UserManager::Query error, db=" << dbname;
        return false;
    }
    struct People_atrribute{
        string name;
        int age;
        string phonenumber;
    };
    People_atrribute someone;
    while (true)
    {
        Field* pRow = pResult->Fetch();
        if (pRow == NULL)
            break;
        someone.name = pRow[0].GetString();
        someone.age = pRow[1].GetInt32();
        someone.phonenumber = pRow[2].GetString();
        LOG_INFO << pRow[0].GetString() << "-" << pRow[1].GetInt32() << "-" << pRow[2].GetString();
        LOG_INFO << "name= " << someone.name
                 << "  age= " << someone.age
                 << "  phonenumber= " << someone.phonenumber;
        if (!pResult->NextRow())
        {
            break;
        }
    }
    pResult->EndQuery();

结构People_atrribute 正好有 name、age以及phonenumber成员,从数据库表查询得到的数据赋给了 People_atrribute someone, 后面就直接可以操作someone,实现应用程序与数据库之间的数据转移。
日志信息:
这里写图片描述

以上内容仅仅是起到抛转引玉的作用,也算作为学习mysql 的笔记,可以看到这个示例是比较简单,如果考虑到项目中对数据库的操作是整个应用程序性能的瓶颈,也就是高并发读写数据库,应该可以考虑使用线程池或者队列的形式来进行优化,关于这方面内容,后面应该会有机会去关注。

你可能感兴趣的:(C++,mysql)