【TARS】Mysql操作

目录

 

0.学习链接

1.Mysql的介绍

1.1 特点及优势

1.2 Mysql常用API

1.3 用Mysql原生接口来进行一次查询

1.4 直接使用Mysql提供的接口的问题

2.TARS中的数据库操作相关类

2.1 源码地址

2.2 TC_MySql类及其嵌套类

2.2.1 TC_MySql类

2.2.2 TC_MySql类的内部类(嵌套类)MysqlRecord

2.2.3 TC_MySql类的内部类(嵌套类)MysqlData

2.3 TC_DBConf类

2.4 TC_Mysql_Exception类

2.5 用一个例子来看看如何使用TARS中封装的数据库操作方法


0.学习链接

TARS基金会CSDN-TarsCpp组件之MySQL 操作

1.Mysql的介绍

1.1 特点及优势

开源、体积小、速度快、总体拥有成本低.

1.2 Mysql常用API

MySQL 支持了各种主流的程序设计语言,提供了丰富的数据库操作API函数.
在C++开发中,MySQL官方提供了相关的数据库连接动态库MySQL Connector/C++,
接口定义在mysql.h中,包含MySQL基础操作API.常见的有:
 mysql_real_connect, 
 mysql_real_query, 
 mysql_store_result,
 mysql_fetch_row 等.

这四个常见API在mysql.h中的声明是:
(1)MYSQL *STDCALL mysql_real_connect(MYSQL *mysql, const char *host,
                                  const char *user, const char *passwd,
                                  const char *db, unsigned int port,
                                  const char *unix_socket,
                                  unsigned long clientflag);

(2)int STDCALL mysql_real_query(MYSQL *mysql, const char *q, unsigned long length);
(3)MYSQL_RES *STDCALL mysql_store_result(MYSQL *mysql);
(4)MYSQL_ROW STDCALL mysql_fetch_row(MYSQL_RES *result);


(1)mysql_real_connect函数有很多参数,涵盖了连接数据库所需要的基本信息如host, user, password
  等,成功创建连接会获得一个MYSQL对象,并将参数中的MYSQL* 指针mysql指向该对象,供其它操作使用.
(2)mysql_real_query函数需要传入刚才提到的连接对象指针,SQL字符串q和字符串长度length,返回执行结果的行数.
(3)mysql_store_result函数传入MYSQL对象指针,返回执行结果,类型为MYSQL_RES.
(4)mysql_fetch_row传入获取的结果,返回每行的数据。


另外补充一个mysql_error.

1.3 用Mysql原生接口来进行一次查询

g++ mysql_test.cpp `mysql_config --cflags --libs`

#include "mysql/mysql.h"
#include <iostream>
#include <vector>
using namespace std;
 
int main() 
{
    // 创建 MYSQL 对象
    MYSQL *mysql = mysql_init(NULL);
    
    // 创建连接
    mysql_real_connect(mysql, "127.0.0.1", "root", "123456", "db_tars", 3306, NULL, 0);
   
    
    // 给出要查询的sql语句 
    string sql = "select * from db_tars.t_task";
    cout<<"查询的表是db_tars.t_task,查询语句是select * from db_tars.t_task, you can check the result in your mysql workbench. "<< endl;
    // 执行sql语句
    if(mysql_real_query(mysql, sql.c_str(), sql.length()))
    {
          cout<<"mysql_real_query::mysql_error is "<<mysql_error(mysql)<< endl;
    }
 
    // 获取执行结果
    MYSQL_RES *res = mysql_store_result(mysql);
    // 字段名数组
    vector<string> fields;
    MYSQL_FIELD *field;
    // 通过 mysql_fetch_field 获取字段名保存在数组 fields 中
    while((field = mysql_fetch_field(res))) 
    {
        fields.push_back(field->name);
    }
    
    // 声明 row 用于保存每行数据
    MYSQL_ROW row;
    // 读取返回结果所有字段值
    // mysql_fetch_row 从 res 中获取一行数据
    while((row = mysql_fetch_row(res)) != ((MYSQL_ROW)NULL)) 
    {
        // 获取每个字段值的字符串长度,因为每个字段是一个字符数组 (C 风格字符串)
        unsigned long * lengths = mysql_fetch_lengths(res);
        for(size_t i = 0; i < fields.size(); i++) 
        {
            cout << fields[i] << ":" << string(row[i], lengths[i]) << ";";
        }
        cout << endl;
    }
    return 0;
}

1.4 直接使用Mysql提供的接口的问题

上述代码在 main 函数中,用最简单的方式实现了查询操作,几乎没有包含任何错误和返回值判断的逻辑,
但是看起来已经很复杂了.而实际业务场景中通常还需要对一些错误码还有返回结果进行判断,比如连接失败
或断开,返回值为空等,总的来说,存在以下几个问题:

需要自己构造SQL语句,容易出错;
需要开发者自己添加错误码和异常的判断和处理逻辑;
每次查询返回的结果需要调用mysql_fetch系列的多个函数才能完成结果读取。
可见,开发者需要关注的细节太多,会很大程度上降低了开发效率.

因此,把开发者无需关注的或重复的过程和细节隐藏起来,将MySQL操作API进一步封装显得非常必要.
自己封装的话未免太过于小题大做,而且难免有疏漏,对开发者自身能力要求也比较高.
常见的方式是引入完备的第三方库,TarsCpp的工具组件中就包含数据库操作类TC_Mysql,
能够完美解决这些问题.

2.TARS中的数据库操作相关类

TC_Mysql是TarsCpp中提供的MySQL操作类,定义在文件tc_mysql.h中,
对MySQLC++库中提供的API进一步地封装,屏蔽了很多与业务开发无关的细节,使用上更加方便简单.

文件tc_mysql.h中定义了三个类TC_DBConf,TC_Mysql,TC_Mysql_Exception.
其中TC_Mysql类中还定义两个内部类MysqlRecord和MysqlData,它俩作为数据存储类,
类似于 MySQL C++ 库中的 MYSQL_ROW和MYSQL_RES,用于保存每次查询返回的结果.

 

2.1 源码地址

D:\005-02-代码\016-TARS\TARS\TarsFramework\tarscpp\util\include\util\tc_mysql.h
D:\005-02-代码\016-TARS\TARS\TarsFramework\tarscpp\util\src\tc_mysql.cpp
或
/home/muten/module/TARS/TarsFramework/tarscpp/util/include/util/tc_mysql.h
/home/muten/module/TARS/TarsFramework/tarscpp/util/src/tc_mysql.cpp

2.2 TC_MySql类及其嵌套类

2.2.1 TC_MySql类

 

2.2.2 TC_MySql类的内部类(嵌套类)MysqlRecord

class MysqlRecord
    {
    public:
        /**
         * @brief 构造函数.
         * @brief Constructor.
         *  
         * @param record
         */
        MysqlRecord(const map<string, string> &record);

        /**
         * @brief 获取数据,s一般是指数据表的某个字段名 
         * @brief Get data, s is generally a field name of an index table
         * @param s 要获取的字段
         * @param s Fields to get
         * @return  符合查询条件的记录的s字段名
         * @return  s field name of the record that meets the query criteria
         */
        const string& operator[](const string &s);
    protected:
        const map<string, string> &_record;
    };

2.2.3 TC_MySql类的内部类(嵌套类)MysqlData

 /**
     * @brief 查询出来的mysql数据
     * @brief MySQL data queried
     */
    class MysqlData
    {
    public:
        /**
         * @brief 所有数据.
         * @brief All data.
         * 
         * @return vector<map<string,string>>&
         */
        vector<map<string, string> >& data();

        /**
         * 数据的记录条数
         * Number of records for data
         * 
         * @return size_t
         */
        size_t size();

        /**
         * @brief 获取某一条记录. 
         * @brief Get a record
         *  
         * @param i  要获取第几条记录 
         * @param i  Which record to get
         * @return   MysqlRecord类型的数据,可以根据字段获取相关信息,
         * @return   MysqlRecord type data, you can get relevant information based on the field,
         */
        MysqlRecord operator[](size_t i);

    protected:
        vector<map<string, string> > _data;
    };

2.3 TC_DBConf类

/**
* @brief 数据库配置接口
* @brief Database configuration interface
*/
struct TC_DBConf
{
    string _host;// 主机地址
    string _user;// 用户名
    string _password;// 密码
    string _database; // 数据库
    string _charset; // 字符集
    int _port; // 端口
    int _flag; // 客户端标识
    TC_DBConf()   // 构造函数
        : _port(0)
        , _flag(0)
    {
    }

    /**
    * @brief 读取数据库配置. 
    * @brief Read Database Configuration.
    * 
    * @param mpParam 存放数据库配置的map 
    * @param mpParam Map holding database configuration
    *        dbhost: 主机地址
    *        dbhost: host address
    *        dbuser:用户名
    *        dbuser:username
    *        dbpass:密码
    *        dbpass:password
    *        dbname:数据库名称
    *        dbname:database name
    *        dbport:端口
    *        dbport:port
    */
    void loadFromMap(const map<string, string> &mpParam)
    {
        map<string, string> mpTmp = mpParam;

        _host        = mpTmp["dbhost"];
        _user        = mpTmp["dbuser"];
        _password    = mpTmp["dbpass"];
        _database    = mpTmp["dbname"];
        _charset     = mpTmp["charset"];
        _port        = atoi(mpTmp["dbport"].c_str());
        _flag        = 0;

        if(mpTmp["dbport"] == "")
        {
            _port = 3306;
        }
    }

};

2.4 TC_Mysql_Exception类

/**
* @brief 数据库异常类
* @brief Database exception class
*/
struct TC_Mysql_Exception : public TC_Exception
{
    TC_Mysql_Exception(const string &sBuffer) : TC_Exception(sBuffer){};
    ~TC_Mysql_Exception() throw(){};    
};

2.5 用一个例子来看看如何使用TARS中封装的数据库操作方法

我的虚拟机上的代码路径Muten-1:

/home/muten/module/TARS/MutenTarsStudy/TC_MySql_Test

cat /home/muten/TARS-MODULE/Luxiaofan/lxf/Makefile

CREATE TABLE tars_table(
  `user_id` varchar(50) DEFAULT NULL,
  `age` int ,
  `address` varchar(100) DEFAULT NULL,
  `birthday` datetime DEFAULT NULL
);

#include "tc_mysql.h"
#include <iostream>
using namespace tars;
using namespace std;
int main()
{
        // 声明并初始化数据库配置对象 dbConf
        TC_DBConf dbConf;
        dbConf._port = 3306;
        dbConf._host = "127.0.0.1";
        dbConf._user = "root";
        dbConf._password = "123456";
        dbConf._database = "db_tars";
        dbConf._charset  = "utf8";
        cout << "dbConf._port is "<< dbConf._port << endl;
        // 通过 TC_DBConf 对象构造初始化
        TC_Mysql mysqlObj0(dbConf);

        // 先构造对象,不初始化,后续使用init初始化
        //TC_Mysql* mysqlObj1 = new TC_Mysql();
        //mysqlObj1->init(dbConf);

        // 直接传入数据库配置初始化
        //tars::TC_Mysql mysqlObj2("127.0.0.1" , "root", "123456", "db_tars", "utf8", 3306);


        string tableName = "tars_table";
        map<string, pair<TC_Mysql::FT, string>> record0;
        record0.insert(make_pair("user_id"  , make_pair(TC_Mysql::DB_STR, "abcd")));
        record0.insert(make_pair("age"      , make_pair(TC_Mysql::DB_INT, "25")));

        map<string, pair<TC_Mysql::FT, string>> record1;
        record1.insert(make_pair("user_id"  , make_pair(TC_Mysql::DB_STR, "abcd")));
        record1.insert(make_pair("age"      , make_pair(TC_Mysql::DB_INT, "40")));

        try{
                mysqlObj0.insertRecord(tableName, record0);
                int count = mysqlObj0.getRecordCount(tableName, "where `user_id`='abcd'");
                cout << " line is " << __LINE__ << ", count is " << count<< endl;
                mysqlObj0.replaceRecord(tableName, record1);
                mysqlObj0.updateRecord(tableName, record0, "where `user_id`='abcd'");
                mysqlObj0.deleteRecord(tableName, "where `age`=40");
        }
        catch (exception &e){
                cout << "Error: " << e.what() << endl;
        }


        TC_Mysql::MysqlData res;
        try{    
                res = mysqlObj0.queryRecord("select user_id, age from tars_table");
        }
        catch (exception &e) 
        {
                cout << "Error: " << e.what() << endl;
        }
    
        size_t resCount = res.size();
        for (size_t i = 0; i < resCount; ++i)
        {
            cout << "user_id: " << res[0]["user_id"] 
                 << " age: "    << res[0]["age"] 
                 << endl;
        }

        TC_Mysql::MysqlRecord record = res[0];
        cout << record["age"] << endl;
        cout << res[0]["age"] << endl;

}

猜你喜欢

转载自blog.csdn.net/Edidaughter/article/details/114527808