文档章节

使用 acl 库编写数据库应用程序

郑树新
 郑树新
发布于 2014/09/03 12:11
字数 2132
阅读 46
收藏 0

      acl 的 C++ 版本库(lib_acl_cpp.a)的 db 模块主要与数据库编程相关,通过这些模块库,开发者可以快速地写出支持数据库连接池的数据库应用程序,目前该 db 模块支持 mysql、sqlite 数据库。本文将以 mysql 应用为例讲述如何使用这些 API 接口编程数据库应用。

      在 lib_acl_cpp/include/acl_cpp/db 目录下,可以看到主要分三个部分:数据库操作句柄类(db_handle,db_mysql,db_sqlite)、数据库连接池类(db_pool,mysql_pool,sqlite_pool)及数据库服务类(db_service,db_service_mysql,db_service_sqlite,这些类主要用在阻塞非阻塞结合的应用中,如:MFC界面过程与数据库过程的结合,非阻塞 IO 过程与数据库过程结合)。

 

一、数据库操作句柄

      下图显示了数据库句柄的类继承关系:db_handle 为基础类,db_mysql/db_sqlite 类均继承于 db_handle 类。

 

 

      在 db_mysql.hpp/db_sqlite.hpp 两个头文件中可以看出,这两个子类仅是实现了基础类 db_handle 的一些虚函数而已,大量关于数据的操作函数都集中于 db_handle.hpp 头文件中,下图为 db_handle 类的功能协作图(其中的 db_rows/db_row 两个类为数据库查询结果类):

 

 

      下面给出了一个简单的数据库查询示例:

////////////////////////////////////////////////////////////////////////////////
/**
 * 从数据库中查询表数据
 * @param db {acl::db_handle&} acl 中的数据库连接句柄引用
 */
static void tbl_select(acl::db_handle& db)
{
	// 创建 sql 查询语句
	const char* sql = "select * from group_tbl where"
		" group_name='test_name' and uvip_tbl='test'";
	// 查询数据库
	if (db.sql_select(sql) == false)
	{
		printf("select sql: %s error\r\n", sql);
		return;
	}
	printf("\r\n---------------------------------------------------\r\n");
	// 列出查询结果方法一:从数据库句柄中获得查询结果集合
	const acl::db_rows* result = db.get_result();
	if (result)
	{
		// 遍历查询结果集
		const std::vector<acl::db_row*>& rows = result->get_rows();
		for (size_t i = 0; i < rows.size(); i++)
		{
			const acl::db_row* row = rows[i];
			// 打印一行结果中的所有结果
			for (size_t j = 0; j < row->length(); j++)
				printf("%s, ", (*row)[j]);
			printf("\r\n");
		}
	}
	// 列出查询结果方法二:根据数组下标遍历数据库句柄的查询结果集
	for (size_t i = 0; i < db.length(); i++)
	{
		const acl::db_row* row = db[i];

		// 取出该行记录中某个字段的值
		const char* ptr = (*row)["group_name"];
		if (ptr == NULL)
		{
			printf("error, no group name\r\n");
			continue;
		}
		printf("group_name=%s: ", ptr);
		for (size_t j = 0; j < row->length(); j++)
			printf("%s, ", (*row)[j]);
		printf("\r\n");
	}
	// 列出查询结果方法三:直接从数据库句柄中获得结果数组
	const std::vector<acl::db_row*>* rows = db.get_rows();
	if (rows)
	{
		std::vector<acl::db_row*>::const_iterator cit = rows->begin();
		for (; cit != rows->end(); cit++)
		{
			const acl::db_row* row = *cit;
			for (size_t j = 0; j < row->length(); j++)
				printf("%s, ", (*row)[j]);
			printf("\r\n");
		}
		
	}
	// 必须释放查询结果
	db.free_result();
}
////////////////////////////////////////////////////////////////////////////////
/**
 * 打开 mysql 数据库连接句柄
 * @return {acl::db_handle*} 返回值为 NULL 表示连接数据库失败
 */
static acl::db_handle* open_mysql(void)
{
	const char* dbaddr = "127.0.0.1:3306";
	const char* dbname = "acl_test_db";
	const char* dbuser = "acl_user", *dbpass = "111111";
	acl::db_handle* db = new acl::db_mysql(dbaddr, dbname, dbuser, dbpass);

	if (db->open() == false)
	{
		printf("open mysql db error\r\n");
		delete db;
		return NULL;
	}
	return db;
}
/**
 * 打开 sqlite 数据库句柄
 * @return {acl::db_handle*} 返回值为 NULL 表示连接数据库失败
 */
static acl::db_handle* open_sqlite(void)
{
	const char* dbfile = "test.db";
	acl::db_handle* db = new acl::db_sqlite(dbfile);

	if (db->open() == flase)
	{
		printf("open mysql db error\r\n");
		delete db;
		return NULL;
	}
	return db;
}
////////////////////////////////////////////////////////////////////////////////
void db_demo(void)
{
	acl::db_handle* db;

	// 操作 mysql 数据库过程
	db = open_mysql();
	if (db)
	{
		tbl_select(*db);
		delete db;
	}

	// 操作 sqlite 数据库过程
	db = open_sqlite();
	if (db)
	{
		tbl_select(*db);
		delete db;
	}
}

 

      从上面的例子可以看出,虽然操作的数据库不同,但数据库查询方式却是完全一样的,因为 acl 类内部屏蔽了数据库操作的差异性。下面还有几点需要注意:

      1)对于数据库查询结果集有多种操作方式,开发者可以根据需要进行选择;

      2)其中生成的 sql 查询语句比较简单,所以没有做特殊字符转义,真正生产环境中开发者应注意对 sql 中的一些变化查询字段进行转义(可以使用 acl::db_handle 类中的 escape_string 方法),以防止 sql 注入攻击;

      3)如果查询的数据库结果集非空,则在处理结果完毕毕竟调用 acl::db_handle 类中的 free_result() 方法释放中间动态分配的内存;

      4)在使用 acl 数据库类编写代码时不需要包含 mysql 和 sqlite 的头文件,但在程序连接阶段必须将 mysql/sqlite 的静态库加上。

 

二、数据库连接池

      为了避免建立数据库连接开销对数据造成冲击,一般的数据库连接都建议使用连接池方式(尤其是在JAVA、PHP等应用中);连接池在保持与数据库的长连接过程中,必须要处理连接中断的重连情况,使上层使用者忽略连接中断的情况。

      下图为 acl 的数据库连接池中各类的继承关系及连接池基础类的函数接口:

           

      从 mysql_pool.hpp/sqlite_pool.hpp 头文件中可以看出,二者的主要区别是构造函数略有不同:

      db_pool 类为数据库连接池基类,其中主要有两个方法:

/**
	 * 从数据库中连接池获得一个数据库连接,该函数返回的数据库
	 * 连接对象用完后必须调用 db_pool->put(db_handle*) 将连接
	 * 归还至数据库连接池,由该函数获得的连接句柄不能 delete,
	 * 否则会造成连接池的内部计数器出错
	 * @return {db_handle*} 返回空,则表示出错
	 */
	db_handle* peek();

	/**
	 * 将数据库连接放回至连接池中,当从数据库连接池中获得连接
	 * 句柄用完后应该通过该函数放回,不能直接 delete,因为那样
	 * 会导致连接池的内部记数发生错误
	 * @param conn {db_handle*} 数据库连接句柄,该连接句柄可以
	 *  是由 peek 创建的,也可以单独动态创建的
	 * @param keep {bool} 归还给连接池的数据库连接句柄是否继续
	 *  保持连接,如果否,则内部会自动删除该连接句柄
	 */
	void put(db_handle* conn, bool keep = true);

 

      mysql 数据库连接池的构造函数如下:

/**
	 * 采用 mysql 数据库时的构造函数
	 * @param dbaddr {const char*} mysql 服务器地址,格式:IP:PORT,
	 *  在 UNIX 平台下可以为 UNIX 域套接口
	 * @param dbname {const char*} 数据库名
	 * @param dbuser {const char*} 数据库用户
	 * @param dbpass {const char*} 数据库用户密码
	 * @param dblimit {int} 数据库连接池的最大连接数限制
	 * @param dbflags {unsigned long} mysql 标记位
	 * @param auto_commit {bool} 是否自动提交
	 * @param conn_timeout {int} 连接数据库超时时间(秒)
	 * @param rw_timeout {int} 与数据库通信时的IO时间(秒)
	 */
	mysql_pool(const char* dbaddr, const char* dbname,
		const char* dbuser, const char* dbpass,
		int dblimit = 64, unsigned long dbflags = 0,
		bool auto_commit = true, int conn_timeout = 60,
		int rw_timeout = 60);

 

      下面以 mysql 为例写一个简单的使用连接池的函数:

void dbpool_demo(void)
{
	const char* dbaddr = "127.0.0.1:3306";
	const char* dbname = "acl_test_db";
	const char* dbuser = "acl_user", *dbpass = "111111";
	acl::db_pool* dbp = new acl::mysql_pool(dbaddr, dbname, dbuser, dbpass);  // 创建 mysql 连接池
	acl::db_handle* dbh = dbp->peek(); // 从连接池中获取一个数据库连接
	if (dbh == NULL)
	{
		printf("peek db connection error\r\n");
		delete dbp;
		return;
	}
	tbl_select(*dbh);  // 从数据库中查询数据(使用上面的查询例子)
	dbh->put(dbh);	// 归还数据库连接给连接池
	delete dbh; // 删除连接池对象
}

       由上面示例可以看出 acl 中的数据库连接池还是比较简单易用的,不过需要注意以下几点:

      1)在创建数据库连接池对象时并不立刻连接后端的数据库,数据库的连接过程一般发生在 acl::db_pool::peek() 过程,但在调用 peek 时如果连接池有可用连接则直接使用之;

      2)在使用数据库连接操作数据库时,如果因为网络意外导致连接断开,内部会根据数据库连接的返回错误号决定是否需要重试该数据库操作;

      3)在用完数据库连接后需要调用 acl::db_pool::put() 过程归还数据库连;

      4)在编译 lib_acl_cpp 库时必须需要指定 Makefile.db 为工程文件(make -f Makefile.db),这样才能使 lib_acl_cpp.a 库内部的数据库功能生效;同时在编译自己的应用程序时必须指定 libmysqlclient_r.a 的链接位置。

 

      好了,关于如何使用 acl 库编写数据库应用先写到此,欢迎读者批评指正。

      其它有关数据库使用例子请参考:acl\lib_acl_cpp\samples\mysql,acl\lib_acl_cpp\samples\sqlite。

 

下载:http://sourceforge.net/projects/acl/
svn:svn checkout svn://svn.code.sf.net/p/acl/code/trunk acl-code
QQ 群:242722074

© 著作权归作者所有

郑树新

郑树新

粉丝 104
博文 87
码字总数 161171
作品 2
昌平
程序员
私信 提问
使用 acl 生成向导快速创建服务器程序

在以前有关使用 acl 的技术文章(如:使用 acl::masterthreads 类编写多进程多线程服务器程序 ,用 acl::masteraio 类编写高并发非阻塞服务器程序,使用 acl::master_proc 类编写多进程服务器...

郑树新
2014/09/03
0
0
使用 acl 库编写多线程应用程序

在 《利用ACL库开发高并发半驻留式线程池程序》 中介绍了如何使用 C 版本的 acl 线程库编写多线程程序,本文将会介绍如何使用 C++ 版本的 acl 线程库编写多线程程序,虽然 C++ 版 acl 线程库...

郑树新
2014/08/26
0
0
acl.3.2.2 发布, 跨平台网络通信与服务器框架

acl 3.2.2 版本发布了,acl 是 one advanced C/C++ library 的简称,主要包括网络通信库以及服务器框架库等功能,支持 Linux/Windows/Solaris/FreeBsd/MacOS 平台;整个 acl 项目主要包含四个...

郑树新
2016/11/01
1K
2
跨平台网络通信与服务器编程框架库(acl库)介绍

一、描述 acl 工程是一个跨平台(支持LINUX,WIN32,Solaris,MacOS,FreeBSD)的网络通信库及服务器编程框架,同时提供更多的实用功能 库。通过该库,用户可以非常容易地编写支持多种模式(...

郑树新
2014/08/23
0
0
跨平台网络通信与服务器编程框架库(acl库)介绍

一、描述 acl 工程是一个跨平台(支持LINUX,WIN32,Solaris,MacOS,FreeBSD)的网络通信库及服务器编程框架,同时提供更多的实用功能 库。通过该库,用户可以非常容易地编写支持多种模式(...

郑树新
2014/08/18
7
0

没有更多内容

加载失败,请刷新页面

加载更多

springboot+jpa 错误信息org.springframework.beans.factory.BeanCreationException

报错信息 org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/a......

冥焱
8分钟前
0
0
威胁快报|新兴挖矿团伙借助shodan作恶,非web应用安全再鸣警钟

近日,阿里云安全发现了一个使用未授权访问漏洞部署恶意Docker镜像进行挖矿的僵尸网络团伙。我们给这一团伙取名为Xulu,因为该团伙使用这个字符串作为挖矿时的用户名。 Xulu并不是第一个攻击...

迷你芊宝宝
15分钟前
0
0
十大经典排序算法动画与解析

排序算法是《数据结构与算法》中最基本的算法之一。 排序算法可以分为内部排序和外部排序。 内部排序是数据记录在内存中进行排序。 而外部排序是因排序的数据很大,一次不能容纳全部的排序记...

夜黑人模糊灬
18分钟前
2
0
7. java枚举

1. 枚举是什么 有的时候一个类的对象是有限且固定的,这种情况下我们使用枚举类就比较方便 2. 为什么不用静态常量来替代枚举类呢? 3. 常用方式 3.1 方式1 枚举类: package cn.ali.tencent...

20190513
19分钟前
0
0
elasticsearch – 弹性搜索:“Term”,“Match Phrase”和“Query String”之间的差异

术语查询匹配单个术语,因为它是:不分析值。 所以,它不必根据你索引的情况而降低。 如果您在索引时间提供Bennett并且未分析该值,则以下查询将不返回任何内容: { "query": { "te...

xiaomin0322
25分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部