mooon db wrapper-阿里云开发者社区

开发者社区> 技术小胖子> 正文

mooon db wrapper

简介:
+关注继续查看
mooon db wrapper
1. 前言
mooon db wrapper不是一个DB,仅是对现有的DB API的封装,使得使用更为简单。项目地址:http://code.google.com/p/mooon,可使用SVN下载最新代码。开发和交流论坛:http://bbs.hadoopor.com/index.php?gid=67,可了解项目最新动态。

2. 分层结构

 

共分三层,最底层为各类数据库提供的API,在它之上封装成mooon db api,提供数据库连接池功能。mooon db API本身与具体的数据库无关,通过不同的实现可支持不同的数据库,目前已经有MySQL的实现。
DB Adapter是基于DB API的更高级抽象,内置数据库操作线程和队列,专门负责与数据库的读写交互,为上层应用提供一个异步回调的数据库操作,从而可解除应用和DB之间的一个强耦合。

3. 源码目录结构

3.1. 头文件

 

3.2. CPP文件

 

3.3. 测试代码

4. DB API

4.1. 异常类

数据库错误不能错误码的形式返回,而是采用异常的方式,可使得代码结构变得更为简洁。

4.1.1. CDBException

class CDBException
{
public:
/***
* 构造一个异常对象
* 请注意不应当显示调用构造函数
*/
CDBException(const char* sql, const char* error_message, int error_number=0, const char* filename=__FILE__, int line_number=__LINE__);
 
/** 返回执行出错的SQL语句,如果不是执行SQL语句,则仅返回一个字符串结尾符 */
const char* get_sql() const;
 
/** 返回数据库的出错信息 */
const char* get_error_message() const;
 
/** 返回数据库的出错代码 */
int get_error_number() const;
 
/** 返回执行数据库操作时出错的文件名 */
const char* get_filename() const;
 
/** 返回执行数据库操作时出错的代码行 */
int get_line_number() const;
};

4.2. 抽象接口

提供基础的数据库操作功能,而且也数据库无关,通过不同的实现,可支持不再的数据库。

4.2.1. IRecordrow

/***
* 记录行接口
*/
class IRecordrow
{
public:
/***
* 通过字段编号取得字段的值
*/
virtual const char* get_field_value(uint16_t index) const = 0;
};

4.2.2. IRecordset

/***
* 记录集接口
*/
class IRecordset
{
public:
/***
* 得到记录集的行数
* 对于MySQL,如果query时,参数is_stored为false,则该函数不能返回正确的值,
* 所以应当只有在is_stored为true,才使用该函数
*/
virtual size_t get_row_number() const = 0;
 
/***
* 得到字段个数
*/
virtual uint16_t get_field_number() const = 0;
 
/***
* 判断记录集是否为空
*/
virtual bool is_empty() const = 0;
 
/***
* 检索结果集的下一行
* @return: 如果没有要检索的行返回NULL,否则返回指向记录行的指针,这时必须调用release_recordrow,否则有内存泄漏
*/
virtual IRecordrow* get_next_recordrow() const = 0;
 
/***
* 释放get_next_recordrow得到的记录行
*/
virtual void release_recordrow(IRecordrow* recordrow) = 0;
};

4.2.3. IDBConnection

/***
* 数据库连接接口
*/
class IDBConnection
{
public:
/** 是否允许自动提交 */
virtual void enable_autocommit(bool enabled) = 0;
 
/***
* 用来判断数据库连接是否正建立着
*/
virtual bool is_established() const = 0;
 
/***
* 数据库查询类操作,包括:select, show, describe, explain和check table等
* @is_stored: 是否将所有记录集拉到本地存储
* @return: 如成功返回记录集的指针,这时必须调用release_recordset,否则有内存泄漏
* @exception: 如出错抛出CDBException异常
*/
virtual IRecordset* query(bool is_stored, const char* format, ...) = 0;
 
/***
* 释放query得到的记录集
*/
virtual void release_recordset(IRecordset* recordset) = 0;
 
/***
* 数据库insert和update更新操作
* @return: 如成功返回受影响的记录个数
* @exception: 如出错抛出CDBException异常
*/
virtual size_t update(const char* format, ...) = 0;
};

4.2.4. IDBConnectionPool

/***
* 数据库连接池接口
*/
class IDBConnectionPool
{
public:
/***
* 得到全小写形式的数据库类型名,如:mysql和postgresql等
*/
virtual const char* get_type_name() const = 0;
 
/***
* 线程安全函数
* 从数据库连接池中获取一个连接
* @return: 如果当前无可用的连接,则返回NULL,否则返回指向数据库连接的指针
* @exception: 不会抛出任何异常
*/
virtual IDBConnection* get_connection() = 0;
 
/***
* 线程安全函数
* 将已经获取的数据库连接放回到数据库连接池中
* @exception: 不会抛出任何异常
*/
virtual void release_connection(IDBConnection* db_connection) = 0;
 
/***
* 创建连接池
* @pool_size: 数据库连接池中的数据库连接个数
* @db_ip: 需要连接的数据库IP地址
* @db_port: 需要连接的数据库服务端口号
* @db_name: 需要连接的数据库池
* @db_user: 连接数据库用的用户名
* @db_password: 连接数据库用的密码
* @exception: 如出错抛出CDBException异常
*/
virtual void create(uint16_t pool_size, const char* db_ip, uint16_t db_port, const char* db_name, const char* db_user, const char* db_password) = 0;
 
/***
* 销毁已经创建的数据库连接池
*/
virtual void destroy() = 0;
 
/***
* 得到连接池中的连接个数
*/
virtual uint16_t get_connection_number() const = 0;
};
 

4.3. 助手类

强烈建议:
能使用助手类的时候尽可能地使用它,可以带来不必要的麻烦,而且可以简化代码结构。

4.3.1. DBConnectionHelper

/***
* DB连接助手类,用于自动释放已经获取的DB连接
*/
class DBConnectionHelper
{
public:
DBConnectionHelper(IDBConnectionPool* db_connection_pool, IDBConnection*& db_connection);
 
~DBConnectionHelper();
};

4.3.2. RecordsetHelper

/***
* 记录集助手类,用于自动释放已经获取的记录集
*/
class RecordsetHelper
{
public:
RecordsetHelper(IDBConnection* db_connection, IRecordset* recordset);
~RecordsetHelper();
};

4.3.3. RecordrowHelper

/***
* 记录行助手类,用于自动释放已经获取的记录行
*/
class RecordrowHelper
{
public:
RecordrowHelper(IRecordset* recordset, IRecordrow* recordrow);
 
~RecordrowHelper();
};
 

4.4. 示例

4.4.1. 源代码

#include "sys/db.h"
#include "plugin/plugin_mysql/plugin_mysql.h"
using namespace sys;
using namespace plugin;
 
int main()
{
std::string sql = "SELECT * FROM test"; // 需要查询的SQL语句
std::string db_ip = "127.0.0.1";
std::string db_name = "test";
std::string db_user = "root";
std::string db_password = "";
 
// create_mysql_connection_pool和destroy_mysql_connection_pool两个全局函数
// 在文件plugin/plugin_mysql/plugin_mysql.h中声明
IDBConnectionPool* db_connection_pool = create_mysql_connection_pool();
 
try
{
// 创建数据库连接池
db_connection_pool->create(10, db_ip.c_str(), 3306, db_name.c_str(), db_user.c_str(), db_password.c_str());
}
catch (sys::CDBException& ex)
{
fprintf(stderr, "Create database connection pool error: %s.\n", ex.get_error_message());
exit(1);
}
 
do // 这个循环无实际意义,仅为简化代码结构
{
// 从数据库连接池中取一个连接
IDBConnection* db_connection = db_connection_pool->get_connection();
if (NULL == db_connection)
{
fprintf(stderr, "Database pool is empty.\n");
break;
}
 
// 自动释放
DBConnectionHelper db_connection_helper(db_connection_pool, db_connection);
 
try
{
size_t row = 0; // 当前行数
// 执行一条查询语句
IRecordset* recordset = db_connection->query(false, "%s", sql.c_str());
uint16_t field_number = recordset->get_field_number();
 
// 自动释放
RecordsetHelper recordset_helper(db_connection, recordset);
 
for (;;)
{
// 取下一行记录
IRecordrow* recordrow = recordset->get_next_recordrow();
if (NULL == recordrow) break;
 
// 自动释放
RecordrowHelper recordrow_helper(recordset, recordrow);
 
// 循环打印出所有字段值
fprintf(stdout, "ROW[%04d] ==>\t", row++);
for (uint16_t col=0; col<field_number; ++col)
{
const char* field_value = recordrow->get_field_value(col);
fprintf(stdout, "%s\t", field_value);
}
fprintf(stdout, "\n");
}
}
catch (sys::CDBException& ex)
{
fprintf(stderr, "Query %s error: %s.\n", ex.get_sql(), ex.get_error_message());
}
} while(false);
 
// 销毁数据库连接池
destroy_mysql_connection_pool(db_connection_pool);
}

4.4.2. 编译运行

进入$mooon/common_library/test/plugin/plugin_mysql目录(其中$mooon为mooon源代码所在目录),运行Make编译,成功后执行run.sh即可运行测试代码,如:sh run.sh。

5. DB Adapter

暂未实现。

6. 附录: 如何编译common库?

编译测试代码之前,需要先编译好common库,common库包含两大部分:基础类库和插件类库,它们的编译是自动进行的,而且会自动探测MySQL的安装目录。
要求MySQL安装在/usr/local或用户主目录下,或者由环境变量MYSQL_HOME指定安装路径,而且MySQL的目录结构应当如下:
MySQL安装目录
|---------include
|----------lib
在include目录下要求有mysql.h头文件,在lib目录下要求有libmysqlclient_r.so库文件。
当然,如果您不使用mooon db wrapper,可以不用安装MySQL,因为编译脚本会自动检测,如果没有安装,不会执行编译。
言归正传,先从SVN上取最新的mooon common-library代码,上传到Linux后,进入src目录,然后依次:
1) 执行first_once.sh,该文件默认无执行权限,所以可sh first_once.sh方式执行(注:first_once.sh只有在从SVN上取出代码时执行一次,这也是取first_once的意思)
2) 接下来,完全和普通的automake操作步骤一样,就不多说了
3) 编译好mooon common-library后,就可以进入test目录,编译测试代码了。在测试代码目录中,Makefile是用来编译测试代码的,所以只需要执行make即可,而且run.sh是用来运行测试代码的,每一个CPP文件都会被编译成一个可执行的文件,所以必须保证目录下所有的CPP文件都实现了main函数。



    本文转自eyjian 51CTO博客,原文链接:http://blog.51cto.com/mooon/909647,如需转载请自行联系原作者



版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
使用NAT网关轻松为单台云服务器设置多个公网IP
在应用中,有时会遇到用户询问如何使单台云服务器具备多个公网IP的问题。 具体如何操作呢,有了NAT网关这个也不是难题。
25925 0
阿里云服务器ECS远程登录用户名密码查询方法
阿里云服务器ECS远程连接登录输入用户名和密码,阿里云没有默认密码,如果购买时没设置需要先重置实例密码,Windows用户名是administrator,Linux账号是root,阿小云来详细说下阿里云服务器远程登录连接用户名和密码查询方法
9713 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
10740 0
windows server 2008阿里云ECS服务器安全设置
最近我们Sinesafe安全公司在为客户使用阿里云ecs服务器做安全的过程中,发现服务器基础安全性都没有做。为了为站长们提供更加有效的安全基础解决方案,我们Sinesafe将对阿里云服务器win2008 系统进行基础安全部署实战过程! 比较重要的几部分 1.
8133 0
阿里云服务器ECS登录用户名是什么?系统不同默认账号也不同
阿里云服务器Windows系统默认用户名administrator,Linux镜像服务器用户名root
2966 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,云吞铺子总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系统盘、创建快照、配置安全组等操作如何登录ECS云服务器控制台? 1、先登录到阿里云ECS服务器控制台 2、点击顶部的“控制台” 3、通过左侧栏,切换到“云服务器ECS”即可,如下图所示 通过ECS控制台的远程连接来登录到云服务器 阿里云ECS云服务器自带远程连接功能,使用该功能可以登录到云服务器,简单且方便,如下图:点击“远程连接”,第一次连接会自动生成6位数字密码,输入密码即可登录到云服务器上。
20984 0
21119
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《Nacos架构&原理》
立即下载
《看见新力量:二》电子书
立即下载
云上自动化运维(CloudOps)白皮书
立即下载