一般操作mysql可以直接使用sql语句来建立数据库,修改、查询以及删除等。如果在程序里想要访问数据库又该如何操作呢?下文便是一个简单的例子,能够完成基本的操作,可在此基础上扩充,这个例子也是我将一个开源项目对mysql的部分操作提取出来,加以修改而来。
环境要求linux 平台支持 C++11 安装 mysql 、mysql-devel以及mysqlclient。
源码地址
https://github.com/jeremy505/mysql.git
简单示例创建如下数据表:
#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函数,能实现简单的功能,可以看看代码的实现。
先附上代码日志信息,便于后文说明:
现在在数据库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——建表
建表信息如下
表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";
}
创建数据成功
日志信息
可见,实际插入数据最终还是转化为了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 的笔记,可以看到这个示例是比较简单,如果考虑到项目中对数据库的操作是整个应用程序性能的瓶颈,也就是高并发读写数据库,应该可以考虑使用线程池或者队列的形式来进行优化,关于这方面内容,后面应该会有机会去关注。