目录
2.2.2 TC_MySql类的内部类(嵌套类)MysqlRecord
2.2.3 TC_MySql类的内部类(嵌套类)MysqlData
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;
}