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) { #if defined(_WIN32) || defined(_WIN64) const char*path = "libmysql.dll"; #else const char* path = "./libmysqlclient_r.so"; #endif // 设置动态库加载的全路径 acl::db_handle::set_loadpath(path); 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) { #if defined(_WIN32) || defined(_WIN64) const char* path = "sqlite3.dll"; #else const char* path = "./libsqlite3.so"; #endif // 设置动态库加载的全路径 acl::db_handle::set_loadpath(path); 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); // 从数据库中查询数据(使用上面的查询例子) dbp->put(dbh); // 归还数据库连接给连接池 delete dbp; // 删除连接池对象 }
由上面示例可以看出 acl 中的数据库连接池还是比较简单易用的,不过需要注意以下几点:
1)在创建数据库连接池对象时并不立刻连接后端的数据库,数据库的连接过程一般发生在 acl::db_pool::peek() 过程,但在调用 peek 时如果连接池有可用连接则直接使用之;
2)在使用数据库连接操作数据库时,如果因为网络意外导致连接断开,内部会根据数据库连接的返回错误号决定是否需要重试该数据库操作;
3)在用完数据库连接后需要调用 acl::db_pool::put() 过程归还数据库连;
4)目前该模块依赖于 mysql 及 sqlite 的库,缺省采用动态加载 mysql/sqlite 动态库的方式,使用者需自行编译生成所在平台的这两个库的动态库。
好了,关于如何使用 acl 库编写数据库应用先写到此,欢迎读者批评指正。
其它有关数据库使用例子请参考:acl\lib_acl_cpp\samples\mysql,acl\lib_acl_cpp\samples\sqlite。
相关推荐
通过该库,用户可以非常容易地编写支持多种模式(多线程、多进程、非阻塞、触发器、UDP方式、协程方式)的服务器程序,WEB 应用程序,数据库应用程序。此外,该库还提供了常见应用的客户端通信库(如:HTTP、SMTP、...
通过该库,用户可以非常容易地编写支持多种模式(多线程、多进程、非阻塞、触发器、UDP方式)的服务器程序,WEB 应用程序,数据库应用程序。此外,该库还提供了常见应用的客户端通信库(如:HTTP、SMTP、ICMP、redis、...
通过该库,用户可以非常容易地编写支持多种模式(多线程、多进程、非阻塞、触发器、UDP方式、协程方式)的服务器程序,WEB 应用程序,数据库应用程序。此外,该库还提供了常见应用的客户端通信库(如:HTTP、SMTP、...
通过该库,用户可以非常容易地编写支持多种模式(多线程、多进程、非阻塞、触发器、UDP方式、协程方式)的服务器程序,WEB 应用程序,数据库应用程序。此外,该库还提供了常见应用的客户端通信库(如:HTTP、SMTP、...
acl 工程是一个跨平台(支持LINUX,WIN32,Solaris,MacOS,...通过该库,用户可以非常容易地编写支持多种模式(多线程、多进程、非阻塞、触发器、UDP方式、协程方式)的服务器程序,WEB 应用程序,数据库应用程序。
通过该库,用户可以非常容易地编写支持多种模式(多线程、多进程、非阻塞、触发器、UDP方式、协程方式)的服务器程序,WEB 应用程序,数据库应用程序。此外,该库还提供了常见应用的客户端通信库(如:HTTP、SMTP、...
通过该库,用户可以非常容易地编写支持多种模式的服务器程序、WEB 应用程序以及数据库应用程序。此外,该库还提供了 XML/JSON/MIME 编码及解码功能,这些编码解码库均支持流式解析模式,从而使之更适应不同的网络...
在 Domino 的设计中,表单和视图是构成应用程序的重要组成部分。为了防止其他用户查看或修改这些元素的设计,可以通过以下方式实现隐藏: - **使用隐藏公式**:可以在表单或视图的设计中使用隐藏公式来控制其可见性...
- **C/Perl/PHP编程**:使用这些语言编写应用程序,通过MySQL API与数据库交互。 考试方式为上机考试,考生需在120分钟内完成100分的试题,包括40分的选择题和60分的操作题。操作题涵盖数据库的创建与管理、数据...
- **集成方式**:PB可以直接连接到ASA数据库,利用PB提供的数据库接口进行数据操作,简化了应用程序的开发流程。 - **优势**:PB与ASA的紧密结合使得开发者可以更高效地开发和部署应用程序,同时充分利用ASA的高性能...
Oracle数据库程序包全局变量的应用是数据库开发中一种高效且安全的方法,主要用于管理程序中的共享数据。全局变量在程序包中可以被多...通过合理设计和使用程序包,开发者可以更有效地构建和维护复杂的数据库应用程序。
这类功能应尽量放在应用程序的业务逻辑中实现。 6. **二进制数据的存储**:禁止在数据库中直接存储图片、文件等大容量二进制数据。这些数据应存放在文件服务器上,数据库中仅存储其路径信息。 7. **避免使用可为...
MySQL作为LAMP(Linux, Apache, MySQL, PHP/Python/Perl)技术栈的重要组成部分,被广泛用于Web应用程序的后端数据库。 ### 知识点三:安装和启动Navicat 要在Windows、Mac OS X或Linux系统上使用Navicat,首先需要...
Lotus Domino程序设计是关于构建基于Lotus Domino平台的应用程序的知识点,该平台自1989年以来就作为文档型数据库应用系统而受到业界的认可,特别是在协作软件市场中占据领先地位。Domino以其开放的开发环境和对多种...
通过该库,用户可以非常容易地编写支持多种模式(多线程,多进程,非二进制,异步,UDP方式,协程方式)的服务器程序,WEB应用程序,数据库应用程序。从而,该库还提供了常用应用的客户端通信库(如:HTTP ,SMTP,...
8. **开发与集成**:如果你是开发者,可以学习如何将Opengauss集成到你的应用程序中,利用其提供的API和驱动程序进行数据存取。 通过这个教程,你不仅能够掌握华为数据库Opengauss的基础操作,还能深入了解数据库...
通过深入学习和实践,开发者能够充分利用Poco库的强大功能,提高开发效率,编写出高效、稳定且跨平台的C++应用程序。提供的"Poco_doc"文档将是一个宝贵的资源,其中可能包含了详细的API说明、示例代码和使用教程,...
ACL 是一种常用的安全机制,它允许开发者定义并控制用户对应用程序资源的访问权限。 首先,让我们理解“honeycomb-acl”包的核心概念。蜂窝式 ACL 设计灵感来源于自然界中的蜂巢结构,寓意着其结构紧凑、组织有序。...
这可能涉及到Java Swing或JavaFX库的使用,用于创建窗口、按钮、表格等界面元素,提供友好的用户体验。 4. **MVC设计模式**:Model-View-Controller(模型-视图-控制器)是一种常见的软件设计模式,在Java应用中...